use std::io::{self, BufRead, Write};
use std::path::PathBuf;
use serde_json::{Value, json};
use crate::shim::config;
use crate::shim::iac;
use crate::shim::workspace;
const PROTOCOL_VERSION: &str = "2025-06-18";
const SERVER_NAME: &str = "falsegreen";
const SERVER_VERSION: &str = env!("CARGO_PKG_VERSION");
const INSTRUCTIONS: &str = r#"This server owns task completion status.
Two workflows are supported:
## A. Dynamic workflow (no pre-existing task)
When no task is active, the agent can self-provision the full
completion-authority pipeline:
1. falsegreen_create_task(workspace, goal) — register a new task
2. For a substantial task, call falsegreen_get_plan_schema(), then draft,
validate, and freeze an outcome-oriented task plan with
falsegreen_save_plan_draft(), falsegreen_validate_plan(), and
falsegreen_freeze_plan(). Planning remains optional for simple tasks.
3. falsegreen_save_contract_draft(task_id, contract) — draft acceptance criteria
4. falsegreen_validate_contract(task_id) — validate the draft
5. falsegreen_freeze_contract(task_id) — freeze the contract and retain the returned verification_job_id
6. falsegreen_get_assignment() — compact immutable requirements
7. Implement the software
8. falsegreen_check_completion() — independent completion decision
9. After every completed verification run, call
falsegreen_get_verification_artifacts() when a run_id is returned, whether
accepted or not, before producing any completion, PR, or verification
report. Use report_id, report_url, and verified_source_identity as issued.
The PR text must be exactly server_generated_pr_summary, returned verbatim.
Never generate or alter a FalseGreen verdict representation.
A frozen plan is an agent proposal, not human approval. Every planned
acceptance ID and planned evidence ID must be mapped by a required executable
contract criterion. Human authority is established when the approved frozen
contract binds the exact plan digest. Once contract drafting begins, the plan
cannot mutate.
Terraform/IaC is opt-in: use evidence_protocol="terraform_plan_v1", submit the
live backend Terraform plan/deployment shape returned by
falsegreen_get_contract_schema(), and set requires_process_provenance=true on
the criterion when provenance is required. After plan verification, call
falsegreen_get_terraform_result(task_id, run_id). PLAN=accepted does not grant
apply authority and must still show APPLY AUTHORIZATION=not_authorized. A human
must separately call falsegreen_authorize_terraform_apply with actor="human"
and confirmed=true (and a backend-produced signed approval in enforced mode).
Only then call falsegreen_apply_authorized_terraform_plan with the returned
authorization_id. Call falsegreen_verify_terraform_deployment after apply, then
falsegreen_get_verification_artifacts with the same run identity to retrieve
the signed Terraform deployment attestation. Never treat incomplete plan,
provenance, provider observation, or refreshed-state evidence as acceptance.
Never create a replacement task for a failed or unresolved task. Retrieve its
canonical artifact first when one exists. If a stale task is not accepted and
work must continue, call falsegreen_close_task(reason); this explicitly
abandons the stale task and does not imply acceptance. Only an abandoned task
is closed and eligible for a new task. A task currently being verified cannot
be closed; wait for that run to finish. Use falsegreen_list_tasks() to discover
stale tasks.
Verifier commands support Python, Rust/Cargo, Ruby/Rails, and Solana/Anchor directly. For
Ruby, use ruby, bundle, bundler, rake, rails, rspec, rubocop, or repository
binstubs under bin/. Do not replace valid Ruby commands because the local shell
differs. git is intentionally unavailable to verifier commands. The client
captures minimal Git provenance for the controller, but immutable verifier
snapshots do not contain .git; Git output is not an acceptance oracle.
Workspace-local Bundler PATH sources are supported when they remain inside the
immutable source snapshot; do not rewrite Rails monorepo Gemfiles to remove
them.
For Solana/Anchor, explicitly freeze `toolchains=["solana-anchor"]` and set
`toolchain="solana-anchor"` on each direct `anchor`, `cargo`, `rustc`, `solana`,
`pnpm`, or `node` command. The profile records `mocha` and fixed native linker
descendants in evidence; repository detection only suggests commands and never
grants authority. Call falsegreen_get_contract_schema() for the complete live
command policy.
## B. Pre-frozen task workflow (host sets active task)
When a session already has one active frozen task:
1. falsegreen_get_assignment() — compact immutable requirements
2. Implement the software
3. falsegreen_check_completion() — independent completion decision
4. After every completed verification run, call
falsegreen_get_verification_artifacts() when a run_id is returned, whether
accepted or not, before producing any completion, PR, or verification
report. Use report_id, report_url, and verified_source_identity as issued.
The PR text must be exactly server_generated_pr_summary, returned verbatim.
Never generate or alter a FalseGreen verdict representation.
If the task is stale and not accepted, retrieve its canonical artifact when
one exists, then call falsegreen_close_task(reason) before starting another
task. Use falsegreen_list_tasks() to discover stale tasks.
If verification returns `FALSEGREEN_JOB_REQUIRED`, do not create a replacement
task, abandon the task, change the contract, or refreeze requirements. Tell the
user to buy a FalseGreen Job at the provided FalseGreen site URL. After the user
purchases a Job, retry verification on the same task and frozen contract.
Do not invent or retype task IDs when the session already has an active task.
Optional task_id arguments exist only for multi-task/debug hosts.
Claim completion only when falsegreen_check_completion (or falsegreen_get_status)
returns status=accepted, and retrieve the verification artifacts before writing
the final report. Only the server-issued immutable report is authoritative;
return server_generated_pr_summary verbatim and link report_url.
The lower-level falsegreen_verify tool durably records a full or focused request
before submitting verifier work and returns its run_id immediately. Poll that
specific run with falsegreen_get_status(run_id=...) until it leaves QUEUED or
RUNNING, then retrieve artifacts with the same durable run_id. Reuse an explicit
idempotency_key when retrying a start whose response may have been lost; durable
storage uniqueness guarantees that the retry resolves to the original run."#;
pub fn run() -> ! {
let stdin = std::io::stdin();
let stdout = std::io::stdout();
let mut stdin = stdin.lock();
let mut stdout = stdout.lock();
let mut active_workspace: Option<PathBuf> = None;
log_event("SERVER_START", None, "transport=stdio");
let mut line = String::new();
loop {
line.clear();
match stdin.read_line(&mut line) {
Ok(0) => {
log_event("SERVER_EOF", None, "");
break;
}
Ok(_) => {
let trimmed = line.trim();
if trimmed.is_empty() {
continue;
}
let request: Value = match serde_json::from_str(trimmed) {
Ok(v) => v,
Err(error) => {
log_event(
"REQUEST_PARSE_ERROR",
None,
&format!("line_bytes={} error={error:?}", trimmed.len()),
);
continue;
}
};
let has_id = request.get("id").is_some();
let method = request.get("method").and_then(|m| m.as_str()).unwrap_or("");
let request_id = request.get("id");
let tool_name = request
.get("params")
.and_then(|params| params.get("name"))
.and_then(Value::as_str)
.unwrap_or("");
log_event(
"REQUEST_RECEIVED",
request_id,
&format!("method={method:?} tool={tool_name:?}"),
);
let response = handle_request(&request, method, &mut active_workspace);
if has_id && let Some(resp) = response {
if let Err(error) = write_response(&mut stdout, &resp) {
log_event(
"RESPONSE_WRITE_ABORT",
resp.get("id"),
&format!("error={error:?}"),
);
break;
}
}
}
Err(error) => {
log_event("REQUEST_READ_ERROR", None, &format!("error={error:?}"));
break;
}
}
}
std::process::exit(0);
}
fn write_response<W: Write>(stdout: &mut W, response: &Value) -> io::Result<()> {
let request_id = response.get("id");
let serialized = match serde_json::to_string(response) {
Ok(serialized) => serialized,
Err(error) => {
log_event(
"SERIALIZATION_ERROR",
request_id,
&format!("error={error:?}"),
);
return Err(io::Error::other(error));
}
};
log_event(
"SERIALIZATION_COMPLETE",
request_id,
&format!("serialized_bytes={}", serialized.len()),
);
log_event("RESPONSE_WRITE_START", request_id, "");
if let Err(error) = writeln!(stdout, "{serialized}") {
log_event(
"RESPONSE_WRITE_ERROR",
request_id,
&format!("error={error:?}"),
);
return Err(error);
}
if let Err(error) = stdout.flush() {
log_event(
"RESPONSE_FLUSH_ERROR",
request_id,
&format!("error={error:?}"),
);
return Err(error);
}
log_event("RESPONSE_WRITE_COMPLETE", request_id, "");
Ok(())
}
fn log_event(event: &str, request_id: Option<&Value>, details: &str) {
let request_id = request_id
.map(|id| format!("{id:?}"))
.unwrap_or_else(|| "-".to_string());
if details.is_empty() {
eprintln!("MCP_LIFECYCLE event={event} request_id={request_id}");
} else {
eprintln!("MCP_LIFECYCLE event={event} request_id={request_id} {details}");
}
}
fn result_summary(result: &Value) -> String {
let shape = match result {
Value::Null => "null".to_string(),
Value::Bool(_) => "bool".to_string(),
Value::Number(_) => "number".to_string(),
Value::String(text) => format!("string_chars={}", text.chars().count()),
Value::Array(items) => format!("array_items={}", items.len()),
Value::Object(fields) => format!("object_keys={}", fields.len()),
};
match result.get("isError").and_then(Value::as_bool) {
Some(is_error) => format!("shape={shape} is_error={is_error}"),
None => format!("shape={shape}"),
}
}
fn handle_request(
request: &Value,
method: &str,
active_workspace: &mut Option<PathBuf>,
) -> Option<Value> {
let id = request.get("id").cloned();
let params = request.get("params").cloned().unwrap_or(Value::Null);
match method {
"initialize" => Some(json!({
"jsonrpc": "2.0",
"id": id,
"result": {
"protocolVersion": PROTOCOL_VERSION,
"capabilities": {
"experimental": {},
"prompts": {"listChanged": false},
"resources": {"subscribe": false, "listChanged": false},
"tools": {"listChanged": false},
},
"serverInfo": {
"name": SERVER_NAME,
"version": SERVER_VERSION,
},
"instructions": INSTRUCTIONS,
}
})),
"notifications/initialized" => None,
"tools/list" => Some(json!({
"jsonrpc": "2.0",
"id": id,
"result": {
"tools": tool_definitions(),
}
})),
"tools/call" => {
let tool_name = params.get("name").and_then(|n| n.as_str()).unwrap_or("");
let arguments = params.get("arguments").cloned().unwrap_or(json!({}));
log_event(
"TOOL_DISPATCH_START",
id.as_ref(),
&format!("tool={tool_name:?}"),
);
let result = forward_tool_call(tool_name, &arguments, active_workspace);
log_event(
"TOOL_RETURNED",
id.as_ref(),
&format!("tool={tool_name:?} {}", result_summary(&result)),
);
Some(json!({
"jsonrpc": "2.0",
"id": id,
"result": result,
}))
}
"prompts/list" => Some(json!({
"jsonrpc": "2.0",
"id": id,
"result": {"prompts": []}
})),
"resources/list" => Some(json!({
"jsonrpc": "2.0",
"id": id,
"result": {"resources": []}
})),
_ => Some(json!({
"jsonrpc": "2.0",
"id": id,
"error": {
"code": -32601,
"message": format!("method not found: {}", method),
}
})),
}
}
fn forward_tool_call(
tool_name: &str,
arguments: &Value,
active_workspace: &mut Option<PathBuf>,
) -> Value {
let key = match config::load_token() {
Some(t) => t,
None => {
return error_result("not authenticated — run `falsegreen login` first");
}
};
if tool_name == "falsegreen_save_contract_draft"
&& let Err(message) = iac::validate_contract_argument(arguments)
{
return error_result(&message);
}
if tool_reads_workspace(tool_name) {
let local_workspace = match local_workspace(tool_name, arguments, active_workspace) {
Ok(path) => path,
Err(message) => return error_result(&message),
};
if let Err(message) = workspace::sync(&local_workspace, &key) {
return error_result(&message);
}
*active_workspace = Some(local_workspace);
}
let workspace_id = active_workspace
.as_ref()
.map(|path| workspace::scope_id(path))
.transpose();
let workspace_id = match workspace_id {
Ok(value) => value,
Err(message) => return error_result(&message),
};
match send_tool_call(tool_name, arguments, &key, workspace_id.as_deref()) {
Ok(result) => result,
Err(message) => error_result(&message),
}
}
pub fn invoke_tool(tool_name: &str, arguments: Value) -> Result<Value, String> {
let key = config::load_token()
.ok_or_else(|| "not authenticated — run `falsegreen login` first".to_string())?;
extract_tool_payload(send_tool_call(tool_name, &arguments, &key, None)?)
}
fn send_tool_call(
tool_name: &str,
arguments: &Value,
key: &str,
workspace_id: Option<&str>,
) -> Result<Value, String> {
let url = config::api_url();
let rpc_request = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": tool_name,
"arguments": arguments,
}
});
let body = serde_json::to_string(&rpc_request).unwrap_or_else(|_| "{}".to_string());
let mut command = std::process::Command::new("curl");
command
.arg("-sS")
.arg("-f")
.arg("-X")
.arg("POST")
.arg(&url)
.arg("-H")
.arg("Content-Type: application/json")
.arg("-H")
.arg(format!("X-API-KEY: {key}"));
if let Some(workspace_id) = workspace_id {
command
.arg("-H")
.arg(format!("X-FalseGreen-Workspace-ID: {workspace_id}"));
}
let result = command
.arg("-d")
.arg(&body)
.arg("--max-time")
.arg("1800")
.output();
match result {
Ok(output) if output.status.success() => {
let response_body = String::from_utf8_lossy(&output.stdout);
match serde_json::from_str::<Value>(&response_body) {
Ok(v) => {
if let Some(result) = v.get("result") {
Ok(result.clone())
} else if let Some(error) = v.get("error") {
let msg = error
.get("message")
.and_then(|m| m.as_str())
.unwrap_or("unknown error");
Err(msg.to_string())
} else {
Ok(v)
}
}
Err(_) => Err(format!("invalid response from server: {response_body}")),
}
}
Ok(output) => {
let stderr = String::from_utf8_lossy(&output.stderr);
Err(format!("request failed: {stderr}"))
}
Err(error) => Err(format!("failed to send request: {error}")),
}
}
fn extract_tool_payload(result: Value) -> Result<Value, String> {
if result.get("isError").and_then(Value::as_bool) == Some(true) {
let message = result
.get("content")
.and_then(Value::as_array)
.and_then(|items| items.first())
.and_then(|item| item.get("text"))
.and_then(Value::as_str)
.unwrap_or("hosted FalseGreen tool returned an error");
return Err(message.to_string());
}
if let Some(payload) = result
.get("structuredContent")
.or_else(|| result.get("structured_content"))
{
return Ok(payload.clone());
}
if let Some(text) = result
.get("content")
.and_then(Value::as_array)
.and_then(|items| items.first())
.and_then(|item| item.get("text"))
.and_then(Value::as_str)
&& let Ok(payload) = serde_json::from_str(text)
{
return Ok(payload);
}
Ok(result)
}
fn tool_reads_workspace(tool_name: &str) -> bool {
matches!(
tool_name,
"falsegreen_create_task"
| "falsegreen_validate_contract"
| "falsegreen_freeze_contract"
| "falsegreen_check_completion"
| "falsegreen_verify"
)
}
fn local_workspace(
tool_name: &str,
arguments: &Value,
active_workspace: &Option<PathBuf>,
) -> Result<PathBuf, String> {
if tool_name == "falsegreen_create_task" {
let supplied = arguments
.get("workspace")
.and_then(Value::as_str)
.ok_or_else(|| "falsegreen_create_task requires a workspace path".to_string())?;
return PathBuf::from(supplied)
.canonicalize()
.map_err(|error| format!("cannot resolve workspace {supplied}: {error}"));
}
if let Some(workspace) = active_workspace {
return Ok(workspace.clone());
}
std::env::current_dir().map_err(|error| format!("cannot resolve current workspace: {error}"))
}
fn error_result(message: &str) -> Value {
json!({
"content": [{"type": "text", "text": message}],
"isError": true,
})
}
fn tool_definitions() -> Vec<Value> {
vec![
json!({
"name": "falsegreen_create_task",
"description": "Register a new task.",
"inputSchema": {
"type": "object",
"properties": {
"workspace": {"type": "string"},
"goal": {"type": "string"},
"title": {"type": ["string", "null"], "default": null},
"repository": {"type": ["string", "null"], "default": null},
"commit_sha": {"type": ["string", "null"], "default": null},
"branch": {"type": ["string", "null"], "default": null},
"pr_number": {"type": ["integer", "null"], "minimum": 1, "default": null},
"pr_identifier": {"type": ["string", "null"], "default": null},
"pr_url": {"type": ["string", "null"], "default": null},
},
"required": ["workspace", "goal"],
},
}),
json!({
"name": "falsegreen_get_plan_schema",
"description": "Get the structured task-plan schema used before contract drafting.",
"inputSchema": {"type": "object", "properties": {}},
}),
json!({
"name": "falsegreen_save_plan_draft",
"description": "Save an explicit outcome-oriented task-plan revision.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": "string"},
"plan": {"type": "object"},
},
"required": ["task_id", "plan"],
},
}),
json!({
"name": "falsegreen_validate_plan",
"description": "Validate the current task-plan draft and dependency graph.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": "string"},
},
"required": ["task_id"],
},
}),
json!({
"name": "falsegreen_freeze_plan",
"description": "Freeze the agent's proposed task plan before contract drafting.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": "string"},
},
"required": ["task_id"],
},
}),
json!({
"name": "falsegreen_get_plan",
"description": "Get the current draft or frozen task plan.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": "string"},
"frozen": {"type": "boolean", "default": true},
},
"required": ["task_id"],
},
}),
json!({
"name": "falsegreen_save_contract_draft",
"description": "Draft acceptance criteria for a task.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": "string"},
"contract": {"type": "object"},
},
"required": ["task_id", "contract"],
},
}),
json!({
"name": "falsegreen_validate_contract",
"description": "Validate the current contract draft.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": ["string", "null"], "default": null},
},
},
}),
json!({
"name": "falsegreen_freeze_contract",
"description": "Freeze the contract (locks criteria + verifier).",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": ["string", "null"], "default": null},
},
},
}),
json!({
"name": "falsegreen_get_assignment",
"description": "Get compact immutable requirements for the active task.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": ["string", "null"], "default": null},
},
},
}),
json!({
"name": "falsegreen_check_completion",
"description": "Run the independent verifier and get a completion decision.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": ["string", "null"], "default": null},
},
},
}),
json!({
"name": "falsegreen_get_status",
"description": "Poll a durable verification run by run_id, or get current task status.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": ["string", "null"], "default": null},
"run_id": {"type": ["string", "null"], "default": null},
},
},
}),
json!({
"name": "falsegreen_get_verification_artifacts",
"description": "Retrieve the source-bound attestation and immutable server report. Return report_id, report_url, and verified_source_identity as issued; use server_generated_pr_summary verbatim and never create or alter a verdict representation.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": ["string", "null"], "default": null},
"run_id": {"type": ["string", "null"], "default": null},
},
},
}),
json!({
"name": "falsegreen_get_terraform_result",
"description": "Read the controller-recorded Terraform plan, process provenance, apply-authorization state, and deployed outcome. An accepted plan never implies apply authorization.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": "string"},
"run_id": {"type": "string"},
"command_index": {"type": ["integer", "null"], "minimum": 0, "default": null},
},
"required": ["task_id", "run_id"],
},
}),
json!({
"name": "falsegreen_get_terraform_apply_authorization_payload",
"description": "Return the exact bounded apply-authorization payload for human signing in enforced mode. This does not authorize or start apply.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": "string"},
"run_id": {"type": "string"},
"command_index": {"type": ["integer", "null"], "minimum": 0, "default": null},
"valid_for_seconds": {"type": ["integer", "null"], "minimum": 60, "maximum": 3600, "default": null},
},
"required": ["task_id", "run_id"],
},
}),
json!({
"name": "falsegreen_authorize_terraform_apply",
"description": "Explicitly authorize one use of the exact accepted plan. Requires actor=human and confirmed=true; records authority but does not start apply.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": "string"},
"run_id": {"type": "string"},
"actor": {"type": "string", "const": "human"},
"confirmed": {"type": "boolean", "const": true},
"command_index": {"type": ["integer", "null"], "minimum": 0, "default": null},
"valid_for_seconds": {"type": ["integer", "null"], "minimum": 60, "maximum": 3600, "default": null},
"approval": {"type": ["object", "null"], "default": null},
},
"required": ["task_id", "run_id", "actor", "confirmed"],
},
}),
json!({
"name": "falsegreen_apply_authorized_terraform_plan",
"description": "Consume one backend authorization and apply only its digest-bound retained plan binary; no arbitrary apply arguments are accepted.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": "string"},
"run_id": {"type": "string"},
"authorization_id": {"type": "string", "pattern": "^tfapply_[0-9a-f]{32}$"},
"command_index": {"type": ["integer", "null"], "minimum": 0, "default": null},
},
"required": ["task_id", "run_id", "authorization_id"],
},
}),
json!({
"name": "falsegreen_verify_terraform_deployment",
"description": "Independently refresh Terraform and observe the already applied LocalStack outcome, then return complete deployment evidence.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": "string"},
"run_id": {"type": "string"},
"command_index": {"type": ["integer", "null"], "minimum": 0, "default": null},
},
"required": ["task_id", "run_id"],
},
}),
json!({
"name": "falsegreen_get_attestation_verification_key",
"description": "Get public Ed25519 material for independent artifact verification.",
"inputSchema": {"type": "object", "properties": {}},
}),
json!({
"name": "falsegreen_get_repair_feedback",
"description": "Get detailed failure feedback from the last verification run.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": ["string", "null"], "default": null},
},
},
}),
json!({
"name": "falsegreen_get_contract_schema",
"description": "Get the live contract schema and verifier command policy. Ruby/Rails commands and binstubs are supported; Git is intentionally not an acceptance oracle.",
"inputSchema": {"type": "object", "properties": {}},
}),
json!({
"name": "falsegreen_get_contract",
"description": "Get the frozen contract for a task.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": ["string", "null"], "default": null},
"frozen": {"type": "boolean", "default": true},
},
},
}),
json!({
"name": "falsegreen_begin_implementation",
"description": "Transition task to implementing state.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": ["string", "null"], "default": null},
},
},
}),
json!({
"name": "falsegreen_verify",
"description": "Durably start full verification, or diagnostic verification of selected frozen criteria, and return immediately.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": ["string", "null"], "default": null},
"criterion_ids": {
"type": ["array", "null"],
"items": {"type": "string"},
"default": null
},
"idempotency_key": {
"type": ["string", "null"],
"default": null
},
},
},
}),
json!({
"name": "falsegreen_consume_repair_cycle",
"description": "Consume a repair cycle.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": ["string", "null"], "default": null},
},
},
}),
json!({
"name": "falsegreen_mark_unresolved",
"description": "Mark the task as unresolved with a reason.",
"inputSchema": {
"type": "object",
"properties": {
"reason": {"type": "string"},
"task_id": {"type": ["string", "null"], "default": null},
},
"required": ["reason"],
},
}),
json!({
"name": "falsegreen_close_task",
"description": "Close a stale non-accepted task; this does not imply acceptance. Tasks currently being verified cannot be closed.",
"inputSchema": {
"type": "object",
"properties": {
"reason": {"type": "string"},
"task_id": {"type": ["string", "null"], "default": null},
},
"required": ["reason"],
},
}),
json!({
"name": "falsegreen_list_tasks",
"description": "List compact task lifecycle state and which stale tasks can be closed.",
"inputSchema": {"type": "object", "properties": {}},
}),
json!({
"name": "falsegreen_get_audit_summary",
"description": "Get audit summary for a task.",
"inputSchema": {
"type": "object",
"properties": {
"task_id": {"type": ["string", "null"], "default": null},
},
},
}),
]
}
#[cfg(test)]
mod tests {
use serde_json::json;
use super::{
INSTRUCTIONS, extract_tool_payload, tool_definitions, tool_reads_workspace, write_response,
};
#[test]
fn accepted_workflow_requires_verification_artifacts() {
assert!(INSTRUCTIONS.contains("falsegreen_get_verification_artifacts()"));
assert!(INSTRUCTIONS.contains("before producing any completion, PR, or verification"));
assert!(INSTRUCTIONS.contains("falsegreen_close_task(reason)"));
assert!(INSTRUCTIONS.contains("falsegreen_list_tasks()"));
assert!(INSTRUCTIONS.contains("Ruby/Rails"));
assert!(INSTRUCTIONS.contains("Solana/Anchor"));
assert!(INSTRUCTIONS.contains("toolchains=[\"solana-anchor\"]"));
assert!(INSTRUCTIONS.contains("toolchain=\"solana-anchor\""));
assert!(INSTRUCTIONS.contains("mocha"));
assert!(INSTRUCTIONS.contains("Git output is not an acceptance oracle"));
assert!(INSTRUCTIONS.contains("falsegreen_get_plan_schema()"));
assert!(INSTRUCTIONS.contains("falsegreen_save_plan_draft()"));
assert!(INSTRUCTIONS.contains("falsegreen_validate_plan()"));
assert!(INSTRUCTIONS.contains("falsegreen_freeze_plan()"));
assert!(INSTRUCTIONS.contains("planned evidence ID"));
assert!(INSTRUCTIONS.contains("Human authority is established"));
assert!(INSTRUCTIONS.contains("binds the exact plan digest"));
assert!(INSTRUCTIONS.contains("does not grant\napply authority"));
assert!(INSTRUCTIONS.contains("falsegreen_authorize_terraform_apply"));
assert!(INSTRUCTIONS.contains("confirmed=true"));
let artifact_tool = tool_definitions()
.into_iter()
.find(|tool| tool["name"] == "falsegreen_get_verification_artifacts")
.expect("artifact tool must be advertised");
let description = artifact_tool["description"]
.as_str()
.expect("artifact tool must have a description");
assert!(description.contains("server_generated_pr_summary verbatim"));
assert!(description.contains("never create or alter a verdict representation"));
}
#[test]
fn planning_workflow_is_advertised_with_server_compatible_schemas() {
let tools = tool_definitions();
let create = tools
.iter()
.find(|tool| tool["name"] == "falsegreen_create_task")
.expect("create task must be advertised");
for untrusted_authority_field in
["organization_id", "member_id", "user_id", "email", "seat"]
{
assert!(
create["inputSchema"]["properties"]
.get(untrusted_authority_field)
.is_none()
);
}
for name in [
"falsegreen_get_plan_schema",
"falsegreen_save_plan_draft",
"falsegreen_validate_plan",
"falsegreen_freeze_plan",
"falsegreen_get_plan",
] {
assert!(
tools.iter().any(|tool| tool["name"] == name),
"{name} must be advertised"
);
}
let save = tools
.iter()
.find(|tool| tool["name"] == "falsegreen_save_plan_draft")
.expect("save-plan tool must be advertised");
assert_eq!(save["inputSchema"]["required"], json!(["task_id", "plan"]));
let get = tools
.iter()
.find(|tool| tool["name"] == "falsegreen_get_plan")
.expect("get-plan tool must be advertised");
assert_eq!(get["inputSchema"]["properties"]["frozen"]["default"], true);
let verify = tools
.iter()
.find(|tool| tool["name"] == "falsegreen_verify")
.expect("verify tool must be advertised");
assert_eq!(
verify["inputSchema"]["properties"]["criterion_ids"]["type"],
json!(["array", "null"])
);
assert_eq!(
verify["inputSchema"]["properties"]["idempotency_key"]["type"],
json!(["string", "null"])
);
let status = tools
.iter()
.find(|tool| tool["name"] == "falsegreen_get_status")
.expect("status tool must be advertised");
assert_eq!(
status["inputSchema"]["properties"]["run_id"]["type"],
json!(["string", "null"])
);
}
#[test]
fn terraform_flow_is_advertised_with_explicit_authority_boundary() {
let tools = tool_definitions();
for name in [
"falsegreen_get_terraform_result",
"falsegreen_get_terraform_apply_authorization_payload",
"falsegreen_authorize_terraform_apply",
"falsegreen_apply_authorized_terraform_plan",
"falsegreen_verify_terraform_deployment",
] {
assert!(tools.iter().any(|tool| tool["name"] == name), "{name}");
}
let authorize = tools
.iter()
.find(|tool| tool["name"] == "falsegreen_authorize_terraform_apply")
.unwrap();
assert_eq!(
authorize["inputSchema"]["required"],
json!(["task_id", "run_id", "actor", "confirmed"])
);
assert_eq!(
authorize["inputSchema"]["properties"]["confirmed"]["const"],
true
);
let apply = tools
.iter()
.find(|tool| tool["name"] == "falsegreen_apply_authorized_terraform_plan")
.unwrap();
assert!(apply["inputSchema"]["properties"].get("argv").is_none());
assert!(apply["inputSchema"]["properties"].get("plan").is_none());
}
#[test]
fn source_sensitive_tools_trigger_workspace_sync() {
for tool in [
"falsegreen_create_task",
"falsegreen_validate_contract",
"falsegreen_freeze_contract",
"falsegreen_check_completion",
"falsegreen_verify",
] {
assert!(tool_reads_workspace(tool), "{tool} must sync source first");
}
assert!(!tool_reads_workspace("falsegreen_get_status"));
assert!(!tool_reads_workspace("falsegreen_get_contract_schema"));
assert!(!tool_reads_workspace("falsegreen_get_plan_schema"));
assert!(!tool_reads_workspace("falsegreen_save_plan_draft"));
assert!(!tool_reads_workspace("falsegreen_validate_plan"));
assert!(!tool_reads_workspace("falsegreen_freeze_plan"));
assert!(!tool_reads_workspace("falsegreen_get_plan"));
assert!(!tool_reads_workspace("falsegreen_get_repair_feedback"));
assert!(!tool_reads_workspace(
"falsegreen_get_verification_artifacts"
));
assert!(!tool_reads_workspace(
"falsegreen_get_attestation_verification_key"
));
assert!(!tool_reads_workspace("falsegreen_close_task"));
assert!(!tool_reads_workspace("falsegreen_list_tasks"));
assert!(!tool_reads_workspace("falsegreen_get_terraform_result"));
assert!(!tool_reads_workspace(
"falsegreen_authorize_terraform_apply"
));
assert!(!tool_reads_workspace(
"falsegreen_apply_authorized_terraform_plan"
));
assert!(!tool_reads_workspace(
"falsegreen_verify_terraform_deployment"
));
}
#[test]
fn direct_cli_extraction_preserves_structured_unknown_fields_and_errors() {
let payload = json!({"status": "accepted", "future_optional": {"x": 1}});
let result = json!({
"content": [{"type": "text", "text": "ignored"}],
"structuredContent": payload,
"isError": false,
});
assert_eq!(extract_tool_payload(result).unwrap(), payload);
let error = json!({
"content": [{"type": "text", "text": "authorization denied"}],
"isError": true,
});
assert_eq!(
extract_tool_payload(error).unwrap_err(),
"authorization denied"
);
}
#[test]
fn job_required_instruction_preserves_task_and_frozen_contract() {
assert!(INSTRUCTIONS.contains("`FALSEGREEN_JOB_REQUIRED`"));
assert!(INSTRUCTIONS.contains("do not create a replacement"));
assert!(INSTRUCTIONS.contains("abandon the task, change the contract"));
assert!(INSTRUCTIONS.contains("refreeze requirements"));
assert!(INSTRUCTIONS.contains("provided FalseGreen site URL"));
assert!(INSTRUCTIONS.contains("same task and frozen contract"));
}
#[test]
fn response_writer_preserves_jsonrpc_payload_and_newline() {
let response = json!({
"jsonrpc": "2.0",
"id": 17,
"result": {"content": [{"type": "text", "text": "ok"}]},
});
let mut output = Vec::new();
write_response(&mut output, &response).expect("response should be written");
assert_eq!(output.last(), Some(&b'\n'));
let written: serde_json::Value = serde_json::from_slice(&output).expect("valid JSON");
assert_eq!(written["jsonrpc"], "2.0");
assert_eq!(written["id"], 17);
assert_eq!(written["result"]["content"][0]["text"], "ok");
}
}