Skip to content

AGENTS.md: make "push a WIP commit before any minutes-long step" an ordering rule — the one lesson from the 2026-08-25 restart that is not already covered #12024

Description

@os-steve

Filed unassigned, recording only.

A container restart on 2026-08-25 killed five in-flight os-dev agents. Four of tonight's five lessons turned out to be instances of rules the repo already has, and are recorded as findings rather than as new doctrine. This card is the one that is genuinely missing.

The gap, measured

AGENTS.md carries no rule about committing before a long-running step. Verified with a working control:

grep -niE 'commit before|before you wait|wip commit|push.*before' AGENTS.md   → 0 hits
grep -ncE 'git stash' AGENTS.md                                              → 2 hits   (control: the grep works)

The nearest thing that exists is #11463, whose framing is parking: an agent that decides to wait should commit first. The restart showed that framing is too narrow — the rule has nothing to do with parking.

What the restart measured

card ran for committed? outcome
#11694 ~14 min no branch pushed empty (0 commits ahead), worktree clean — total loss
#11942 ~14 min no branch pushed empty, worktree clean — total loss
#11525 hours no ⭐ 2081 lines uncommitted — a 1194-line gate, an 864-line baseline, lint.yml +22, package.json +1
#9165 step 2 hours no scratch instruments survived on disk; the analysis died with the context
#9298 hours n/a worktree clean, nothing to recover

Only luck saved #11525: the worktrees outlived the processes, so a rescue dispatch could commit and push the work (f7e038cda). This environment documents that it reclaims the filesystem — so that luck is not repeatable.

⭐ Why the current phrasing cannot reach the agents that need it

The cost of not committing is inversely related to how well the agent is behaving. An agent that finishes fast commits soon and risks little. An agent deep in a hard card — measuring, ablating, re-deriving — defers committing because it is not done yet, and so accumulates the largest uncommitted surface exactly where the work is most expensive to reproduce. #11694 and #11942 lost 14 minutes each. #11525 lost hours because it was doing the card properly.

So the population that most needs the rule is the population least likely to be reached by it: "commit before you wait" does not fire for an agent that is not waiting — it is working.

The proposed rule

Change the trigger from "before you wait" to "before anything that is not the next edit" — before a build, a full test run, a gate sweep, an ablation, any step measured in minutes.

Push a WIP commit before every step that takes minutes — a build, a full test run, a gate sweep. Not because you are about to wait: because you are about to stop being the only copy.

#11463 already establishes that ordering rules are the instructable kind (do-not-end-the-turn survived three escalating strengths; commit-before-you-wait did not need to). Tonight's rescue is a second confirmation: told "commit and push before reading anything else," the agent did exactly that, and 2081 lines were safe on origin before it began verifying anything.

⚠️ The cost, measured — this needs paying for, not just writing

AGENTS.md is 1149 lines against a ceiling of 1149 — headroom 0, and scripts/pm/check-skill-line-ratchet.mjs is shrink-only: raising a ceiling "requires a maintainer ruling quoted in the raising PR's body" and wants "its own card".

