8000 GREEN's dependency check reads only the plan text the human's grant pins (M0.9, M10.2) by adeelahmad · Pull Request #113 · adeelahmad/aloop · GitHub
[go: up one dir, main page]

Skip to content

GREEN's dependency check reads only the plan text the human's grant pins (M0.9, M10.2) - #113

Merged
adeelahmad merged 5 commits into
masterfrom
claude/plan-approval-gate-mbyefz
Oct 20, 2026
Merged

adeelahmad merged 5 commits into
masterfrom
claude/plan-approval-gate-mbyefz

Conversation

@adeelahmad
@adeelahmad adeelahmad commented Oct 20, 2026 •
Copy link
Copy Markdown
Owner

Requested by Adeel · project thread

Before: GREEN's check for an unplanned dependency (M10.2) read the story's plan files as they were when GREEN stopped, whatever the human had approved. The approved plan (M0.9) left out the sprint's intake.md and standards.md. A re-plan that touched a story file could also make a dependency the human approved count as unplanned.

After: a dependency counts as planned only in the text the human's grant pins. Under review that is the plan they approved. Under their autonomy grant it is the plan as the planner's gate last passed it on the planner's stop. It counts as it was then, so text added later names nothing, and a re-plan does not undo an approved dependency. The approved plan now also covers the sprint's intake.md and standards.md.

When nothing pins the text, the block says why: no approval yet, a grant that does not hold, an approval recorded before per-file pins, or an autonomous sprint whose plan has not passed the planner's gate yet.

How

  • plugin/bin/_approval.py:
    • A reviewed grant records file_sha256, the plan's files one by one, and keeps their text in run data under each sha256. A copy counts only while it still hashes to its name.
    • pinned(cwd, sprint) returns (files, why) from the sprint's newest grant. A reviewed grant pins its approved files, provided it holds and its per-file hashes add up to the plan its code names. An autonomy grant pins the newest plan.gated of that sprint, provided the grant holds.
    • SPRINT_FILES now includes intake.md and standards.md. An approval recorded before that says so, instead of reporting "the plan changed".
  • plugin/bin/ids gated SPRINT_DIR records plan.gated (the files' sha256, with their text kept). It needs the planner's stop payload on stdin. gate-stage2-complete runs it last when the planner's stop passes, never on a self-check. plan.gated is a new event type, and events emit refuses it.
  • plugin/bin/deps unplanned reads only pinned text: the story's tasks.md, targets.json, plan.md and validate.md, and the sprint's stories.md, by realpath. Its note names the files whose text changed since the pin.
  • scripts/test/mission-tick.test.py check 30: on a fresh checkout the human now types the raised cap's approval after the second of the last ledger record. A line from that second counts as older (stale_before, unchanged), so on a fast runner the test asked again and failed (CI on 86cbe9b; reproduced on master's test with a frozen clock).
  • Docs:
    • ROADMAP M0.9 row: a "Since" note, and the stale open item removed.
    • The event table.
    • CHANGELOG.
    • plugin/bin/README.md: the ids, deps and gate-stage2-complete rows.

Evidence

  • scripts/test/supply-chain.test.py, now 8 checks:
    • 2: a dependency the approved plan does not name is refused.
    • 2b: one named after the approval is refused; the human's approval of the plan that names it admits it.
    • 2c: another story's re-plan, or this story's tasks.md growing a task, keeps the approved dependency. A dependency only the new text names is refused.
    • 2d: a sprint-dir STORY_DIR reads no plan text.
    • 2e, under autonomy:
      • Nothing counts before the planner's gate passes, and the block says so.
      • Text named after that pass is refused.
      • A bare ids gated, a builder's payload, a self-check and events emit plan.gated record nothing.
      • The planner's next pass, naming it in stories.md, admits it.
  • scripts/test/plan-approval.test.py, now 21 checks:
    • Check 4 covers intake.md and standards.md.
    • Check 21 covers the pin itself: nothing before a grant, only the sprint's own gated plan, and a forged record or edited per-file hashes pin nothing. It also covers the message for an older approval.
  • Two adversarial review rounds: 3 lenses each, every finding re-verified by a skeptic. They found 11 real defects and test gaps, and all are fixed here. The first version only asked events approval, and the review showed that missed autonomy mode.
  • Mutation test: 16 mutants of _approval.py, deps, ids, events and gate-stage2-complete, each dropping one check. The two suites killed all 16, and the unpatched control passes.
  • On fe17696 (master Run 52: slugify (Python) under the new gates, 12/12 for $0.56 #112 merged in): DSH_BIN=… ALOOP_REQUIRE_DSH=1 make test-hosts exits 0, including the pinned dsh runtime suite. make conformance passes 82/82. make shellcheck, make pycheck and eval-validate are clean. CI: all 5 jobs green.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WBVFNrEg85XAnSfHQidCXN

…ins (M0.9, M10.2)

A reviewed grant now records the plan file by file (and keeps the
approved text); under an autonomy grant the plan is the one the planner's
gate passed on the planner's stop (`plan.gated`, `ids gated`, gate-owned).
`deps unplanned` reads only that text, so text added after the grant names
no dependency and a re-plan does not undo an approved one. The approved
plan now covers the sprint's intake.md and standards.md too.

Tests: supply-chain.test.py 2-2e, plan-approval.test.py 4 and 21;
16/16 mutants killed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WBVFNrEg85XAnSfHQidCXN
…follow-up

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WBVFNrEg85XAnSfHQidCXN
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WBVFNrEg85XAnSfHQidCXN

# Conflicts:
#	plugin/bin/README.md
…s second

On a fresh checkout a typed line from the same second as an earlier human
decision counts as older (stale_before), so on a fast runner the raise-the-cap
approval asked again and the test failed. The human now types after that
second, as a clone's human always does. Reproduced with a frozen clock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WBVFNrEg85XAnSfHQidCXN
Copy link
Copy Markdown
Owner Author

The hosts job on 86cbe9b failed at mission-tick.test.py:1229 (check 30, the raised cap on a fresh checkout). This diff didn't cause it. The cause is a same-second race in the test.

After rmtree .aladdin there is a new prompt log, so stale_before treats every typed line from the second of an earlier human decision on the mission as older. That rule is correct. On a fast runner, though, the human's approve line landed in the same second as the init approval, so the command asked again (exit 3). I reproduced this on master's test with a frozen clock: ASK[approve]: mission.approved is the human's own decision….

15b9022 fixes it: before the human types the line, the test waits until the clock passes the last ledger row's second, as a clone's human always would. The gate is unchanged.

Local run on fe17696, master #112 merged in: lint 0, make test-hosts exit 0 (dsh runtime included), conformance 82/82.


Generated by Claude Code

@adeelahmad 8000
adeelahmad marked this pull request as ready for review October 20, 2026 18:04
@adeelahmad
adeelahmad merged commit 4dd5664 into master Oct 20, 2026
5 checks passed
adeelahmad pushed a commit that referenced this pull request Oct 20, 2026
…sh-clone stop) into the M9.31 branch

ROADMAP: the M9 row keeps this branch's done/open lists and adds M9.42
(done in #114) and M9.48 (open); the M10 row takes master's fresh-clone
stop and says the OpenCode plugin exports ALOOP_AGENT_SHELL since M9.31.
aloop.js: the SubagentStart comment names both the core principles and
the artifact gate's per-task start.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012rsDo9GYzNx8tXucywZHnA
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0