Skip to content

Narrow the cloud implement completion gate to a --completion-gate mode on the parallel coordinator #2136

Description

@The01Geek

Problem Statement

As a maintainer of this repository, I want the cloud implement run's completion gate to run a small set of checks instead of the whole test suite, so that runs finish sooner and use less of the shared GitHub Actions capacity.

Today every cloud /prflow:implement run must produce a whole-suite result before it is allowed to write Status: Complete. The whole-suite command is the parallel coordinator lib/test/run-parallel.sh, which runs the tested partition of five CI shards inside one checkout. That single command dominates the run, and its cloud elapsed time sits close to the tier's per-command execution ceiling, so a run that trips the ceiling has to decompose the suite into shards and recombine them. The cost gap is re-derivable in this repository by timing the coordinator against the eight commands named below.

The gate also buys little. The catch rate behind this change was measured during drafting rather than read from a tracked artifact: over the cloud implement runs of the two weeks ending 2026-08-28, read from their GitHub Actions job logs with the maintainer's local implement-timeline reader, the in-run whole-suite gate caught nothing that the same commit's CI did not also catch. That measurement is recorded here as a drafting-time observation and is not re-derivable from this repository. CI remains the required check that gates the human merge, so the expensive in-run pass is mostly re-proving what CI proves again minutes later.

Current Behavior

The implement prompt extension makes Phase 4.3 responsible for one whole-suite result per run.

  • Verified: .prflow/prompt-extensions/implement.md states "Phase 4.3 owns this run's whole-suite obligation, exactly once".

The shipped skill body does not hardcode which command that is. It reads the scope from this repository's own prompt extension and falls back to the full suite when the extension sets no scope.

  • Verified: skills/implement/phases/phase-4-documentation.md says the run establishes its evidence "or the full whole-suite command when it sets none".

That fallback is the seam this change uses. It also means a change that only adds a pointer to the extension, without an operative scope statement, would silently keep the old behavior.

Desired Behavior

lib/test/run-parallel.sh gains a second mode, --completion-gate. The mode runs eight checks in a fixed order inside the current checkout, stops at the first one that fails, retains a log, prints the path of that log, reports each check and its status on its own output, and exits with a single status. A cloud implement run discharges its Phase 4.3 obligation by running that one command.

The eight checks are the coordinator's own read-only pre-launch checks, the internal-documentation long-line lint, the pin-corpus classifier suite, the four parts of the Python script suite, and the module that owns the coordinator's own behavior.

Because the whole gate is one command with one exit status, the run records one honest suite_summary.command, and the single flight coordinates one command, exactly as it does today for the coordinator.

The local and interactive tier is untouched. It continues to discharge its completion gate by reading CI for the commit it pushed.

User Impact

The maintainer of this repository benefits, and so do the cloud implement runs this repository starts. Runs finish sooner and hold an Actions concurrency slot for less time. Nobody else changes how they work: nothing in this change reaches a consumer repository, because lib/test/ is pruned from the vendor slice and neither CLAUDE.md nor the prompt extension is installed into a consumer checkout.

The trade is stated plainly, in terms of what stops running. The narrowed gate does not run four of the five tested CI shards — monolith, modules-pin, modules-large and modules-rest — nor lib/test/test_module_runner.py from python-pool. It runs only the coordinator's read-only pre-launch checks, lib/test/lint-internal-docs.py, lib/test/test_pin_corpus_classifier.py, the four test_python_scripts parts, and the parallel-suite-runner module. In particular the whole of lib/test/run.sh, and every lint that file alone drives, moves out of the in-run gate. A defect that only the dropped population catches now surfaces on CI after the pull request is marked ready, instead of inside the run. That is the accepted cost of the change.

Technical Context

Scope note: The files and details below are the known starting points, not the full list. Before implementing, trace the change through the codebase to find every affected call site, consumer, and layer — this issue maps the work, it does not bound it.