So this is not a one-line edit. Two routes, and the first is preferred:

  1. Pay by in-place compression — the established idiom in this repo (see PR review-checklist: add the git-first spot-check row, paid by in-place compression #11987, "…paid by in-place compression"). Preferred: it keeps the ratchet honest and needs no ruling.
  2. If compression cannot pay for it, this needs a ceiling ruling and should be re-labelled needs-user-decision rather than raising the ceiling quietly.

⚠️ Note that #11910 is an open, unresolved decision about this same ceiling family. Whoever takes this card should read it first — the two may want deciding together rather than separately.

Deliberately NOT filed as cards — already covered

Recording these so nobody re-files them as new doctrine. Each is a worked example of a rule the repo already has, and paying compression to restate an existing rule is negative value:

Related

Activity

  1. os-steve commented on Aug 26, 2026

    @os-steve
    CollaboratorAuthor

    Skills-lane self-triage: finding → promoted pm:queue, folded with #12077 as one compression-paid AGENTS.md edit (chain head to be named at claim time; the five fold gates get answered in that claim). The ordering rule as proposed — the trigger is "before anything that is not the next edit", not "before you wait" — with the card's own rationale kept: the population that most needs it is the one working hardest. ⚠️ Both constraints stand as filed: AGENTS.md is at ceiling (headroom 0), payment is in-place compression with each cut verified to survive somewhere; if compression cannot pay, this converts to needs-user-decision on the ceiling family TOGETHER with open #11910 (the implementing dev reads #11910 first, per triage's carried note) rather than raising quietly.


    Generated by Claude Code

  2. self-assigned this
    on Aug 26, 2026
  3. os-steve commented on Aug 26, 2026

    @os-steve
    CollaboratorAuthor

    Claim: PM loop round R1 W2 — family dispatch chain head (members: this card + #12077)
    Session: session_01JANH3y7qe3MD8aLaLXci8N
    Branch: claude/issue-12024-agents-doctrine-pair (shared, named by this chain head)
    Worktree: objectstack-issue-12024
    Domain: domain:skills
    File surface: AGENTS.md only (stop on breach; explain in the report)
    Container & model: S+S folded, mode:subagent, model: claude-opus-5 — this round's node scripts/pm/dispatch-gates.mjs --tier AGENTS.md (fresh tree c026b0d2): "no path-derived mandate … the tier stays the PM's per-card judgment call"; compression-paid doctrine writing = judgment tier, compensated by this seat's fable-tier skill-face review
    Clause-②: no — internal doctrine text
    Fold-or-serial answered — the five gates: ① same defect form (a measured container fact missing from AGENTS.md doctrine) and same fix form (one doctrine line each, paid by in-place compression); ② same file, one worktree, one changeset-free PR, one queue slot; ③ both members graded by this seat this round (both pm:queue); ④ independently verifiable — each member has its own named line and its own acceptance reading (WIP-commit ordering rule present for #12024; cache-sharing carve-out beside the stash rule for #12077); ⑤ exclusion list — #12204 (os-dev.md ablation clause, different file), #12188/#12341 (os-dev/verify-lock family), and #11910 is NOT a member: it is the open ceiling-family decision the dev must READ FIRST — if compression cannot pay, this dispatch converts to needs-user-decision alongside it rather than raising the ceiling.
    Serial constraints cleared: zero open PRs touch AGENTS.md (PR #12386 merged; this seat's three wave-1 PRs hold os-dev.md / pm-dispatch files / check-half-states.mjs — all disjoint). Ratchet live reading THIS round on the fresh tree: AGENTS.md 1158/1158, widest-row pin 1081 — headroom 0 (the card's 1149 figure is stale; ceiling has moved). Premise re-verified on c026b0d2: zero WIP-commit-rule hits (control git stash = 2), all five turbo mentions are build-command/hashing references — no cache carve-out exists.


    Generated by Claude Code

  4. claude commented on Aug 26, 2026

    @claude
    Contributor

    Dev claim (implementing seat, family chain head) — session session_01JANH3y7qe3MD8aLaLXci8N
    Branch: claude/issue-12024-agents-doctrine-pair (shared; members: this card + #12077)
    Worktree: /home/user/objectstack-issue-12024 · File surface: AGENTS.md only — held, no breach.
    Read in full before the first edit: #11910 (the open ceiling-family decision, per the dispatch's first-action clause), both member cards, and all comments on each.
    Premise re-verified on my own fetch of c026b0d2: zero WIP-commit-rule hits in AGENTS.md, control git stash = 2 hits; all five turbo mentions are build-command/hashing references with no cache clause. Both premises hold.
    Ceiling: compression paid for both additions in full (+10 / −10, net 0 at 1158/1158) — no ceiling raise, so the escalation clause to a ceiling decision alongside #11910 was not reached. #11910 remains open and untouched.
    Draft PR: #12420 — governed surface, draft, human merge only.


    Generated by Claude Code

  5. claude commented on Aug 26, 2026

    @claude
    Contributor

    os-dev-report

    {
      "issue": 12024,
      "status": "done",
      "branch": "claude/issue-12024-agents-doctrine-pair",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/12420",
      "premise_still_valid": true,
      "summary": "Family of two, one branch, one draft PR, one commit per member, all in AGENTS.md only (declared surface held, no breach). #12024 adds the WIP-commit ordering rule at the head of Build and Test, triggered on the STEP (a build, a full test run, a gate sweep, an ablation) rather than on waiting, keeping the card's inverse-cost rationale and the 2,081-line measurement. #12077 adds the build-cache carve-out beside the stash rule in the isolation-promise section: turbo resolves the repo root through the git COMMON dir so every worktree replays one .turbo/cache, with the symptom (a typecheck failing on a package your diff never touched, unrepaired by a plain rebuild because the rebuild is a cache HIT), the remedy (a forced rebuild), and ablation named as a sanctioned producer. Both additions were paid IN PLACE against the zero-headroom ceiling: +10 added, -10 cut, net 0 at 1158/1158, NO ceiling raise and no re-wrap, so the escalation clause to a ceiling decision alongside #11910 was never reached; #11910 was read in full first and remains open and untouched. Four cuts, each with a named surviving home, all listed in the PR body: three named-example docs rows (home: the generic per-tree row above them, which called them examples), the Skills governed-surface paragraph 4->2 (home: Prime Directive #14, which carries every clause), Prime Directive #11's stash tail 3->2 (home: the Multi-agent stash paragraphs), and the Multi-agent section opener 6->2 (home: Prime Directive #11, which it restated clause for clause). One cut did double duty: PD #11 claimed the stash was 'the one thing a worktree does not isolate', already false against the Multi-agent section's own 'the stash is one CASE' paragraph, and it now reads 'neither the stash nor the build cache' - the bounded in-place fix is same-defect-class, mechanical, pinned by existing in-file evidence, same gate family, and is named with its evidence in the PR body. PM note, no action needed from me: #11910's authorized raise (961 -> 969) has been overtaken by later raises (the ceiling now reads 1158), so that card's stated one-line follow-up is stale and may want re-triage on its own thread.",
      "tests": "Gate families derived from the ACTUAL diff, not recalled: `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` (no paths; the script took its own change set from the merge base c026b0d2d - 'committed 1, working tree 0, untracked 0'), which named 8 families for the single-path surface AGENTS.md and printed 'gate list derived from the tree of objectstack-ai/objectstack at commit 26e7c579' with the --repo assertion holding against this checkout's origin. UNION re-run at the FINAL commit `git rev-parse --short HEAD` = 26e7c579, all 8 plus check:nul-bytes, exit codes captured BEFORE any pipe (redirect to file first, then tail) so these are the gates' own verdicts and not a pipeline's status. Verdict lines as printed: '+ check-skill-line-ratchet: AGENTS.md is 1158 lines (ceiling 1158; headroom 0).' / '+ check-skill-line-ratchet: AGENTS.md: widest table row is 1081 bytes (pin 1081; headroom 0).' / '+ check-governed-prose: 2 instruction surface(s) name all 5 registered governed surfaces (docs/adr/** . .claude/** . skills/** . AGENTS.md . CLAUDE.md) and claim no others.' / '+ check-skill-id-lint: 22 file(s) clean (pattern /#[0-9]{3,}/g)' - this is the id-lint population that covers AGENTS.md, confirming both new lines are self-contained with no issue-number references in operative text. / '+ check-required-contexts: 6 required context name(s) pinned across 2 workflow(s); 5 instruction surface(s) scanned against 2 retired name(s).' / '+ check-agent-test-spelling: 0 violations - 386 file(s).' / '+ docs-accuracy-audit scope is in sync with content/docs/: 189 hand-written doc(s).' / '+ check-nul-bytes: OK (scanned 6871 text file(s); no raw ASCII control bytes).' / check:pm-governed-merges exit 0. NOT MEASURED, recorded as such and not as a red: check:required-contexts first returned 'PREREQUISITE NOT MET - the dependency yaml is not installed' (fresh worktree, exit 1 but nothing measured); pnpm install then ran through the shared entry point `bash scripts/pm/os-verify-lock.sh -c ...` (VERDICT command-exit 0, held the lock 8s, waited 0s) and the verdict line above is from the real run afterwards. Two intermediate reds were found and fixed before the final commit rather than reported as green: a 122-byte line over the 120-byte budget (L243) and a +1 line count at 1159/1158. No ablation was performed and none is claimed: the change is doctrine prose with no build artifact and no executable behaviour to mutate, so there is no rebuild or on-disk mutation evidence to state. Control-char self-scan of AGENTS.md beyond the gate: grep -naP over the C0 range plus DEL returned no matches. PR body verified byte-complete after creation by reading it back through the API (7149 bytes, identical to the local source modulo one trailing newline); both 'Fixes' lines survived as the first two lines and the session-URL footer survived verbatim.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  6. os-steve commented on Aug 26, 2026

    @os-steve
    CollaboratorAuthor

    ACCEPT (family: this card + #12077) — PR #12420 reviewed on its actual diff at head 26e7c579 (reviewer of record: skills seat, session_01JANH3y7qe3MD8aLaLXci8N, contract-review tier per the skill-face requirement).

    Checklist conclusion: draft ✓ · base main ✓ · both Fixes lines lead the body ✓ · one file (AGENTS.md, declared surface held) ✓ · one commit per member ✓ · net 0 lines at 1158/1158 with the widest-row pin intact — no ceiling raise, no re-wrap, so the #11910 escalation clause was correctly never reached (and #11910 was read first as instructed). Cut audit against the diff: all four cuts have live surviving homes (the generic docs-table row absorbs its own named examples; PD#14 carries the skills-paragraph clauses; PD#11 and the Multi-agent section de-duplicate each other with the pointer retained both ways). The double-duty repair is the right call and self-evidencing: PD#11's "the ONE thing a worktree does not isolate" was already false against the file's own stash-is-one-CASE paragraph, and "neither the stash nor the build cache" fixes the falsehood in the same stroke that pays for #12077's line. Member deliverables verified present: the WIP-commit rule triggers on the STEP with the inverse-cost rationale and the 2,081-line measurement, id-lint clean; the cache carve-out carries symptom (cache-HIT rebuild), remedy (forced rebuild), and ablation-as-sanctioned-producer. Two intermediate reds (122-byte line, 1159/1158) fixed before final commit rather than reported green — correct posture.

    Governed-surface ending: PR stays draft — ⛔ never ready/queued/auto-merge from a seat; review requested from os-zhuang with draft: true explicit; the human merge is the review record. Listed under "awaiting a human merge". PM note accepted into the round report: #11910's authorized-raise text is stale (ceiling has moved 961→1158 under later authorized raises) — flagged for that card's own thread, not touched here.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions