The agent proposes, the human decides: grapharc mcp and the Claude Code skill - #99
Open
Shashankss1205 wants to merge 5 commits into
Open
The agent proposes, the human decides: grapharc mcp and the Claude Code skill#99Shashankss1205 wants to merge 5 commits into
Shashankss1205 wants to merge 5 commits into
Conversation
…are real now Two flags that promised a gate and delivered none. GovernedLoop returned PLANNED before the approval block could fire, so `plan --approve` — the form the Slack gate injects — was inert by ordering; and `_cmd_go` accepted --approve/--approval-timeout for the saved-plan path and passed neither on, so `go <dir> --approve` was accepted and silently ignored. The approval block now precedes the plan_only return (an approved plan is still PLANNED, a denial saves nothing for a later go to pick up), execute_plan wires the same file handshake `plan` uses — one shared gate builder, so the two commands cannot drift on how a parked run asks — and a denied or timed-out go leaves plan.json without an executed_run_id. For the external drivers this exists to serve, `plan --json` now carries the admitted shape as data: run_dir, fingerprint, the proposal's nodes, edges and rationale, and a `mutating` verdict computed against the registry module's own MUTATING_KINDS — with a module that declared nothing read as "assume mutating", never as "declared safe" (RegistryBundle.mutating now distinguishes silence from an empty declaration). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The supervision surface external agents plug into, behind the mcp extra that has waited in pyproject since it was declared. Three stdio tools and deliberately not a fourth: plan proposes through the governed loop and returns the admitted shape as data; show_graph reads a run directory back (rendered summaries only, never raw state); execute re-admits and runs the saved plan — parking on the file handshake exactly when the plan's own record says it can change files, so an all-read-only plan runs on the host's prompt while anything mutating waits for a human at a terminal. There is no approve, deny, or decide tool, and a gate test holds the tool list to that sentence: a client that could call approve() would be approving its own proposal, which is not approval. The server drives the CLI as subprocesses — stdout belongs to the protocol, the --json payloads and exit codes are the tested interface, and a parked execute cannot wedge show_graph. Argv is built, never parsed: no tool accepts a registry, policy or model, because the operator's grapharc.toml in the server's root decides those, and a run_dir a client names is confined to that root. plan.json now records the mutating verdict beside the fingerprint, and a record without the field reads as mutating — an old plan is never assumed safe. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…boundary grapharc init --claude-code writes the two files that adopt the MCP supervision surface: .mcp.json registering `grapharc mcp` as a project server, and .claude/skills/grapharc/SKILL.md — the contract that routes multi-step and state-changing work through plan -> show the user the graph -> execute, quotes the approve command when a mutating plan parks, and states the never-clauses for the hands the server cannot see: never run grapharc approve, never touch the request or decision files, a timeout means ask rather than retry. The templates are string constants in init's convention, asserted byte-for-byte by the gate tests, and neither file is ever overwritten — a refusal names what exists and half-writes nothing. Cookbook 09 walks the adoption; the deep dive and README state the trust boundary out loud rather than implying it: the MCP gate binds the MCP surface, not a co-located agent's own file tools — the boundary is the working directory, as it is for the Slack workspace — the host's permission prompt is UX in front of the gate, not the gate, and a parked execute lives inside one tool call. ROADMAP gains 9.7, distinct from the still-open MCP *client* in 3.5. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…st count it Three defects that all followed from the same stale assumption — that a failed node leaves no terminal event worth reading. `observe.cost` skipped every failed execution, so a run stopped for overspending reported a cost report that disagreed with its own audit trail by precisely the spend the stop was about. The kernel stamps `error` terminals with the node's tokens exactly as it stamps `end`, and `metrics.summarize` already counted them; cost now does too, prices them, and keeps `tokens_before_error` for the narrower case it was really about — a trace whose `error` event carries no count of its own, where the AgentNode sub-steps are the only record. The live view double-counted: sub-step tokens accumulated into `live_tokens` were never cleared at a terminal, so a node that ran, finished and ran again showed its closed execution's spend twice. And `go` never resolved `policy` from `grapharc.toml` the way `plan` does, so a saved plan re-admitted through the gate was judged by the built-in default rather than the operator's document. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…put is ignored uv.lock records the `mcp` dev dependency added with `grapharc mcp`, so its gate tests run in CI rather than silently skipping. The `.mcp.json` and skill that `grapharc init --claude-code` writes when this repo dogfoods its own supervision join the generated-not-authored list, alongside the runtime residue ignored above. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 2 of the supervision plan, scoped to the MCP server and the Claude Code skill. Three commits, each with failure-mode gate tests; the full suite is green after each (2,009 passed at HEAD). Builds on main independently of #98.
What lands
1. The approval gate becomes real where it was promised (
9d115b4)GovernedLoopused to returnPLANNEDbefore the approval block could fire, soplan --approve— the form the Slack gate injects — was inert by ordering. The approval now precedes the plan-only return: an approved plan is stillPLANNED, and a denial saves noplan.jsonfor a latergoto pick up.go <dir> --approvewas accepted and silently dropped (_cmd_gopassed neither flag on;execute_planhad no parameter for them). Both commands now share one gate builder, so they cannot drift on how a parked run asks its question.plan --jsoncarries the admitted shape as data:run_dir,fingerprint, the proposal's nodes/edges/rationale, and amutatingverdict computed against the registry module's ownMUTATING_KINDS— with a module that declared nothing read as "assume mutating", never as "declared safe".2.
grapharc mcp(164d14f) — the supervision surface, behind themcpextra that had waited unimported since it was declared. Exactly three stdio tools:plan(goal, scripted?, max_rounds?)— no registry, policy or model parameters: the operator'sgrapharc.tomlin the server's root decides those, never the requester's call.show_graph(run_dir)— read-only; rendered summaries, never rawstate_delta; run directories confined to the server's root.execute(run_dir, approval_timeout?)— re-admits the saved plan; parks on the file handshake exactly when the plan's own record says it can change files, so a read-only plan runs on the host's prompt while a mutating one waits forgrapharc approve <run_dir>from a human, out of band.--jsoncontract is the tested one), andplan.jsonwithout amutatingfield reads as mutating — an old plan is never assumed safe.3.
grapharc init --claude-code(8df4a4f) — writes.mcp.jsonand.claude/skills/grapharc/SKILL.md: the contract that routes multi-step work through plan → show the user the graph → execute, quotes the approve command when parked, and carries the never-clauses (never rungrapharc approve, never touch the handshake files, a timeout means ask). Templates are string constants, byte-asserted by tests, never overwritten. Cookbook 09 walks the adoption; the deep dive and README state the trust boundary: the MCP gate binds the MCP surface, not a co-located agent's own file tools.Verified
test_approval.py,test_mcp_gate.py,test_adopt.py— including: a deniedplan --approveleaves no plan file; an unanswered mutatingexecuteleaves the plan unexecuted and reissuable; the tool list contains no approval verb; arun_diroutside the root is refused; the server writes nothing to stdout; the skill's never-self-approve clause is present in the exact bytes shipped.grapharc approvefrom a thread → execute, plus the read-only path executing without a park.init --claude-codesmoke-tested through the real CLI, including the exit-2 refusal on rerun.🤖 Generated with Claude Code