This builds on #2123, merged as pull request #2124, which added the three tool grants the narrowed recipe needs. All eight commands are already present in prflow_implement.allowed_tools on the default branch, so this change ships no grant and no acceptance criterion here depends on trigger-time-resolved configuration. The new mode needs no grant of its own: the existing entry is a prefix grant over lib/test/run-parallel.sh, so it already covers the script invoked with a new flag, and the invocation keeps the bare-leading-token shape the cloud matcher requires.

  • Relevant Classes/Fileslib/test/run-parallel.sh holds the existing --preflight mode and the two check helpers the new mode reuses. lib/test/modules/parallel-suite-runner.sh is the module that owns this script's behavior, and it is one of the eight checks. .prflow/prompt-extensions/implement.md sets the Phase 4.3 scope. CLAUDE.md describes the tier ladder and the record a completion gate writes.
  • Architecture Alignment — the argument case block that already carries the bare invocation and --preflight gains a third arm, and the mode it selects reuses _artifact_preflight and _cheap_lint_preflight rather than re-invoking the script.
  • Dependencies — none beyond what lib/test/run-parallel.sh already uses.
  • Data/Schema Considerations — the completion-evidence record format is unchanged, and the command it names changes. The fields a Verification evidence: marker carries for this path change with it, because the mode runs no shard and so reports no aggregate line and no shard tally.
  • Cross-layer Impact — one test-harness script, its owning test module, one repo-local prompt extension, CLAUDE.md, and the internal documentation pages that describe the gate.

Three facts the design rests on:

  • Verified: the completion-evidence validator checks only that the recorded command is a nonempty string — scripts/check-completion-evidence.py rejects a record only with the message "suite_summary.command is missing or not a nonempty string", so narrowing the gate needs no validator change.
  • Verified: the fourth part of the Python script suite already runs the install-state checker, which is why lib/generate-install-state.py is absent from the recipe — lib/test/test_python_scripts_part4.py introduces that assertion with the comment "Drift gate: the generator's --check must pass".
  • Verified: the pooled Python shard is a more expensive route to the same four parts, because lib/test/run-python-pool.sh describes its membership as "test_module_runner.py and the four parts test_python_scripts.py".

One ordering constraint the design rests on: the helper that makes a run root for the retained log is defined below the argument block, and between the two sit the reentrancy guard, the shard-dispatcher readability check, the shard-population derivation, and the coordinator's own pre-launch invocation. The mode therefore dispatches from a new point this change creates, immediately after the run-root layout exists, with those four steps placed behind a guard that skips them in this mode, so the mode neither aborts on a shard dispatcher it does not use nor runs its own pre-launch checks twice.

  • Verified: lib/test/run-parallel.sh defines the run-root helper with the comment "parent -> prints a fresh writable run root", and that definition sits below the argument-handling block that the existing read-only mode exits from.

One asymmetry the failure arms must respect:

  • Verified: a zero exit from the pre-launch checks does not prove they ran — lib/test/run-parallel.sh documents that the mode "exits 0 to proceed (clean, or a fail-open inconclusive result)" and non-zero on an attributed finding.

Acceptance Criteria

