Skip to content

Structural checks: prevent new prose restatements and cycle-decision bypass #362

Description

@brenpike

Deferred preventive half of the root-cluster remediation on #361. The current sites are fixed there; this issue covers the guard against future authoring.

Root cause

Prose-as-contract. The watch loop's operative contract was expressed as free prose duplicated across seven-plus independent copies, with no single authoritative statement and no enforcement at the site that consumes it. Each copy was an independent drift surface.

Three consecutive review iterations each found a different copy drifted, or a different prose assertion the executable never enforced:

  1. Pre-PR local review — the re-arm seed was specified after the dispatch it must precede.
  2. Post-PR cycle 0 — arm-expiry re-seeding reopened the blind window; the snapshot-seeded APPROVED_PRESENT suppressed a standing approval.
  3. Post-PR cycle 1 — ADR-0029:61 still instructed the "re-capture a seed" that cycle 0 had removed (and :53 carried a second copy of the same wrong ordering); the prose-declared six-cycle floor was never enforced by loop-state.sh.

The recurrence rate was itself the signal.

What #361 fixes

  • max_remediation_cycles floor becomes a named constant owned and enforced by plugin/skills/github-review-loop/scripts/loop-state.sh, exposed via a floor subcommand. Every prose site drops the literal and cites the script. This closes the class for the number: no live prose states it, so no prose statement can diverge from the enforced value.
  • The seed-capture rule collapses to one named INVARIANT block in plugin/skills/github-review-loop/SKILL.md; every other site, including ADR-0029, is reduced to a reference.

What this issue covers

Two structural checks in tools/policy_check.sh, neither of which exists today:

  1. New-caller bypass. Detect a caller that re-implements the cycle arithmetic instead of routing through loop-state.sh cycle-decision, and therefore never reaches the floor guard. This is the rule-7 absence defect named in tests/policy/safety-cycle-floor-single-source.json — a presence pin cannot see a site that simply does not call the thing.
  2. New prose restatement. Detect a newly-authored copy of a single-sourced contract. feat!: make the post-PR watch the default with a bounded idle window #361 deletes every current restatement, but nothing makes a future one unrepresentable, so the seed-rule half of that remediation narrows the class rather than eliminating it. That partial closure is recorded as a residual in ADR-0029 rather than claimed as a fix.

Bounded impact

No current caller undercuts the floor, and after #361 no live prose restates the seed rule or the number. The exposure is future-authoring only, and it is invisible to CI — which is precisely why it warrants a structural check rather than another review pass.

Why deferred rather than done in #361

#361 already changes tools/, and validator regressions are the worst failure class in this repo: a broken check is silent and leaves the suite green through real defects. Adding validator surface for preventive-only value, inside an already-large MAJOR release, trades a real risk for a hypothetical one.

Review threads

References

  • plugin/governance/remediation-doctrine.md — Closed-by-Construction Acceptance Test; Cross-Iteration Same-Surface Recurrence; Defer-with-Scope
  • tests/policy/README.md — pin-authoring contract, rule 7 (absence defects need a structural check, not a presence pin)
  • CLAUDE.md — the "unprotected coupling (P3)" pitfall
  • docs/adr/0029-default-pr-watch-and-idle-window-lifecycle.md

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions