You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 5164966
Browse filesBrowse the repository at this point in the historyBrowse files
mcp: the execute timeout no longer severs an approved mutating run
Three defects on one path, all from issue #113.
**One budget covered the park and the work.** `approval_timeout + 120s`
bounded the whole subprocess, so a human approving near the end of the park
left roughly two minutes for the run itself -- and a governed run's agent
phases delegate to Claude Code, which reads files, edits them and verifies.
The likely outcome was not a wedged process being cleaned up, it was a
human-approved, tree-mutating run being SIGKILLed partway through mutating
the tree. The non-mutating branch passed `timeout=None` and was not bounded
at all. Separate budgets now: `DEFAULT_WORK_TIMEOUT` is 1800s, the number
`GRAPHARC_SLACK_WORK_TIMEOUT` already uses for this exact case, overridable
via `GRAPHARC_MCP_WORK_TIMEOUT` -- which refuses a non-numeric or
non-positive value rather than substituting a ceiling nobody chose.
**plan.json could not catch the reissue.** `executed_run_id` is stamped
after `loop.run()` returns, so a killed run never reaches it; the record
said "never executed" and the #100 guard waved a second `go` through on one
human approval. The guard now reads the trace, which is written as the run
proceeds and is the only place that evidence survives a kill. Deliberately
not a pre-stamp: that would mark a plan executed when it had merely parked
and been denied. The phase vocabulary is pinned by a test, because the
obvious spelling -- "not one of `observe.metrics`'s loop phases" -- counts
`approval_response` as an execution and so refuses a denied plan forever.
A torn trace reports no evidence rather than wedging the plan beside it.
**The kill left grandchildren.** No new session and `process.kill()` signals
the direct child only, so a delegated Claude Code process outlived the run
and kept the workspace. `start_new_session=True` plus a process-group
SIGKILL, falling back to the single process where `killpg` is unavailable.
The docstring promised "a timeout leaves the plan unexecuted and this call
safe to reissue", true of an unanswered park and false of everything else.
It distinguishes them now and sends the agent to `show_graph` first; the
`DriverError` says the same. Both asserted -- that docstring is the agent's
whole briefing.
Verified: 2171 selected, 13 deselected, ruff clean. Red without the fix
(reverting only `grapharc/` fails the reissue test `assert 0 == 2`), while
the two over-refusal guards pass on both sides. deep-dive.md's figure
updated 2,151 -> 2,171.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/deep-dive.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -254,7 +254,7 @@ A stable system is not one that claims to have no edges — it is one whose edge
254
254
-**`.env` and `grapharc.toml` follow the same discovery rule: the working directory, and nowhere else.** Neither searches parent directories — a run must not be governed by a file you did not know about, and must not be *billed* to one either. **This is a behaviour change:** the credential loader used to walk up to `/`, so a `.env` in an ancestor directory (a `$HOME` one on a shared box, a client project one above a demo checkout) was picked up silently. If you relied on that, move the file into the directory you run from, `export` the variable, or pass `env_file=` to name it explicitly. A real environment variable still beats any file.
255
255
-**`grapharc run` has no budget unless you give it one.** Set any of `--max-tokens`, `--max-iterations`, `--max-seconds`, or `--max-concurrency`; without them each dimension is unlimited and the gate admits a topology of any worst-case cost.
256
256
257
-
**Verified this pass:**`pytest` → green, 2,151 selected and 13 deselected (the live ones); `ruff check .` clean; all eight `grapharc demo` stages green, plus the `trace` / `metrics` / `viz` / `replay` tour against a freshly recorded demo trace; the wheel builds and imports all submodules in a clean virtualenv with `[all]`, and `0.1.7` on PyPI is that wheel. The counts are a snapshot, not a property of the project — `pytest` re-derives them in one command, which is the only reason they are quoted, and `tests/test_deep_dive.py` fails this line rather than letting it drift.
257
+
**Verified this pass:**`pytest` → green, 2,171 selected and 13 deselected (the live ones); `ruff check .` clean; all eight `grapharc demo` stages green, plus the `trace` / `metrics` / `viz` / `replay` tour against a freshly recorded demo trace; the wheel builds and imports all submodules in a clean virtualenv with `[all]`, and `0.1.7` on PyPI is that wheel. The counts are a snapshot, not a property of the project — `pytest` re-derives them in one command, which is the only reason they are quoted, and `tests/test_deep_dive.py` fails this line rather than letting it drift.
258
258
259
259
[ROADMAP.md](../ROADMAP.md) tracks what is built and what is not, item by item.
0 commit comments