Every enumeration below that names the recipe's checks is the same closed set: lib/test/run-parallel.sh --preflight's own read-only pre-launch checks, lib/test/lint-internal-docs.py, lib/test/test_pin_corpus_classifier.py, lib/test/test_python_scripts.py, lib/test/test_python_scripts_part2.py, lib/test/test_python_scripts_part3.py, lib/test/test_python_scripts_part4.py, and lib/test/run-module.sh parallel-suite-runner — exactly these eight, complete by construction.

  • Running lib/test/run-parallel.sh --completion-gate on a checkout where all eight checks pass exits with status 0 and launches no suite shard.
  • Running lib/test/run-parallel.sh --completion-gate on a checkout where one of the eight checks fails exits non-zero, names the failing check on the command's own output, and does not run the checks ordered after it.
  • On a passing run, lib/test/run-parallel.sh --completion-gate prints one line per check naming that check and its status, and one final line carrying the mode's own elapsed wall-clock seconds, so a run composes its Verification evidence: marker from the mode's own output.
  • lib/test/run-parallel.sh --completion-gate exits non-zero and names the disabled check when any of the eight checks was disabled by an empty environment override, so a gate run never reports success over fewer than eight checks.
  • lib/test/run-parallel.sh --completion-gate writes the retained log of the checks it ran and prints that log's absolute path on its own output, so a run can record it as the retained-log root in its Verification evidence: marker.
  • On a checkout where no writable run root can be allocated, lib/test/run-parallel.sh --completion-gate still runs all eight checks, states on its own output that no log was retained, and exits with the checks' own combined status rather than the run-root allocation failure.
  • lib/test/run-parallel.sh --completion-gate treats a zero exit from the read-only pre-launch checks as permission to continue, matching the fail-open posture the coordinator's existing pre-launch check already applies.
  • Running lib/test/run-parallel.sh with --completion-gate twice in a row on an unchanged checkout produces the same exit status both times.
  • .prflow/prompt-extensions/implement.md states that a cloud implement run's Phase 4.3 whole-suite obligation is discharged by lib/test/run-parallel.sh --completion-gate, naming that command.
  • After the change, .prflow/prompt-extensions/implement.md is the only file stating which command discharges the cloud implement tier's Phase 4.3 gate; CLAUDE.md and each page under docs/internal/ that describes the gate reach it by pointer, and the <!-- canonical: docs/internal/implement-skill.md --> marker on CLAUDE.md's cloud-tier ladder entry is removed, that page having ceased to be the canonical home of this rule.
  • After the change, no bullet in CLAUDE.md's The whole-suite gate section states that the cloud implement tier's completion gate is discharged only by a lib/test/run-parallel.sh whole-suite coordinator result, and none states it is discharged only by a recombined complete shard partition; that section instead states that the cloud implement tier's Phase 4.3 gate command is set by .prflow/prompt-extensions/implement.md, and the issue-Single-source the flight scope that discharges the Phase 4.3 completion-evidence gate #1132 execution-ceiling decomposition bullet and the issue-An environment-only fix after a RED completion-gate pass re-pays the whole ~16-minute coordinator; permit a fingerprint-gated failed-shard-only relaunch #2008 same-tree relaunch bullet each state which obligation they still serve after the narrowing.
  • After the change, CLAUDE.md's record-contents bullet states which fields a Verification evidence: marker carries for a gate discharged by lib/test/run-parallel.sh --completion-gate, and no longer requires any of a coordinator aggregate line, a shard tally, and a coordinator elapsed line on that path.
  • After the change, prflow_implement.allowed_tools in .prflow/config.json contains the same entries in the same order as before the change.
  • After the change, lib/capability-profiles.json, lib/review-profile.tokens, and the manifest_version value are byte-identical to their state before the change.
  • After the change, scripts/check-completion-evidence.py, scripts/verification-flight.py, and scripts/workpad.py are byte-identical to their state before the change.
  • After the change, lib/test/run.sh contains no occurrence of the string pin-corpus-classifier.py, so lib/test/modules/harness-python-guards.sh's assertion #798 pin-corpus classifier remains maintainer-run (no run.sh invocation) continues to pass.
  • After the change, CLAUDE.md still states that the local and interactive tier discharges a whole-suite completion gate by reading CI for the commit the run pushed.
  • No page under docs/internal/ states that the cloud implement tier's Phase 4.3 gate is discharged by the whole-suite coordinator.

