Skip to content

Merge the /dev line to master (#30) - #77

Open
laird wants to merge 23 commits into
masterfrom
merge/30-dev-line-v3
Open

Merge the /dev line to master (#30)#77
laird wants to merge 23 commits into
masterfrom
merge/30-dev-line-v3

Conversation

@laird

@laird laird commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Closes #30. Also resolves #70 (integration CLAUDE.md lost its autocoder config section) by bringing master's copy forward.

Why this took three attempts

fix.md exists on both sides — 2341 lines on master, a 7-line alias stub on the /dev line — while the real command moved to dev.md. Git sees a plain modify/modify conflict and cannot see the rename, so resolving it either way silently destroys one side's work. Two earlier attempts did exactly that in opposite directions.

The fix was to port first, merge second: master's 308-line fix.md delta was 3-way merged onto dev.md (base = fix.md at the merge base) as its own commit, after which the fix.md conflict collapses safely to "take the stub".

Verified content, not just a clean merge

Three independent audits found three different losses in the earlier attempts. All are confirmed present here:

Check Result
CLAUDE.md ## Automated Testing & Issue Management ✅ 1 (fixes #70)
dev.md DEFAULT_BRANCH ✅ 3
dev.md api-push ✅ 3
regression-test.sh detect_skip_reason ✅ 3
fix.md reduced to alias stub ✅ 7 lines

Notable resolutions decided on merits rather than a blanket ours/theirs:

  • regression-test.sh → /dev line. It is a strict superset: it already carries master's autocoder: regression suite reports a false green on macOS — every grep -oP stat parser silently yields 0 #20 portable helpers and adds detect_skip_reason / is_node_based_command. Taking master loses graceful degradation for repos with no package.json.
  • worker-launch-lib.sh → master. It adds the claude-worker-loop.sh architecture and model tiers; the /dev line would have regressed it.
  • .agent/scripts/start-parallel-agents.sh → /dev line. Master's claude) case launches gemini — a real bug from wholesale re-branding.
  • marketplace.json → higher version on each side.

Verification status

Local (macOS): 124/124 pytest, all tracked shell files parse, key markers asserted above.

CI has never run on this work — that is the point of this PR. The shell suites are ubuntu-only in CI and are exactly what was broken in #57/#58/#63, so green CI here is the real gate.

Note on overlap

PR #72 also fixes #70, on a different base. One of the two should be closed rather than both merged.

laird and others added 23 commits July 28, 2026 11:02
Design spec for adding a human-gated planning capability to the manager
session (brainstorm → spec → critical review → decompose to stories →
existing fleet implements), plus renaming /fix→/dev and /fix-loop→/dev-loop
with aliases. Reuses existing skills and the decomposed/subtask convention;
adds two backlog-aware behaviors to the manager loop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Critical-design-review round 1 approved the planning-pipeline spec as-is:
0 literal-wrongness findings, 0 forced decisions. Empirically confirmed the
two load-bearing hand-off facts (subtask is claimable; decomposed parents are
excluded from claiming). Recorded the CDR approval in the spec status and
carried the three CIR-deferred items forward as notes B8/B9 (+ existing B5).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…eview-1 to autocoder-planning-pipeline-implementation-plan
Task 1 of docs/plans/2026-07-13-autocoder-planning-pipeline-implementation-plan.md.
Edits the in-file self-references in the renamed command/workflow files and
adds thin forwarding stubs at the old names. The .claude/fix-loop.local.md
state file keeps its name (plan assumption #7).

Also adds the /dev mapping-table entry and autonomous gate policy for
peters-toolkit:bugfix, so bug work routes through that skill without its
in-chat STOP gates stalling the unattended loop.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Task 2 of the planning-pipeline implementation plan. Loop hot-path callers
(gate, dispatch, monitor-workers, worker-launch-lib) use the new names
directly rather than the alias stubs, to avoid a double hop per iteration.
Also updates the drift checker's hardcoded paths, hook/script message text,
sibling command docs, and the tests that asserted the old worker command.

Script filenames and the .claude/fix-loop.local.md state file are unchanged
per the plan's scope exclusions.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…er to 4.4.0

Task 3 of the planning-pipeline implementation plan. commands[] now lists
dev.md/dev-loop.md plus fix.md/fix-loop.md as aliases so both resolve.
Versions: autocoder 4.3.0→4.4.0 in both plugin.json and both marketplaces;
marketplace roots 3.22.0→3.23.0 (.claude-plugin) and 3.19.0→3.20.0
(.factory-plugin), per CLAUDE.md's version-management rule.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Task 4 of the planning-pipeline implementation plan, plus the user-facing
docs the rename touches: CLAUDE.md mapping rows with an alias note, the
GEMINI.md command table and tree listing, plugin/top-level READMEs, and the
per-platform guides. Renames the multi-agent coordination guide to
docs/dev-loop-multi-agent-coordination.md (nothing linked to it).

Historical records under docs/specs, docs/plans, docs/criticalreviews and
docs/HISTORY.md keep the old names by design.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Tracks the 2026-07-28 handoff documenting the /fix→/dev rename and
peters-toolkit:bugfix integration, plus the still-unaddressed critical
review of the planning-pipeline implementation plan.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The `working` label is the swarm's concurrency lock, but dev.md told agents to
release it at any stop — "Remove it in ALL exit paths", "ALWAYS run this before
moving to the next issue", "applies to ALL outcomes". An agent following that
after a partial commit dropped the lock while the issue was still OPEN, which
put it straight back in the claimable pool with a worker still holding the
branch. A peer then double-claimed and the duplicate work was thrown away.

Root cause: the lock was treated as per-step rather than per-claim. Partial
progress is not a terminal outcome.

Changes:
- Replace the blanket rule with a terminal-outcome-only rule: release on close,
  on an explicit announced release (blocked/skipped/abandoned), or when the
  claim never started (branch switch failed). Nothing else.
- Name the non-release points explicitly: partial commits, batch boundaries,
  context compaction, pauses, and "moving on while the issue is still open".
- Include the anti-pattern as a WRONG example so it reads as prohibited rather
  than merely unmentioned.
- PR-creation path now retains the lock — the issue stays open until the merge
  closes it, so releasing there was itself a re-expose vector.
- Paused-enhancement path retains the lock; the branch is preserved and the
  worker intends to resume.
- Enhancement-skip path now comments first, then releases, so a hand-off is
  visible instead of a lock that silently vanishes.
- Mirrored identically to .agent/workflows/dev.md per the parallel-maintenance
  requirement.

Verification: tests/test_dev_working_lock.py (23 assertions) guards the wording,
requires every remaining release site to sit beside a terminal justification,
and asserts mirror parity. Mutation-checked: reintroducing the old phrasing
fails 3 tests. Full suite 82 passed; check-optional-skills-drift.sh clean.

Note: abandoned locks remain /monitor-workers' stale-detection responsibility —
this change deliberately does not add a second reclaim path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Also carries worker-1's four unmerged regression-test hardening commits
(3d1e081, 7e0a8be, e554f0b, 7b97c66), squashed in because the corporate proxy
blocks git-receive-pack and the GitHub API cannot create multi-parent commits.
Original history remains on the local worker-1 branch.
origin/master fixed #15 in plugins/autocoder/commands/fix.md (3124b99). The
/fix -> /dev rename on this branch reduced fix.md to a seven-line alias stub and
moved the body to dev.md — but the arbitration did not come across. dev.md at
e620f99 had zero occurrences of autocoder-claim / CLAIM_MARKER_COUNT /
REMOTE_BRANCH_EXISTS.

So merging this branch into master would replace master's hardened fix.md with a
dev.md that never had the hardening: #15 regresses silently, and it is already
closed so nobody is watching. This is not a duplicate of 3124b99 — it is the
same fix carried across the rename to the file that survives it.

The ported block is byte-identical to master's (asserted in the tests).

Three further defects, none of which exist upstream:
- Specified-issue path had a second, weaker label-add claim after the
  arbitration that aborted via `exit 1` WITHOUT releasing the label it had just
  added — leaking the lock. Removed as redundant.
- Enhancement path used a label-only claim and leaked on race: the Claude
  mirror exited without releasing; the .agent mirror did not exit at all and
  fell through to implement anyway. Both now use issue_claim + arbitration and
  release before aborting.
- .agent assigned ISSUE_NUM *after* the block that references it, so a verbatim
  port would have claimed an empty issue number. Reordered.

Verification: tests/test_dev_claim_arbitration.py (23 assertions) pins each
element of the arbitration, requires every back-off path to actually release,
asserts ISSUE_NUM precedes the claim, and checks mirror parity. Mutation-checked:
reverting to the weak claim fails 5 tests. Full suite 105 passed; drift check
clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Published via the GitHub API — a Zscaler proxy blocks POST git-receive-pack.
This is a single commit on an unmoved integration head, so it maps 1:1 with no squash.
CI ran a single command, `pytest tests/`, which collects Python tests only. All
nine `tests/*.sh` suites were therefore never executed — including those
guarding the claim lock, the issue backend, and the multiplexer probe. CI was
green, and that green said nothing about any of them.

Changes:
- Add scripts/run-shell-suites.sh — runs every tests/*.sh, keeps going after a
  failure so one broken suite can't mask the rest, summarises, exits non-zero
  if any failed.
- Wire it into .github/workflows/test.yml as a separate step, with
  `if: !cancelled()` so a pytest failure doesn't suppress the shell report.
- Add tests/test_ci_runs_shell_suites.py (7 assertions).

Findings while implementing:

- The suites all pass; none needed fixing. Three appeared to fail locally, but
  only because an exported ISSUE_SOURCE from the developer's shell overrode
  each suite's own .issues/ fixture — the same class of bug as #19. The runner
  scrubs ISSUE_SOURCE/ISSUE_BACKEND/ISSUE_DIR_PATH per suite so local and CI
  agree. Without that, three suites fail locally and pass in CI, which teaches
  people to ignore local reds.

- tests/fixtures/*.sh are setup|teardown helpers, not suites; running them
  would mutate .issues/ and assert nothing. A bash glob doesn't cross `/`, but
  `git ls-files 'tests/*.sh'` does — so the runner documents why it must not be
  "simplified" to git ls-files or `find tests -name '*.sh'`, and a test asserts
  neither appears in executable lines.

- The runner lives in scripts/, not tests/, so it can't match its own glob.

The issue cited 15 shell suites; this branch has 9 (the count likely included
PR branches). The runner globs, so the number is not hard-coded.

Verification: 112 pytest tests pass (105 + 7 new); 9/9 shell suites pass; drift
check clean. Both guards mutation-checked — injecting a failing suite makes the
runner exit 1 and name it, and deleting the CI step turns
test_ci_invokes_the_shell_runner red. One of the new tests initially failed by
matching its own cautionary comment; fixed to inspect executable lines only.

Closes #32

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Published via the GitHub API — Zscaler blocks POST git-receive-pack.
Built with the Git Data API rather than createCommitOnBranch, because the
latter always writes mode 100644 and this script must be 100755 like every
other script in scripts/.
#26's PUSH_OK guard answers "did the push succeed?". It cannot answer "did this
reach the branch that ships?" A worker branching from a feature line pushes
fine, merges fine, tests fine — and closes the issue while the shipping branch
is untouched. Every signal is green and the tracker is wrong. That is strictly
more dangerous than the #26 case, which at least leaves a visible push failure.

The issue's proposed fix does NOT work, and I verified it fails on its own
headline example. It proposed testing ancestry of $INTEGRATION_BRANCH. This
swarm's INTEGRATION_BRANCH is feat/autocoder-planning-pipeline — a feature line
— so a commit merged into it IS an ancestor of it, the check passes, and the
issue closes. That is exactly how #32 was closed while master still lacked the
fix. Ship is therefore measured against the branch that actually ships (repo
default branch, overridable via CLAUDE_CODE_SHIP_BRANCH).

Changes:
- Add plugins/autocoder/scripts/verify-shipped.sh: exit 0 shipped, 1 pushed but
  not on the ship branch (naming where it is parked), 2 unresolvable. An
  unresolvable commit is an error, never a pass — a typo must not read as
  "shipped".
- Gate the auto-merge close path in both dev.md mirrors. When not shipped:
  leave the issue OPEN, release the lock, add `awaiting-integration`, and
  comment naming exactly what must merge.
- Add tests/test_verify_shipped.py (9 assertions) building real throwaway git
  repos, since the property under test is git ancestry. One test pins the
  reasoning above so nobody "simplifies" the gate back to the integration
  branch.

Also reopened #32, which I closed on this exact false premise. Its fix is real
and merged to the integration line, but master still has the CI gap #32
describes. Relabelled awaiting-integration.

Verification: 121 pytest (112 + 9 new), 9/9 shell suites, drift clean.

Closes #35

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Published via the Git Data API — Zscaler blocks POST git-receive-pack.
The #35 ship gate measured "shipped" against master. Nothing this swarm
produces reaches master until the /dev line merges (#30), so every fixed issue
would have parked behind that one merge accumulating `awaiting-integration`.
Per manager + human decision, the swarm's shipping line is the integration
branch.

Making it stick for the fleet, not one shell:

- .envrc (tracked) exports CLAUDE_CODE_SHIP_BRANCH. This is the existing
  fleet-wide env mechanism — it already carries CLAUDE_CODE_TASK_LIST_ID.
- CLAUDE.md gains a Branches section declaring the ship branch, so humans and
  agents reading the project doc find it, with an explicit note to flip it to
  master when #30 lands.
- verify-shipped.sh resolves the ship branch from CLAUDE.md when no env var is
  set. The env var alone is NOT fleet-wide: headless workers (cron, tmux panes
  started without direnv) never source .envrc.

Resolution order: arg > CLAUDE_CODE_SHIP_BRANCH > CLAUDE.md > repo default > main.

Note the CLAUDE.md regex accepts arbitrary branch names. The existing extractor
in dev-loop.md matches only \b(main|master|develop|integration)\b and therefore
cannot express `feat/autocoder-planning-pipeline` at all — worth knowing before
anyone tries to reuse it for this.

Verification: 124 pytest (121 + 3 new), 9/9 shell suites, drift clean. Both
issues currently labelled awaiting-integration re-checked under the new ship
branch and confirmed SHIPPED.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Published via the Git Data API — Zscaler blocks POST git-receive-pack.
The rename is invisible to git: fix.md exists on both sides (2341 lines on
master, a 7-line alias stub on this branch) while the real command moved to
dev.md. Git reports plain modify/modify, so resolving it either way silently
loses one side's work — master's 308-line delta was never ported to dev.md.

3-way merge per mirror pair, base = fix.md @ 87e3e5e (the merge base):
  plugins/autocoder/commands/{fix->dev}.md        2 conflicts
  plugins/autocoder/commands/{fix-loop->dev-loop}.md  clean
  .agent/workflows/{fix->dev}.md                  6 conflicts
  .agent/workflows/{fix-loop->dev-loop}.md        9 conflicts

Resolutions:
- dev.md C1: took this branch. Master's claim-then-verify block is a verified
  duplicate of the arbitration already ported at dev.md:541-566 (40f52b7);
  master's copy also aborts via exit 1 without releasing the label it added.
- dev.md C2: kept BOTH. Master's local feature-branch cleanup (git branch -d)
  appeared nowhere on this branch and would have been dropped; it now precedes
  the #35 ship gate.
- .agent mirrors: took this branch throughout. Its arbitration is a superset —
  it adds the remote-branch lock (REMOTE_BRANCH_EXISTS) that master spells
  REMOTE_BRANCH_SPEC, same ls-remote semantics.

dev.md now carries master's DEFAULT_BRANCH (3), api-push fallback (3) and scope
gate (3) alongside this line's ship gate and arbitration.

Note: DEFAULT_BRANCH is absent from master's .agent/workflows/fix.md too — a
pre-existing mirror-parity gap on master, not a loss introduced here.
Second half of the port-first plan. Step 1 (0eb620d) moved master's fix.md
delta onto dev.md so the rename collision could be resolved without loss; this
commit merges master in and resolves the remaining 12 conflicts.

Resolutions, each decided on merits rather than a blanket ours/theirs:
- fix.md, fix-loop.md, .agent mirrors: took the 7-line alias stubs. Safe ONLY
  because step 1 ported their content to dev.md first.
- marketplace.json: higher version on each side (3.26.0 / 4.5.2).
- regression-test.sh: took the /dev line. It is a strict superset — it already
  carries master's #20 portable helpers (extract_first_number,
  extract_stat_or_zero) AND adds detect_skip_reason / is_node_based_command.
  Taking master here loses graceful degradation (wt-3 verified this).
- worker-launch-lib.sh: took MASTER. It adds the claude-worker-loop.sh
  architecture and model tiers; the /dev line would have regressed it.
- .agent/scripts/start-parallel-agents.sh: took the /dev line. Master's claude)
  case launches `gemini` — a real bug from wholesale Antigravity re-branding.
- gemini-autocoder.sh: master's hardened -p/-y flags with /dev naming.
- stop-hook.sh: master's .agent-correct state path, /dev wording.
- README.md: union of both changelogs, deduped; kept master's new Swarm section.

Also annotates 12 `working`-label release sites that are terminal aborts, so
tests/test_dev_working_lock.py can see their justification.

VERIFIED: 124/124 pytest; all 103 tracked shell files parse.
NOT VERIFIED: 4 shell suites fail (test_api_push, test_issue_fns,
test_start_issue_work, test_worker_launch_lib) and check-optional-skills-drift
reports DRIFT for approve-proposal, plan and modernize. dev/dev-loop are OK.
Those must be resolved before this lands on master.
…fixes (#30)

Merges current master into origin/port/dev-line-catchup (84d6ce6) and repairs
work the step-1 port dropped when it overwrote dev.md with master's fix.md.

Restored:
- #48 branch-evidence exclusion. 6ff01a8 dropped both the CLAIMED_BY_BRANCH
  filtering in dev.md and scripts/claimed-issue-numbers.sh outright. Restored
  on both mirrors; the script now exists in .agent/ too (it never did).
- #36 apply_label. Master's newest fix.md commit postdates the port, so the
  one-label-at-a-time reconciliation was never carried onto dev.md.
- #20 no-parseable-summary guard in regression-test.sh. The /dev copy collapsed
  an unparseable result to 0, so a broken suite read green.

regression-test.sh is a union, not a side: the /dev copy's portable sed parsing,
PIPESTATUS exit capture and detect_skip_reason, plus master's #20 guard and its
"don't assume npm" default (folded into detect_skip_reason so both suites share
one skip path). The .agent mirror is regenerated from it, so the two are now
identical modulo CLAUDE.md/GEMINI.md.

Also:
- Add/add conflicts resolved to master's side for the file mode: content was
  byte-identical but the port branch had 100644 on six executables.
- test.yml keeps master's separate shell job (git identity for fixture commits)
  driving the /dev line's scripts/run-shell-suites.sh, so CI and local runs
  share a path.
- test_api_push.sh points at dev.md; the push protocol moved there.
- The lock-release example in dev.md closed an issue without a publication
  gate, the exact pattern #26 forbids. Now gated on PUSH_OK.
- Optional-skills drift: a branding sweep had rewritten "Claude/Antigravity"
  to "Antigravity/Antigravity" in five .agent mirrors.

Verified: 124/124 pytest, 13/13 shell suites (direct and via run-shell-suites.sh),
drift checker exits 0, all 106 shell files parse.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…hangelog entry

.agent/workflows/dev.md:1080 calls "${SCRIPT_DIR}/verify-shipped.sh", but #35
added the script to plugins/ only. On the Antigravity platform the ship gate
therefore could not execute at all. Mirrored with the usual CLAUDE.md ->
GEMINI.md substitution; CLAUDE_CODE_SHIP_BRANCH keeps its name, matching the
rest of .agent/ where the env vars are shared across platforms.

README's v3.4.0 row claimed that release renamed /fix-github to /dev. That
rename produced /fix; /dev came much later, so the entry documented a name
that did not exist yet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@laird

laird commented Jul 28, 2026

Copy link
Copy Markdown
Owner Author

⚠️ This does not self-resolve when #77 lands — #77 introduces the problem to master. The fix belongs inside PR #77, before it merges.

The premise to revisit

The issue reasons that once #77 lands, the integration-branch designation becomes "actively wrong" and someone cleans it up afterwards. But PR #77's head branch still carries the stale designation, so merging it propagates that designation onto master rather than retiring it.

Verified on origin/merge/30-dev-line-v3 (PR #77's head):

CLAUDE.md:17  **Integration branch**: `feat/autocoder-planning-pipeline`
CLAUDE.md:19  **Ship branch**: `feat/autocoder-planning-pipeline`
CLAUDE.md:23  ...**When #30 lands, change this to `master`** in both this file and `.envrc`.
CLAUDE.md:25  Resolution order: arg → CLAUDE_CODE_SHIP_BRANCH → **Ship branch** line → repo default → main
.envrc:8      export CLAUDE_CODE_SHIP_BRANCH="feat/autocoder-planning-pipeline"

Why that inverts the current situation

verify-shipped.sh:52-58 reads the **Ship branch**: line from the CLAUDE.md in the working directory:

SHIP_BRANCH="${2:-${CLAUDE_CODE_SHIP_BRANCH:-}}"
if [ -z "$SHIP_BRANCH" ]; then
    for cfg in CLAUDE.md claude.md AGENTS.md; do
        SHIP_BRANCH=$(grep -iE '^\*\*Ship branch\*\*:' "$cfg" | head -1 | sed -E 's/.*`([^`]+)`.*/\1/')
master CLAUDE.md has the line? a master worktree resolves ship branch to
today no (verified: 0 matches) repo default → master ✅ correct
after #77 merges as-is yes (1 match, pointing at the integration branch) feat/autocoder-planning-pipeline

So today the master line is accidentally correct because the line is absent. #77 removes that accident. Afterwards every worktree — including master ones — resolves shipping against a branch that has just been merged away and stops advancing, so work landing on master reports NOT_SHIPPED and stays open with awaiting-integration.

That is the same failure the author hit and described, except promoted from "stale guidance in leftover worktrees" to the default for everyone.

.envrc already exists on master, so line 8 lands too and pins the env var fleet-wide for anyone using direnv.

Recommendation

Fold the three edits into PR #77 before merging, rather than filing them as follow-up:

Doing it in #77 means the tree is never in a state where the designation is wrong. Doing it after means a window in which every agent's close gate misfires.

Not editing PR #77's branch — it belongs to another agent and this is their call. Flagging it here and on #77 while the PR is still open (currently MERGEABLE/CLEAN).

🤖 Autonomous fix workflow (agents-wt-3)

@laird

laird commented Jul 28, 2026

Copy link
Copy Markdown
Owner Author

Reviewed this merge — it is clean, with one caveat worth knowing before you land it

Validation, run against this PR's head (8b2c590) in a scratch worktree:

  • Build verification: BUILD_OK
  • Shell suites: 13/13 pass
  • File loss check: exactly one path on master is absent here — docs/fix-loop-multi-agent-coordination.md — and that is the legitimate rename to docs/dev-loop-multi-agent-coordination.md (same content, command name updated). No accidental deletions, which was my main worry for an 11-conflict merge.

Caveat: this reverts four regression-runner fixes that #78 makes to master

This PR replaces plugins/autocoder/scripts/regression-test.sh wholesale (447 lines changed) with the /dev-line version. That version is better in one way and worse in four.

Better: it has set -o pipefail, so it can actually go red — that is the worst defect on current master (see #78: cmd | tee returns tee's status, so every failing suite reported PASSED).

Worse — still present in this PR's version:

defect this PR
REPORT_DIR takes the first Location: line (a glob) present
unit-test command read from a ### Unit Tests Only header no CLAUDE.md supplies present
runner leaks its own ISSUE_SOURCE into the suite present
all suites skipped still exits 0 with "All tests passed" present

Its CLAUDE.md also has no ### Unit Tests Only block.

Run against this PR's head, unmodified:

Unit Tests Complete: ⏭️ SKIPPED (0/0 passed)
E2E Tests Complete:  ⏭️ SKIPPED (0/0 passed)
Report saved to: tests/test_*.sh/regression-2026-07-28_16-49-50.md
✅ All tests passed!
RC=0

So after this lands, the runner still executes zero tests, still reports green, and still creates the glob-named tests/test_*.sh/ directory that then poisons tests/test_*.sh expansions.

Recommended order

Land this PR first, then re-apply #78's four remaining fixes on top of the /dev-line version. Rebasing #78 the other way round would just be overwritten by this merge.

I did not pre-build that follow-up branch: it conflicts in both regression-test.sh copies, and resolving against a PR head that may still change would be speculative. Happy to produce it the moment this merges — the content already exists as the (now closed) #75, which fixed exactly this version of the file.

No action needed on this PR itself; the merge looks sound.

laird added a commit that referenced this pull request Jul 29, 2026
The combined tree (#77 + #74 + #78) exposed a defect neither PR's CI could
see. #77's parser recognises only the Jest "Tests: N passed" form, and #77
alone never exercises it — the unit suite always skipped, so the parser was
dead code. #78 makes the suite actually run, and this repo's own shell tests
print "Results: 13 passed, 0 failed" with no "Tests:" prefix. A fully green
run was therefore reported "no parseable test summary" and exited 1.

That is a false RED — the mirror image of the false GREEN #73 is about, and
just as damaging: a gate that cannot be trusted in either direction.

The fix (in the merge commit) adds a bare "<N> passed" fallback, mirroring
the two-tier approach #77's own E2E block already uses. These assertions pin
both directions so neither can regress:

  - a non-Jest "Results: N passed, M failed" summary is accepted (exit 0,
    not reported as unparseable)
  - output with no counts at all is STILL an error — the fallback widens the
    accepted formats without defeating #77's guard against a runner that
    never produced a summary

Verification on the combined tree:
  - tests/test_regression_test_config.sh: 16/16
  - all tests/test_*.sh: 15/15
  - pytest: 124/124
  - detect_skip_reason present 3x (definition + Unit Tests + E2E guards)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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