Skip to content

Execution Log: a contribution is asked where its outer loop goes, and then waits

Append-only log for #199.

Phase transitions

PhaseEnteredReviewed/approved byNotes
requirements-definition2026-08-10pending — PR gateRisk tier 3: graph hooks + the ingress↔graph seam; no schema, no new config key
design2026-08-10pending — PR gateOne predicate read in three places; one new GraphContext field; one guarded evaluation at spawn
test-planning2026-08-10pending — PR gate14-row matrix; every row runs offline against a fake integration
tasks-breakdown2026-08-10pending — PR gate9 tasks; T1–T4 code, T5–T7 tests, T8 docs, T9 verification
implementation2026-08-10T1–T8 complete, plus one unplanned change recorded in tasks.md
verification2026-08-10Every applicable row executed; see testing-plan.md § Verification results
needs-review2026-08-10pendingSelf-review done (three rounds); the human gate is the PR
complete

Pull requests

PRScope / tasksStatus
#200 (this repository)Tasks 1–9 — the whole work itemopen

Progress entries

2026-08-10 — spec chain locked

  • Phase: requirements-definition → tasks-breakdown
  • Did: Read the ticket, then the code it names and the code around it: graph/hooks/selection.py, graph/hooks/goal.py, graph/pdlc-contribution-loop.yaml, graph/runtime.py, graph/state.py, graphlink.py, control.py and webhook/dispatcher.py. Confirmed both halves by reading and then by running the ticket's scenario offline against the pre-change code (evidence/reproduction.md): the pointer stops at goal-definition with nothing posted, and the checklist a later event finally produces carries a row about an outer loop this loop does not have. Established why the second defect is invisible on the outer loop — its start node needs a different comment anyway, and two control keywords in one comment are refused as ambiguous — which is what makes the contribution loop the first graph to hit it. Wrote and locked bugfix.mddesign.mdtesting-plan.mdtasks.md.
  • Checkpoint/tests: baseline make check green — 1760 passed, 1 skipped.
  • Next: implement T1–T4, then the tests.
  • Blockers: none.

2026-08-10 — implementation

  • Phase: implementation
  • Did: T1–T8. _asks_surface() in selection.py read at render, parse, confirm and freeze (with NO_SURFACE documenting never asked vs default kept); GraphContext.loop plus the _is_contribution branch in render_graph_context; GraphLink.on_spawn evaluating a human start node once with the spawning event attached, behind _entered_a_human_gate (fails closed) and the fresh-entry guard; the dispatcher passing routed at the spawn seam; the documentation set.
  • Checkpoint/tests: make check green.
  • Next: verification, then self-review.
  • Blockers: none.

2026-08-10 — verification

  • Phase: verification
  • Did: Ran every applicable row of the testing plan and recorded the results in testing-plan.md § Verification results. Captured the before/after reproduction, the unit and abuse-case runs, the dispatcher scenario and the full check under evidence/.
  • Checkpoint/tests: make check exit 0 — 1770 passed, 1 skipped.
  • Next: self-review.
  • Blockers: none.

Review cycles

Self-review — 2026-08-10

Three rounds over the diff, the reasoning recorded because the findings shaped the code:

  1. Round 1 — "does the spawn-time evaluation break anything that was working?" Traced every caller of on_spawn: _spawn_tmux (a real first spawn) and the respawn seam, which is pointer-idempotent. Found the case that would have been a regression — an agent start node, whose exit chain gates artifacts the session has not written — and narrowed the evaluation to human nodes, with test_a_spawn_never_evaluates_an_agent_start_node pinning it. Also confirmed Runtime.advance does not re-acquire the graph-state lock _guarded already holds (only Runtime.complete does), and ordered the advance after _bind_session so a session: inherit gate resolves against the session just spawned.
  2. Round 2 — "is the surface really absent, in every direction?" Render, parse, confirm and freeze were each checked separately, because three of them could have been left inconsistent: a checklist without the row whose parser still honoured a typed token would be worse than the bug. Settled on recording "" rather than the default after checking that _record_selected_skips writes state.surface only for a truthy value — so never asked is representable without teaching any reader a third literal. Chose the negative predicate (!= PDLC_CONTRIBUTION_LOOP) so a repository-supplied outer loop keeps the question.
  3. Round 3 — "what does the user see?" Ran the reproduction end to end and read the posted comment and the prompt block as a human would. The empty space where the row had been read as an omission, so the checklist now says where a contribution's conversation happens instead; and the prompt's "iterate the outer loop's artifacts on…" line was still instructing a contribution about an outer loop, which is the same defect one layer along — fixed with GraphContext.loop rather than left as a follow-up.

No finding was carried forward unresolved. Critic review and the security review are for the reviewer to run at the PR gate.

Security review (gate)

The change moves when untrusted comment text reaches a hook chain, not whether — the three trust boundaries and the abuse cases are enumerated in bugfix.md § Security considerations, and each abuse case has a test named in evidence/unit.md § T10. Reachable state is strictly reduced in one place: a contribution can no longer be given a pull-request surface at all.

Capability docs

  • docs/capabilities/process-graph.md — the contribution-loop bullet gains the "no outer loop to place" clause; the ingress-drives-the-graph bullet gains the human-start-node evaluation; a history row.
  • docs/capabilities/webhook-triggers.md — the control-keyword bullet gains why a spawning command is handed to the graph; a history row.
  • docs/capabilities/spec-workflow.md — the outer-loop surface bullet names the contribution exception.

Documentation

  • docs/cli/commands/graph.md — the phase-selection section says the row is absent on a contribution and what is said instead.
  • skills/the-loop/SKILL.md, skills/the-loop/reference/workflow.md, skills/the-loop/reference/collaboration.md — the same two facts where an agent reading the skill will meet them.

Verification results

Recorded in testing-plan.md § Verification results, with the transcripts under evidence/: reproduction.md (before/after), unit.md (T1, T2, T4, T10, T12), integration.md (T3), check.md (T6, T14).

Released under the MIT License.