Implementation Notes

  • Approach — add a --completion-gate arm to the argument case block in lib/test/run-parallel.sh, and split it in two. The arm in that block records the requested mode and passes the argument-count guard; the mode itself runs from a new dispatch point this change creates, immediately after the run-root layout exists. Between the argument block and that point sit four steps the mode does not use — the reentrancy guard, the shard-dispatcher readability check, the shard-population derivation, and the coordinator's own pre-launch invocation — so this change places all four behind a guard that skips them in this mode. The mode then calls the two pre-launch helpers itself, runs the remaining checks in order, short-circuits on the first non-zero status, names the check that failed, and reports each check and the elapsed seconds on its own output. Each check is invoked through an environment seam with a bundled default, the same idiom the script already uses for its preflight helpers, so the module can drive the mode's arms from a synthetic tree without running the real suites; because that seam also makes a check disable-able, the mode refuses a run in which any seam resolved empty. Update the argument guard's message and the help block, which both enumerate the accepted arguments. Then replace the scope sentence in the implement prompt extension with one that names the new command, and reduce the matching statements in CLAUDE.md to pointers at that extension.

  • Relevant files — this touches lib/test/run-parallel.sh (the argument case block, the help-start block, the argument-count guard, a new runner function beside _cheap_lint_preflight, the guard around the four skipped steps, and the new dispatch point after the run-root layout), lib/test/modules/parallel-suite-runner.sh (new assertions beside the existing #1288 preflight block), .prflow/prompt-extensions/implement.md, and CLAUDE.md (the tier-2 ladder entry, the whole-suite-gate section, and the record-contents bullet). It plausibly also touches docs/internal/implement-skill.md, docs/internal/claude-md-tiered-suite-rationale.md, and docs/internal/DEVFLOW_SYSTEM_OVERVIEW.md.

  • Code Patterns — mirror _cheap_lint_run in lib/test/run-parallel.sh: a labelled helper that takes a command string from an environment variable with a bundled default, uses - rather than :- so an explicitly empty override disables that step, and short-circuits so the step that fired is the one reported. Decide whether each step passed with bash builtins, in the while IFS= read -r and case shape that file already uses, rather than through a tool the preflight does not guarantee, because a missing tr command would fail open and let a failing check read as a pass. Keep the new code free of flags that only GNU tools accept, since the runners this repository uses include a Windows host. Mirror the existing #1288 preflight assertions in lib/test/modules/parallel-suite-runner.sh for the test shape, including its use of a synthetic tree.

  • Testing Strategy — the tests live in lib/test/modules/parallel-suite-runner.sh, driven through the module runner, and they use the environment seams so the tests do not run the real Python suites. That module is itself one of the eight checks, so the gate this change installs runs its own covering tests. Write them before the mode exists, and confirm each fails against today's script, which has no --completion-gate arm at all.

    The cases, beyond what the criteria already pin: a run where a check is made to exit 2 rather than 1, asserting the mode still treats it as a failure, which covers the risk that the arm keys on a single expected status; a run where the pre-launch checks return zero from a fail-open inconclusive result, asserting the mode continues, which protects the documented fail-open posture; a run on a tree whose shard dispatcher is unreadable, asserting the mode still completes, which protects the guard around the four skipped steps; and an invocation passing --completion-gate together with a second argument, asserting the existing argument-count guard still refuses it, which protects the message the module already pins.

    The guarantee-class residual case: the mode is the deterministic replacement for a recipe an agent would otherwise run by hand, so one case asserts that invoking the mode runs every one of the eight checks even when an earlier phase of the run already ran some of them by hand, rather than skipping work it believes is done.

    governing conventions consulted: CLAUDE.md, CONTRIBUTING.md, docs/internal/.

  • Documentation Neededdocs/internal/implement-skill.md, docs/internal/claude-md-tiered-suite-rationale.md, and docs/internal/DEVFLOW_SYSTEM_OVERVIEW.md each describe the cloud implement tier's completion gate and reach the new command by pointer at the implement prompt extension.

  • Potential Gotchas — one nearby page is deliberately not a deliverable: docs/internal/architecture/execution-model.md needs no change, because its statement is about focused tests rather than about which command discharges the gate. This change ships no changeset. Nothing in it reaches a consumer repository: lib/test/ is pruned from the vendor slice, and both CLAUDE.md and .prflow/prompt-extensions/ are repo-local. The CLAUDE.md edits are made directly by the implementing run under the standing carve-out for a required CLAUDE.md edit, recorded in the workpad, rather than by invoking an interactive revision skill mid-run. The mode must be dispatched after the run-root layout exists, so an implementer who puts the whole mode inside the argument block will find no run root and no retained log. The argument-count guard's message and the help-start block both enumerate the accepted arguments and must be updated together; lib/test/modules/parallel-suite-runner.sh pins only the substring at most one argument, so it will not catch a stale enumeration, and this change adds an assertion that the rendered --help output and the guard message both name --completion-gate. The mode dispatches before the coordinator records its checkout fingerprint, so a --completion-gate run root carries no fingerprint.json, and the issue-An environment-only fix after a RED completion-gate pass re-pays the whole ~16-minute coordinator; permit a fingerprint-gated failed-shard-only relaunch #2008 same-tree failed-shard-only relaunch route reads none from it; that route continues to serve coordinator runs only, and this change states so where issue An environment-only fix after a RED completion-gate pass re-pays the whole ~16-minute coordinator; permit a fingerprint-gated failed-shard-only relaunch #2008 is documented. The #798 assertion counts the string pin-corpus-classifier.py across the whole of lib/test/run.sh, and its subject is the census instrument rather than the focused test the gate runs, so adding lib/test/test_pin_corpus_classifier.py to the eight checks leaves it green. Finally, the prompt extension is read live from the working tree during a run, so an implementing run that lands this change will itself read the new rule at its own Phase 4.3.

Generated via /prflow:create-issue (v2.36.15, claude-opus-5, medium)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions