Add /graph G3: dynamic replan from DISCOVERED work items

Workers, verifiers, and serial node goals can now surface out-of-scope work
as line-anchored 'DISCOVERED: <text>' markers (fence-stripped and
placeholder-filtered — the templates' own examples are fenced so verbatim
echoes never parse; worker summaries embedded in verifier prompts get the
marker neutralized alongside NODE_RESULT/NODE_VERDICT). Discoveries queue on
the orchestration as persisted state and fold into the graph at dispatch
boundaries: a replanner subagent produces a strictly APPEND-ONLY appendix,
validated against the live graph (existing-id deps allowed; edges onto
gn-final rejected — they would cycle the moment the final-gating extension
lands; Blocks deps on Failed/Blocked nodes rejected as DeadDep so the
attempt-2 feedback loop repairs the artifact). Installing an appendix bumps
plan_version, freezes an immutable graph.baseline.v{N}.json next to the
prior versions, extends gn-final's gate (demoting a Ready final back to
Waiting), and recomputes readiness.

DiscoveredFrom edges are audit metadata, never scheduling gates: an origin
is always terminal at replan time, so gating on it is either a no-op or a
permanent wedge — and a failed node's discoveries are still real work.
Replanning is bounded by KIGI_GRAPH_REPLAN_CAP (default 3; 0 disables it
quietly): past the cap, after the final node has achieved, or on replan
failure, discoveries drain to history only — a working graph is never
paused for a failed enhancement pass, and it always converges. The budget
gate now precedes the replan boundary (a budget-dead graph keeps its
discoveries queued for a later --budget top-up instead of spending two
replanner runs first), and both planner runners delete stale artifacts
before spawning so a child that responds without writing can never get a
previous pass's file validated as its own output.

Tests: validate_replan unit coverage (existing-id resolution,
DiscoveredFrom dedup, collisions, dead deps vs dead origins, terminal-node
edges, combined-graph cycles), tracker appendix/regate/audit-edge tests,
and two e2e flows — discovery → replan → appended node runs to Achieved
with both baselines frozen, and cap-0 draining to history while the graph
still converges. kigi-shell 4935 lib tests green; workspace clippy clean.
This commit is contained in:
2026-07-20 19:17:24 -04:00
parent 1579558b56
commit bb5cbff62d
22 changed files with 1272 additions and 10 deletions
@@ -0,0 +1,55 @@
You are the Graph Replanner for the Kigi harness. A running dependency
graph surfaced NEW out-of-scope work (DISCOVERIES below). Your job is to
extend the graph with the FEWEST additional nodes that cover exactly that
work — nothing else.
## Inputs (below this prompt)
- OBJECTIVE: the overall graph objective, verbatim.
- CURRENT GRAPH: the existing nodes as JSON (id, title, status, deps).
These are IMMUTABLE — you cannot edit, remove, or reorder them.
- DISCOVERIES: the queued out-of-scope items, each with the node id that
surfaced it.
## Rules
- Append-only: output ONLY new nodes. Merge related discoveries into one
node where a single coherent unit of work covers them.
- A discovery already covered by an existing non-terminal node's spec
needs NO new node — cover only genuine gaps. If nothing needs a new
node, still write a file with an empty check? NO — see the escape
hatch below.
- `deps` may reference EXISTING node ids (the `gn-…` strings from
CURRENT GRAPH) and/or other new nodes. Only true ordering constraints.
- Each new node MUST set `discovered_from` to the existing node id(s)
whose discoveries it covers.
- Specs are outcome contracts in the OBJECTIVE's vocabulary, sized for
one focused autonomous run.
## Output contract — STRICT
Use your `{WRITE_TOOL}` tool to write JSON to `{GRAPH_FILE}`:
```
{
"nodes": [
{
"id": "short-kebab-slug",
"title": "One-line human title",
"spec": "Outcome contract for this node alone.",
"deps": ["gn-existing-or-new-slug"],
"discovered_from": ["gn-originating-node"]
}
]
}
```
Escape hatch: when every discovery is already covered by existing nodes,
write `{"nodes": []}` — the harness treats an empty appendix as "nothing
to add" and drains the discoveries.
Your terminal response must be exactly:
```
Done
```