Skip to content

Make the checkout form Phase 4's default Tests-fix revert and gate ladder rung 1 - #136

Merged
dmccoystephenson merged 3 commits into
mainfrom
fix/tests-fix-revert-mechanism
Aug 27, 2026
Merged

Make the checkout form Phase 4's default Tests-fix revert and gate ladder rung 1#136
dmccoystephenson merged 3 commits into
mainfrom
fix/tests-fix-revert-mechanism

Conversation

@dmccoystephenson

@dmccoystephenson dmccoystephenson commented Aug 25, 2026

Copy link
Copy Markdown
Member

Summary

Two defects in Phase 4's Tests-fix rubric item and its fallback ladder are
corrected in create-dev-loop.md. Both were reported from generated instances,
both live in the same paragraph pair, and neither is separable from the other
without leaving the surrounding text self-contradictory — so they are batched as
one coherent change.

  • The prescribed revert was a structural no-op (Phase 4's Tests-fix stash-and-run is structurally a no-op, because the fix is already committed by the time it runs #116). The rubric item
    instructed git stash push -- <src files> to revert the fix. By the time that
    item is reached, Phase 3 has committed the fix and Phase 4 has pushed the
    branch and opened the PR, so the working tree is clean and the stash saves
    nothing. What follows is worse than a missing check: the subsequent run
    executes against the unmodified (fixed) tree and passes, git stash pop
    then fails with No stash entries found, and the sequence is naturally scored
    as "reverted, still passed → false negative in the test" when in fact nothing
    was ever reverted. The checkout form —
    git checkout origin/{{DEFAULT_BRANCH}} -- <src files> to revert,
    git checkout HEAD -- <src files> to restore — is now stated as the default,
    because it is correct in the state Phase 4 actually finds the tree in. Stash
    is demoted to the rarer genuinely-uncommitted case, and a new instruction
    requires the revert to be confirmed to have changed the tree
    (git status --porcelain / git diff --stat) before either half of the
    result is trusted.
  • Ladder rung 1 had no entry condition (Tests-fix ladder rung 1 is unsafe under headless dispatch, and has no entry condition saying so #132). Rung 1 pushes a head that
    deliberately undoes the PR's own fix, waits for a red signal, then resets and
    force-pushes. It is therefore only safe when the session is certain to survive
    both round-trips. Under a headless or timeout-bounded dispatch there is no
    human present and the run may be killed at any point, which produces exactly
    the stuck-mid-revert state that Phase 1's orphaned-PR handling was added to
    repair. An entry condition now forbids rung 1 in that situation and directs
    the reader through to rung 2 or rung 3's hand-off.

The ladder's preamble is rewritten to match: its "before concluding the local
anchor cannot run, retry with a checkout-based revert" instruction is removed,
since that retry has become the default, and the entry condition is restated so
that a failure of one git mechanism is explicitly not the same as an
unavailable tool or interpreter. Two downstream references to the experiment
by its old name (the Phase 4 self-review comment example and Phase 8's
regression gate) are renamed from "stash-and-run" to "revert-and-run".

Two refinements beyond the two issues' literal scope

Disclosed rather than smuggled. Both were produced by the Phase 4 self-review of
this PR's own first draft and are recorded in the self-review comment.

  • Rung 1 is stated to be unavailable where the anchor is manual. The first
    draft replaced rung 1's hardcoded CI with {{EXTERNAL_SIGNAL_LABEL}}, on
    the reasoning that a hardcoded anchor name is the defect PR Fix documentation drift across the three sources of truth #57 corrected
    elsewhere. On review that was wrong and has been reverted: rung 1 pushes a
    commit and reads a check result, so it is inherently CI-specific and cannot be
    performed at all where the label names a manual checklist. The literal wording
    is restored, and a sentence now says so outright rather than leaving a
    generated skill to substitute its way into an instruction it cannot follow.
  • The revert target is the merge base, not the branch tip. The checkout form
    as first written said git checkout origin/{{DEFAULT_BRANCH}} -- <src files>,
    which also pulls in anything that landed on the default branch since the
    branch was cut, so the experiment would no longer isolate this PR's change.
    The superseded ladder text had this precision (<merge-base-with-…>) and
    dropping it would have been a regression; git merge-base is now named.

Research grounding

  • RESEARCH.md §1 (Self-critique without an external signal is unreliable,
    confidence: high) covers both issues. §1's entry for PR Apply consolidated template-rule backlog (#36, #39–#51) #54 records that the
    Tests-fix item exists specifically to ground regression scoring empirically
    rather than by judgment; a revert that silently does nothing returns that item
    to unanchored judgment while still reporting an anchor's verdict. §1's entry
    for PR Add Tests-fix CI-revert fallback ladder and scratch-file guidance #67 records the ladder's addition for the case where the local anchor
    cannot run; a rung whose interruption leaves the branch's head undoing its own
    fix damages the anchor it was meant to substitute for. An Implementations
    entry for this PR is added under §1.
  • RESEARCH.md §3 (localization is the real bottleneck, confidence: high)
    is the source of the regression gate that Tests-fix feeds, quoted at Phase 8:
    regression evidence is the only way to distinguish a real fix from a
    coincidental patch. A no-op revert manufactures that evidence rather than
    collecting it. An Implementations entry is added under §3 as well.

Doc sync check

  • Every {{placeholder}} added or changed has a corresponding Step 4
    substitution-table row — {{DEFAULT_BRANCH}} and
    {{EXTERNAL_SIGNAL_LABEL}} were already used elsewhere in Phase 4 and
    already carry rows; python3 scripts/check_docs.py confirms parity
  • RESEARCH.md updated with Implementations entries under §1 and §3

README.md's "What it does" line is not listed: no Step was added, removed, or
renamed by this diff, so the 1:1 list is unchanged.

Test plan

  • python3 scripts/check_docs.pyDoc consistency check passed.
  • python3 -m unittest discover -s tests -vRan 13 tests ... OK
  • CI doc-consistency green on the PR head
  • Phase numbering unchanged; no fenced code block was opened or closed by
    this diff, only edited within, so no new fence needed escaping
  • No repo-specific build command, reviewer name, or branch prefix was
    introduced into create-dev-loop.md
  • Steps 3, 5, and 6 are untouched, so nothing here is a breaking change for
    gardener
  • Phase 4's Tests-fix stash-and-run is structurally a no-op, because the fix is already committed by the time it runs #116's central claim was verified against source rather than accepted from
    the issue body: Phase 3's staging-hygiene rule ends at "before
    committing", and Phase 4 opens with git push -u origin ..., so the fix
    is committed before the rubric runs and the tree is clean
  • Empirical revert performed rather than reasoned about, using the very
    mechanism this PR promotes: with create-dev-loop.md checked out from
    origin/main, the rubric item reads git stash push with no checkout
    alternative and rung 1 carries no entry condition; with the fix restored,
    both are present. scripts/check_docs.py passes in both states, which
    is the point of [self-audit] The repo's own validation anchor is structurally unrunnable in a headless dispatch, so template changes can never merge autonomously #109 — CI cannot observe this class of defect
  • UNVERIFIED — the behavioral anchor could not run. This repository's
    anchor for a template change is a manual /create-dev-loop run against a
    real target repository, per CLAUDE.md's "Testing changes" section. That
    run creates a GitHub repository in Step 6 and writes outside the checkout
    in Steps 3 and 5, neither of which this dispatch is authorized to do, and
    it is structurally unrunnable headlessly in any case ([self-audit] The repo's own validation anchor is structurally unrunnable in a headless dispatch, so template changes can never merge autonomously #109). This PR
    changes create-dev-loop.md, which is anchor-relevant, so per Phase 4's
    scope gate it is handed to a human rather than merged autonomously. None
    of the five checklist items in "Testing changes" was exercised

Retrofit note

Per CLAUDE.md's "Promoting a rule into the template": both rules are
template-wide, so every generated skill predating this change keeps the old
git stash wording and the ungated rung 1 until it is regenerated.
Retrofit
passes are likely needed on any existing <slug>-dev-loop skill and are not
opened here — this dispatch is path-restricted to this repository's own checkout
and cannot reach the other skill repositories, and only the person running those
skills can see which ones exist. Two are known by name:

Deferred this cycle

Recorded for auditability, per Phase 1's skip-reason rule. All open issues not
listed below were left untouched because this cycle was scoped to the two
Phase-4 Tests-fix defects above.

Closes #116
Closes #132

This PR description was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).


drafted by Claude on behalf of Daniel Stephenson

…ng 1

The Tests-fix rubric item prescribed `git stash push` on a tree that Phase 3
has already committed and Phase 4 has already pushed, so the stash saved
nothing, the check ran against the unmodified tree, and the no-op read as a
false-negative in the test. The checkout form is now the default, stash is
demoted to the genuinely-uncommitted case, and the revert must be confirmed to
have changed the tree before either half is trusted.

The fallback ladder loses its "retry with checkout first" preamble, since the
retry is now the default, and rung 1 gains an entry condition: a headless or
timeout-bounded dispatch must not push a head that undoes its own fix, because
a killed session leaves nothing to restore it.

Closes #116
Closes #132
Finding 1's entry covers both defects: a revert that silently does nothing
returns Tests-fix to unanchored judgment while still reporting an anchor's
verdict, and a ladder rung whose interruption leaves the head undoing its own
fix damages the anchor it substitutes for. Finding 3's entry covers the
consequence for the regression gate it motivated in PR #31 — manufactured
evidence cannot distinguish a real fix from a coincidental patch.
Two self-review findings.

Substituting {{EXTERNAL_SIGNAL_LABEL}} into the ladder preamble and rung 1 was
wrong: rung 1 pushes a commit and reads a check result, so it is inherently
CI-specific and cannot be performed at all where the anchor is a manual
checklist. The literal wording is restored and a sentence now states that
unavailability outright.

The checkout form said `git checkout origin/<default> -- <src files>`, which
also pulls in anything that landed on the default branch since the branch was
cut. The merge base is named as the correct revert target.
@dmccoystephenson

Copy link
Copy Markdown
Member Author

Phase 4 self-review

Posted as a plain comment rather than a formal review object: reviewer
independence is not implied here, and gh pr review is unavailable to this
dispatch in any case.

Universal rubric

  • Scope: PASS, after a FAIL that was fixed. The final diff touches
    create-dev-loop.md and RESEARCH.md and nothing else — 9 insertions, 5
    deletions across 2 files, all of it inside the Phase 4 Tests-fix item, its
    ladder, and the two downstream references to that experiment by name. The
    first draft scored FAIL on this item: rung 1's hardcoded CI had been
    replaced with {{EXTERNAL_SIGNAL_LABEL}}, which neither issue asked for. The
    substitution was reverted in 94f494c, and not merely for scope — it was also
    substantively wrong (see Instruction-quality findings below). The one
    remaining out-of-scope refinement, naming git merge-base as the revert
    target, is disclosed in the PR body under "Two refinements beyond the two
    issues' literal scope".
  • Tests-new: no signal this cycle. No public function was added; the diff is
    template prose plus two RESEARCH.md entries. tests/test_check_docs.py
    needed no new case because scripts/check_docs.py is untouched.
  • Tests-fix: PASS — confirmed empirically, using the very mechanism this PR
    promotes.
    With create-dev-loop.md reverted via
    git checkout origin/main -- create-dev-loop.md, git status --porcelain
    reported M create-dev-loop.md (so the revert demonstrably changed the tree,
    which is the confirmation step this PR adds), and both assertions were
    false: the rubric item did not name the checkout form as default, and rung
    1 carried no entry condition. After git checkout HEAD -- create-dev-loop.md,
    both were true. python3 scripts/check_docs.py printed
    Doc consistency check passed. in both states — a direct demonstration of
    [self-audit] The repo's own validation anchor is structurally unrunnable in a headless dispatch, so template changes can never merge autonomously #109, that CI cannot observe this class of defect.
  • Sibling renames: PASS. "stash-and-run" was renamed to "revert-and-run" at
    every site that names the current mechanism: the ladder preamble
    (create-dev-loop.md:372), the self-review comment example (:395), and
    Phase 8's regression gate (:464). RESEARCH.md:48 retains the old phrase
    deliberately — that line is the Implementations entry recording what PR
    Apply consolidated template-rule backlog (#36, #39–#51) #54 shipped in June, and rewriting a historical record to match present
    wording would falsify it.
  • Sibling structure: no signal this cycle. No file was created.
  • Docs: PASS. Every row of the Phase 7 sources-of-truth table was checked
    against this diff; findings are recorded below under Phase 7.
  • Issue resolution: PASS. Phase 4's Tests-fix stash-and-run is structurally a no-op, because the fix is already committed by the time it runs #116's named surface — the Tests-fix rubric item's
    git stash push instruction and the ladder preamble's "retry with checkout
    first" framing — is changed exactly as the issue's suggested wording proposed.
    Tests-fix ladder rung 1 is unsafe under headless dispatch, and has no entry condition saying so #132's named surface — an entry condition on rung 1 — is added. Neither is
    partially resolved.
  • CI: PASS. doc-consistency is green on head 94f494c
    (run 32820846200, 9s). Locally, python3 scripts/check_docs.py
    Doc consistency check passed. and python3 -m unittest discover -s tests -v
    Ran 13 tests ... OK, so the anchor is confirmed to have actually executed
    tests rather than exiting 0 on zero.

Repo-specific rubric

  • Placeholder parity: PASS. {{DEFAULT_BRANCH}} and
    {{EXTERNAL_SIGNAL_LABEL}} are the only placeholders touched and both already
    carried Step 4 rows; check_docs.py confirms parity mechanically.
  • README Step parity: PASS. No Step was added, removed, or renamed, so the
    1:1 "What it does" list is unchanged and correct.
  • Fence escaping: PASS. No fenced code block was opened or closed by this
    diff — only prose inside existing ones was edited — so no new fence needed the
    leading backslash.
  • Phase numbering: PASS. No phase was renumbered. The one cross-reference
    added ("the autonomous-batch rule in Phase 5") was verified to point at the
    right phase: create-dev-loop.md:420 is where that rule lives.
  • Version comments: PASS. The generated skill's header is untouched, so
    template-version / generated-at remain directly below the H1.
  • Research grounding: PASS. RESEARCH.md §1 and §3 are cited in the PR body,
    and an Implementations entry for this PR was added under each.
  • No back-ported specifics: PASS. No repo-specific build command, reviewer
    name, or branch prefix entered create-dev-loop.md.
  • gardener interface: PASS. Steps 3, 5, and 6 are untouched, so nothing here
    is a breaking change for gardener.
  • Retrofit note: PASS. The PR body names create-dev-loop-dev-loop and
    medieval-factions-dev-loop as likely needing a retrofit pass, and states why
    the passes are not opened from this dispatch.
  • Behavioral validation: FAIL — and this is the item that gates the merge.
    This repository's anchor for a template change is a manual /create-dev-loop
    run against a real target repository. That run creates a GitHub repository in
    Step 6 and writes outside the checkout in Steps 3 and 5, neither of which this
    dispatch is authorized to do, and [self-audit] The repo's own validation anchor is structurally unrunnable in a headless dispatch, so template changes can never merge autonomously #109 records that it is structurally
    unrunnable headlessly regardless. None of the five checklist items in
    CLAUDE.md's "Testing changes" section was exercised. Per Phase 4's scope
    gate the change is anchor-relevant, so the PR is marked UNVERIFIED and handed
    to a human rather than merged autonomously.

Instruction-quality findings folded in from the diff read

Both were found by reading the first draft as a generated skill would receive it
after substitution, and both were fixed in 94f494c rather than left as
comments.

  • create-dev-loop.md:373 — rung 1 had been generalized to
    {{EXTERNAL_SIGNAL_LABEL}}. Substituted for a repository whose label is
    manual validation, the instruction became "push a commit, confirm manual
    validation goes red" — an instruction no reader can follow, since rung 1
    inherently requires an automated remote signal. The literal CI is restored
    and the rung's unavailability under a manual anchor is now stated outright,
    which is strictly more useful than a substitution that silently produces
    nonsense.
  • create-dev-loop.md:365 — the checkout form named origin/{{DEFAULT_BRANCH}}
    as the revert target. Where the default branch has advanced since the branch
    was cut, that reverts more than this PR's change and the experiment stops
    isolating the fix. The superseded ladder text carried this precision
    (<merge-base-with-…>); dropping it would have been a regression, so
    git merge-base is now named.

Phase 7 — documentation accuracy check

Each row of the sources-of-truth table was checked against this PR's
implementation:

  • create-dev-loop.md — internally consistent; confirmed mechanically by
    check_docs.py and by hand for the Phase 5 cross-reference.
  • README.md — no Step change, so the 1:1 list still holds.
  • RESEARCH.md — the two new entries were checked against §1's and §3's actual
    claims and against the PR numbers they reference (Add pre-edit localization verification and pre-merge regression gate #31, Apply consolidated template-rule backlog (#36, #39–#51) #54, Add Tests-fix CI-revert fallback ladder and scratch-file guidance #67).
  • CONTRIBUTING.md, SECURITY.md, scripts/README.md,
    .github/PULL_REQUEST_TEMPLATE.md, .github/ISSUE_TEMPLATE/*.md — a search
    across the repository for stash, Tests-fix, fallback ladder, and rung
    returns hits only in create-dev-loop.md and RESEARCH.md, so no other
    document restates the mechanism this PR changed and none required updating.
  • CLAUDE.md — its rules still hold after this PR; nothing in it describes the
    Tests-fix mechanism, so no edit is needed (which is fortunate, as editing it
    is harness-blocked).

A complete pass found nothing further to fix.

Merge state

The do-not-auto-merge path check was run: the modified paths are
create-dev-loop.md and RESEARCH.md, matching no universal entry
(.github/workflows/*, security/, a single file with >50 lines deleted — 5
deletions total) and no repo-specific entry (CLAUDE.md,
scripts/check_docs.py, version.txt, or a Step 3/5/6 change affecting
gardener). The blocker is therefore not a protected path but the UNVERIFIED
behavioral anchor above, compounded by the fact that this dispatch carries no
merge authorization. The PR is left open for human review and merge.

This review was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).


drafted by Claude on behalf of Daniel Stephenson

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

Labels

None yet

Projects

None yet

1 participant