Skip to content

Fix the queue, proof and guard defects from the adversarial review - #4

Open
addyosmani wants to merge 6 commits into
mainfrom
fix/adversarial-review-3
Open

Fix the queue, proof and guard defects from the adversarial review#4
addyosmani wants to merge 6 commits into
mainfrom
fix/adversarial-review-3

Conversation

@addyosmani

Copy link
Copy Markdown
Owner

Fixes the twelve findings in #3. All of them reproduced; the notes below are only for the places where the fix needed a decision, or where a boundary moved.

Closes #3

Changed safety boundaries

Negative-test proof got stricter, and slower to satisfy. prove-test.sh used to accept any non-zero exit from the reverted run. Reverting deletes new implementation files, so a test that only imports the new module fails to load whether or not it asserts anything — an assertion-free test was certified as proving the fix. It now classifies the failure and reports PROVEN only when the test actually ran and failed.

The cost is real and worth agreeing to before merging: for a new module, the information isn't there. An import error and an assertion failure are the same observation from outside. That shape now reports UNPROVEN, the verifier records could-not-determine, and the PR lands at accepted-with-reservations with a human read. Bug fixes to existing code still get a genuine proof. A test command that fails silently is also UNPROVEN, which is a deliberate nudge toward real runners over shell predicates. LIMITS.md section 8 documents this.

Every factory PR is now explicitly a draft. ROUTINES.md and the implement skill disagreed about what a non-draft factory PR meant. README, ADVICE.md, and the charter already say every run ends at a draft, so that's the reading I kept; the four conditions that used to gate draftness now set Human read required instead. If conditional drafts were the intent, this is the commit to reverse.

gates.sh sweeps REQUIRED at the end. A required gate the DETECT block never reached emitted neither run nor skip and left the verdict GREEN. REQUIRED_FULL="... mutation" and build on a Python repo both hit it, and both are configs gates.conf's own comments invite. Anyone who has already tuned gates.conf may find a gate they thought was green is actually MISCONFIGURED — which is the point.

The push guard matches refspec destinations. git push origin +main and git push origin mybranch:main both got through before. .factory/scripts/ is now protected too; the path check looks for a write verb or a redirect target rather than any command that mentions a protected path, so prove-test.sh ... > log still runs.

Not fully fixed

Back-pressure is read at run start and the counted label is applied at run end. The count now includes in-progress, which closes the window two overlapping runs used to slip through, but it isn't compare-and-swap. Claims have one in the remote ref; back-pressure doesn't, and I didn't invent one.

Verification

bash tests/run.sh passes. New coverage, each written against the failing behavior first:

  • test-gates.sh — a required gate the stack never reaches must be MISCONFIGURED, and the stock config must still reach GREEN
  • test-proof.sh — load failure and unclassified failure both report UNPROVEN; the classifier is pinned to sample output from pytest, jest, vitest, go and cargo; co-located test_* files stay out of the revert; the default path runs under bash 3.2
  • test-hook.sh — every spelling of a push at a protected branch blocks, while claiming and releasing a claim branch still works
  • test-doctor.sh — a repo where install.sh skipped settings.json must not report healthy

A required gate the DETECT block never reaches emits neither run nor skip,
so the verdict stayed GREEN. gates.conf's own comments invite exactly that
config: mutation is gated to deep, and the python arm has no build line.
Sweep REQUIRED at the end and record any gate with no verdict as
MISCONFIGURED.
Reverting the non-test hunks deletes new implementation files, so a test
that only imports the new module fails to load whether or not it asserts
anything, and that was reported as PROVEN. Classify the reverted run and
report UNPROVEN (exit 3) unless the test actually ran and failed.

Also fixes an empty test_paths array under set -u on bash 3.2, and adds
pytest's co-located src/utils/test_foo.py layout to the default patterns.
git push origin +main and git push origin mybranch:main both passed the
protected-branch regex, which only recognised a destination written as a
bare name, refs/heads/, or HEAD:. Match the refspec destination, and treat
any +refspec as a force push.

Guard .factory/scripts/ as well: prove-test.sh was the one load-bearing
script with no write protection. The path check now looks for a write verb
or a redirect target rather than any command mentioning a protected path,
so running a guarded script and capturing its output still works.

doctor now checks that settings.json and block-merge.sh were installed and
wired, which install.sh's skip-if-exists can silently leave undone.
The implement routine opened a draft PR unconditionally while verify
triggered on pull_request.opened filtered to non-drafts, so stage 3 never
ran and every check happened inside the implementer's own session. Drop the
draft filter.

The skill and ROUTINES also disagreed about what a non-draft factory PR
meant. Every factory PR is a draft, matching README and ADVICE.md; the four
conditions that used to gate draftness now set Human read required.
…tates

Three ways the live queue jammed for good.

Nothing cleared factory:awaiting-review and the PR body carried no Closes
keyword, so merged work counted against back-pressure forever. The PR body
now closes the issue, and back-pressure counts open issues, in-progress
included.

An abort after claiming released the label but not
refs/heads/claude/fq-<n>, so every later run picked the same item and lost
the push race to its own ghost. Delete the ref first; if the delete fails,
park the item at in-progress rather than advertise something no run can
take.

Triage no longer re-triages in-progress or awaiting-review items, which
could strip a claim mid-implementation. Handoff comments now require a
collaborator author, and gate_level is a floor rather than a dial.
Both verification paths now read the PROOF line rather than the exit code.
A new module cannot be proved this way at all: report could-not-determine
and accept with reservations instead of rejecting or retrying.

Carries the untrusted-input rule into the two skills that consume handoff
fields, and dates the limit itself in LIMITS.md.
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.

Adversarial review at 8af1165: the verify stage never fires, prove-test.sh proves too much, and three more silent happy-path failures

1 participant