mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-01 06:24:41 +01:00
v1.91.256.0 feat(ship): review loop converges (frozen scope, 3-pass cap, NO FINDINGS) and receipts carry across conflict-only merges (#3481)
* feat(ship): review loop converges (frozen finding scope, 3-pass cap, NO FINDINGS first-class) and receipts carry across conflict-only merges
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test(ship): keep the Step 9 first-entry and Fix-First phrasing the skip/requeue contract asserts; fill the review record's pass, review_scope and range_base placeholders in the caller template test; narrow a test type
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix: pre-landing review fixes (pass 1) and the review-loop budget wording
Two blocking findings from /ship's own pass 1, both reproduced in the QA smoke before the fix:
gstack-ship-receipt carry hashed the product diff with git patch-id --stable, which strips
whitespace, so a space moved in a reviewed shell line carried as "no product change"; and it
classed package.json and every lockfile as release metadata, so a dependency swap carried too.
The patch-id is now --verbatim, package.json and VERSION are metadata only while the change is a
version stamp (lib/pregate/stamp-only.ts), lockfiles are product unless the policy names them,
the test globs name code extensions, a dirty working tree refuses before anything is compared,
and a receipt carried twice keeps the reviewed head as carry.from.
gstack-review-scope: an unresolvable base is POLICY_SOURCE_UNAVAILABLE instead of a silent
working-tree read; --range-base is validated; git runs with core.quotePath=false; range_base is
null on pass 1; a blocking finding with no path is fix; category "testing" is a test finding
below the floor; defer searches once, files nothing on a failed search and exits 0 unfiled;
summary refuses zero matching passes and the receipt write is chained to it with &&; a finding
the diff causes elsewhere names the changed line in `cause` and classify follows it.
Review-loop budget wording (ideas from tstack's sol-loop and pstack's interrogate, rewritten;
NOTICE.md): the blocker / should / nit glossary and two stop rules in Step 9.4, the checklist
and docs/review-policy.md; a lead-judgment stage in the finding merge both /review and /ship
render. The core read diffs $RANGE_BASE from pass 2 on. Caps and pins re-measured with reasons.
Step 7 coverage audit: four tests added in existing files, the --review-loop case moved to its
own test (the shared test tripped bun's 5 s default).
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix: pin the renumbered review-army stages and point the wording test at product code
CI at c3e27fc failed three free-tier tests, all this branch's own: the
workflow-required-steps fixture and shared-libs-rendering still pinned the
pre-Lead-judgment stage numbers, and test-of-test-ratchet counted
review-loop-budget-wording.test.ts as test-only code because it read only
generated markdown. The fixture and rendering test now pin stages 1-8, and the
wording test renders both armies through generateReviewArmy and reads the
authored ship template as well as the generated sections.
Pass-2 maintainability notes folded in: docs/troubleshooting.md names the two
new RECEIPT_CARRY_REFUSED causes (dirty tree, unreadable status) and the two new
REVIEW_SCOPE_INPUT causes (non-hex --range-base, summary with no matching
pass); gstack-review-scope's help states that classify reads the base branch's
policy and refuses an unresolvable base unless --policy is passed.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix: date a review-log row from its binding start when it has no timestamp
The Step 9.4 template asks the writer to fill `timestamp`; the logger stamps
`review_binding.started_at` but never `timestamp`. `gstack-review-scope
summary` dropped such a row silently, so this PR's own pass 2 was missing from
REVIEW_LOOP (passes=1 for a two-pass loop). A row now dates from `timestamp`,
else from its binding start; a row with neither is still not counted.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix: pass-4 review fixes (carry stamp rule, deleted-file locus, scanned defer, diff prefixes, row time)
Five findings at the blocking floor from the Step 11 outside voice (gpt-6.1-sol)
and the native adversarial pass, fixed as a fourth pass approved by GStack
Minder under the three-pass policy:
- carry: a path the policy names under stamp_files, stamp_paths or
release_outputs is metadata only while its change is a stamp (versionStampOnly
for a manifest kind, stampOnlyText otherwise), the rule the tree receipt
applies; a logic change in a stamped source file is product. Every
metadata-listed path is read at both heads and a non-stamp change drops its
globs from the product exclusion.
- review-scope: parseHunks keeps a deleted file as a whole changed location, so
a cause anchored in a removed export is the diff's to fix, not deferred.
- defer: issues are filed through lib/gstack-post's publication scan; a HIGH
finding in the title or body refuses and a MEDIUM one needs a human, both
recorded as unfiled with the reason; --repo names the destination.
- classify: both diffs pin --src-prefix=a/ --dst-prefix=b/ --no-textconv, so
diff.mnemonicPrefix cannot send every finding outside.
- summary: a row dates from review_binding.started_at before the writer-typed
timestamp, so a date-only value cannot drop a real pass.
Docs: ship-policy.md#receipt-carry, review-policy.md, the Step 9.4 template
and the CHANGELOG gate-integrity note record the rule and the pass.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test(review-army): assert the Red Team branch the run took in the N+1 case
Under the blocking floor the Performance specialist rates a 12-line N+1
INFORMATIONAL, so the conditional Red Team does not activate; the case
encoded the old severity (N+1 is CRITICAL, a Red Team always follows). It
now asserts the branch the run took: a Red Team that ran must follow the
Performance specialist; no Red Team is correct only when the report's own
count says 0 critical and its Red Team line names that reason. The N+1
detection assertion is unchanged. The free contract test gains the
informational-not-activated and informational-claimed controls and the
budget test's synthetic report carries the count line.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* v1.91.256.0 stamp
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
b004c06dbc
commit
576839cb8c
48 files changed
+2638
-186
No files matched your search
@@ -131,7 +131,7 @@ Dispatch via the Agent tool with ${FOREGROUND_IF_AVAILABLE} (background is the d
|
||||
Subagent prompt:
|
||||
"${DEFENSIVE_REVIEW_FRAMING}
|
||||
|
||||
Read the diff for this branch. First list changed files: \`DIFF_BASE=$(git merge-base origin/<base> HEAD) && git diff --name-status "$DIFF_BASE"\`. ${FIXTURE_SUMMARY_MODE}
|
||||
Read the diff for this branch. First list changed files: \`DIFF_BASE=$(git merge-base origin/<base> HEAD) && git diff --name-status "$DIFF_BASE"\`. ${FIXTURE_SUMMARY_MODE}${isShip ? ' From Step 9\'s pass 2 on, \`DIFF_BASE=$RANGE_BASE\` applies here too; \`NO FINDINGS\` is the expected result for a clean range.' : ''}
|
||||
|
||||
Think like an attacker and a chaos engineer. Your job is to find ways this code will fail in production. Look for: edge cases, race conditions, security holes, resource leaks, failure modes, silent data corruption, logic errors that produce wrong results silently, error handling that swallows failures, and trust boundary violations. No compliments — just the problems. For each finding, classify as FIXABLE (you know how to fix it) or INVESTIGATE (needs human judgment). After listing findings, end your output with ONE line in the canonical format \`Recommendation: <action> because <one-line reason naming the most exploitable finding>\` — examples: \`Recommendation: Fix the unbounded retry at queue.ts:78 because it'll DoS the worker pool under sustained 429s\` or \`Recommendation: Ship as-is because the strongest finding is a theoretical race that requires conditions we can't trigger in production\`. The reason must point to a specific finding (or no-fix rationale). Generic reasons like 'because it's safer' do not qualify."
|
||||
|
||||
@@ -303,7 +303,9 @@ in order before leaving Step 11:
|
||||
new attempt separately, then reconsider these decisions. Without that correction,
|
||||
or if the recovery fails, ask for repair and remain blocked.
|
||||
2. **Fixes queued after native completion:** Keep the findings and their approvals.
|
||||
Insert Steps 9, 10 and 11 before the pending Step 11.5 in the work list.
|
||||
Under \`review_scope frozen\`, scope the queue first as Step 9.4 item 0 does; when
|
||||
nothing is \`scope: fix\`, run item 3.5 (defer and render) and take decision 3.
|
||||
Otherwise insert Steps 9, 10 and 11 before the pending Step 11.5 in the work list.
|
||||
Step 9 completes full review before fixes; any further repair inserts its checks
|
||||
ahead of the remaining items. These fresh reviews after code edits are not recovery retries.
|
||||
Returning here never resets Step 9's three-cycle fix limit.
|
||||
|
||||
@@ -57,7 +57,7 @@ ${ctx.paths.binDir}/gstack-specialist-stats 2>/dev/null || true
|
||||
|
||||
### Select specialists
|
||||
|
||||
Based on the scope signals above, select which specialists to dispatch.
|
||||
Select specialists from the scope signals above.
|
||||
|
||||
**Always-on (dispatch on every review with 50+ changed lines):**
|
||||
1. **Testing** — read \`${ctx.paths.skillRoot}/review/specialists/testing.md\`
|
||||
@@ -78,13 +78,13 @@ Based on the scope signals above, select which specialists to dispatch.
|
||||
|
||||
After scope-based selection, apply adaptive gating based on specialist hit rates:
|
||||
|
||||
For each conditional specialist that passed scope gating, check the \`gstack-specialist-stats\` output above:
|
||||
For each conditional specialist that passed scope gating, read the \`gstack-specialist-stats\` output:
|
||||
- If tagged \`[GATE_CANDIDATE]\` (0 findings in 10+ dispatches): skip it. Print: "[specialist] auto-gated (0 findings in N reviews)."
|
||||
- If tagged \`[NEVER_GATE]\`: always dispatch regardless of hit rate.
|
||||
|
||||
**Force flags:** \`--<specialist>\` (e.g. \`--simplification\`) or \`--all-specialists\` in the prompt force-includes it.
|
||||
|
||||
Note which specialists were selected, gated, and skipped. Print the selection:
|
||||
Print the selection:
|
||||
"Dispatching N specialists: [names]. Skipped: [names] (scope not detected). Gated: [names] (0 findings in N+ reviews)."`;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ so they run in parallel. Each subagent has fresh context — no prior review bia
|
||||
|
||||
**Each specialist subagent prompt:**
|
||||
|
||||
Construct the prompt for each specialist. The prompt includes:
|
||||
Each specialist prompt includes:
|
||||
|
||||
1. The specialist's checklist path from the selection above (the subagent reads it; never paste its content)
|
||||
2. Stack context: "This is a {STACK} project."
|
||||
@@ -120,20 +120,21 @@ Every other specialist, including Testing, reads the full diff with fixtures.
|
||||
"You are a specialist code reviewer. Read the checklist at {checklist path}, then run
|
||||
\`DIFF_BASE=$(git merge-base origin/<base> HEAD) && git diff "$DIFF_BASE"\` to get the full diff. Apply the checklist against the diff.
|
||||
|
||||
For each finding, output a JSON object on its own line:
|
||||
\`NO FINDINGS\` alone is the expected, complete result for a clean diff; never invent an item to show work. Otherwise output one JSON object per finding, on its own line:
|
||||
{\\"severity\\":\\"CRITICAL|INFORMATIONAL\\",\\"confidence\\":N,\\"path\\":\\"file\\",\\"line\\":N,\\"category\\":\\"category\\",\\"summary\\":\\"description\\",\\"fix\\":\\"recommended fix\\",\\"fingerprint\\":\\"path:line:category\\",\\"specialist\\":\\"name\\"}
|
||||
|
||||
Required fields: severity, confidence, path, category, summary, specialist.
|
||||
Optional: line, fix, fingerprint, evidence, test_stub, advisory, evidence_paths, helper_target.
|
||||
Optional: line, fix, fingerprint, evidence, test_stub, advisory, evidence_paths, helper_target, cause.
|
||||
CRITICAL means a test that would fail, a production break, or a security or data-loss defect; everything else is INFORMATIONAL.
|
||||
A defect the diff causes but that shows elsewhere (a caller broken by a changed signature, a consumer of a removed export) keeps the victim as \`path\` and names the changed line in \`cause\`: {\\"cause\\":{\\"path\\":\\"file\\",\\"line\\":N}}. Without it the finding reads as pre-existing code.
|
||||
|
||||
Optional extraction advice belongs to the core shared-code check; do not duplicate its proposals. Report real defects in duplicated code independently. Preserve advisory metadata when returning structural advice, and never label a demonstrated defect advisory merely because sharing a helper could fix it.
|
||||
Leave extraction advice to the core shared-code check; report real defects in duplicated code independently, keep advisory metadata on structural advice, and never call a demonstrated defect advisory because a shared helper could fix it.
|
||||
|
||||
If you can write a test that would catch this issue, include it in the \`test_stub\` field.
|
||||
Use the detected test framework ({TEST_FW}). Write a minimal skeleton — describe/it/test
|
||||
blocks with clear intent. Skip test_stub for architectural or design-only findings.
|
||||
|
||||
If no findings: output \`NO FINDINGS\` and nothing else.
|
||||
Do not output anything else — no preamble, no summary, no commentary.
|
||||
Output nothing else: no preamble, no summary or commentary.
|
||||
|
||||
Stack context: {STACK}
|
||||
Past learnings: {learnings or 'none'}"
|
||||
@@ -162,9 +163,9 @@ their source labels: specialist scoring is not the final review's defect count.
|
||||
#### 1. Parse outputs
|
||||
|
||||
After specialist attempts settle, collect their outputs, tagged by actual source.
|
||||
Successful \`NO FINDINGS\` is a completed empty result. Otherwise parse each JSON line and
|
||||
skip invalid lines. Missing or unusable output is incomplete coverage, not an
|
||||
empty success. Retain each specialist's returned findings for activity stats.
|
||||
\`NO FINDINGS\` is a completed, clean result, never a failed reviewer. Otherwise parse
|
||||
each JSON line and skip invalid lines. Missing or unusable output is incomplete
|
||||
coverage, not an empty success. Keep each specialist's returned findings for activity stats.
|
||||
|
||||
#### 2. Validate severity
|
||||
|
||||
@@ -190,12 +191,29 @@ Compute identities for both core and specialist findings:
|
||||
or \`{path}:{category}\` when no line exists.
|
||||
|
||||
Within the specialist list, merge matching identities in the same partition: keep
|
||||
the highest confidence and all source names. Confirmation by distinct specialists
|
||||
adds +1 (cap at 10) and \`MULTI-SPECIALIST CONFIRMED ({specialist1} + {specialist2})\`.
|
||||
the highest confidence and all source names. Confirmation by distinct specialists adds +1 (cap at 10) and \`MULTI-SPECIALIST CONFIRMED ({specialist1} + {specialist2})\`.
|
||||
Core findings never earn a specialist confidence boost. Preserve \`advisory\`,
|
||||
\`evidence_paths\` and \`helper_target\` through every merge.
|
||||
|
||||
#### 4. Apply specialist confidence gates
|
||||
#### 4. Lead judgment
|
||||
|
||||
Reviewers fill the space they are given. Before any merged finding counts, the
|
||||
parent reads each one and drops or downgrades it on these grounds, saying so:
|
||||
- **Nit gravity.** When a reviewer's whole return is style, naming and preference,
|
||||
the code is probably fine; write that down and count nothing from it as a defect.
|
||||
- **Hypothetical, not actual.** A defect names the call site or input that reaches
|
||||
it. One that does not is a note until someone traces it.
|
||||
- **Structure for a case that does not exist.** Advice to abstract, generalize or
|
||||
guard a path nothing takes is advisory, never a defect.
|
||||
- **A different design is not a defect.** "I would have done it differently" with
|
||||
no failing case behind it is dropped.
|
||||
- **Missing context.** A finding that assumes a rule the repository documents
|
||||
otherwise is dropped with the pointer.
|
||||
Agreement between reviewers that did not see each other's output is the strongest
|
||||
signal; the \`MULTI-SPECIALIST CONFIRMED\` boost above records it. A reviewer that
|
||||
returned \`NO FINDINGS\` was right until a traced defect says otherwise.
|
||||
|
||||
#### 5. Apply specialist confidence gates
|
||||
|
||||
- Confidence 7+: show normally in the findings output
|
||||
- Confidence 5-6: show with caveat "Medium confidence — verify this is actually an issue"
|
||||
@@ -204,14 +222,14 @@ Core findings never earn a specialist confidence boost. Preserve \`advisory\`,
|
||||
|
||||
Core findings keep the core Confidence Calibration gates.
|
||||
|
||||
#### 5. Score and present specialists
|
||||
#### 6. Score and present specialists
|
||||
|
||||
Only specialist findings enter this header and \`quality_score\`; core findings do not.
|
||||
Use the merged NON-advisory specialist findings for both counts and score;
|
||||
the header's N is X + Y, so advisory findings never add to it:
|
||||
\`quality_score = max(0, 10 - (critical_count * 2 + informational_count * 0.5))\`
|
||||
Cap at 10 and retain for ${persistRef}. These are not final unresolved-defect totals.
|
||||
Print only this block: the stage 6 activity object and \`test_stub\` bodies are log and Fix-First data.
|
||||
Print only this block: the stage 7 activity object and \`test_stub\` bodies are log and Fix-First data.
|
||||
Validated \`"advisory": true\` findings from any source are excluded from score,
|
||||
header, unresolved-defect totals and clean-status blockers. Show them separately;
|
||||
they remain ASK-only, never auto-applied. Real defects follow normal Fix-First.
|
||||
@@ -236,9 +254,9 @@ PR Quality Score: X/10
|
||||
\`Simplification: lean already — nothing to cut.\`
|
||||
- If it was not dispatched, print neither line.
|
||||
|
||||
Do not add core shared-code savings to this specialist footer. Explain any overlap once in the core proposal instead of presenting duplicate savings.
|
||||
Core shared-code savings never enter this footer; explain any overlap once in the core proposal.
|
||||
|
||||
#### 6. Save specialist activity
|
||||
#### 7. Save specialist activity
|
||||
|
||||
Compile a \`specialists\` object for ${persistRef}.
|
||||
${isShip ? 'For each specialist' : 'For DIFF_LINES < 50, keep `specialists: {}`; do not manufacture per-specialist scope records. Otherwise record each considered specialist'} (testing, maintainability, security, performance, data-migration, api-contract, design, simplification, state-machines, red-team):
|
||||
@@ -252,7 +270,7 @@ Advisory findings COUNT in the stats \`findings\` field, not its defect counts.
|
||||
Include Design despite its different checklist. Preserve dispatch/failure status:
|
||||
zero returned findings from a failed attempt is not a clean review.
|
||||
|
||||
#### 7. Hand off to Fix-First
|
||||
#### 8. Hand off to Fix-First
|
||||
|
||||
Send these findings to ${fixFirstRef} alongside ${critPassRef}.
|
||||
Consolidate equivalent shared-code advice under the core proposal, retaining all
|
||||
@@ -280,12 +298,12 @@ The Red Team subagent receives:
|
||||
Prompt, after the defensive framing and fixture handling from item 4 of the specialist dispatch: "You are a red team reviewer. The code has already been reviewed by N specialists
|
||||
who found the following issues: {merged findings summary}. Your job is to find what they
|
||||
MISSED. Read the checklist at {red-team checklist path}, list changed files with \`DIFF_BASE=$(git merge-base origin/<base> HEAD) && git diff --name-status "$DIFF_BASE"\`, read them as that fixture handling says, and look for gaps.
|
||||
Output findings as JSON objects (same schema as the specialists). Focus on cross-cutting
|
||||
Output findings as JSON objects (same schema as the specialists), or \`NO FINDINGS\` alone, the expected result when they missed nothing. Focus on cross-cutting
|
||||
concerns, integration boundary issues, and failure modes that specialist checklists
|
||||
don't cover."
|
||||
|
||||
If the Red Team finds additional issues, tag them \`"specialist":"red-team"\`.
|
||||
Add them to the original specialist outputs and rerun stages 1–7 of Step ${stepMerge}
|
||||
Tag Red Team findings \`"specialist":"red-team"\`.
|
||||
Add them to the original specialist outputs and rerun stages 1–8 of Step ${stepMerge}
|
||||
before ${fixFirstRef}; do not boost or count the earlier findings twice.
|
||||
|
||||
If the Red Team returns NO FINDINGS, note: "Red Team review: no additional issues found."
|
||||
|
||||
Reference in new issue
Block a user