mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 22:14:25 +01:00
fix: pre-landing review fixes (targeted patch id covers tests, record binds the diff it read, role list pin)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
88cf2540cb
commit
db2b5b00ab
7 files changed
+60
-28
No files matched your search
@@ -17,7 +17,7 @@ import * as path from 'node:path';
|
||||
import { EXIT, exitTableHelp } from '../lib/headless-artifacts';
|
||||
import { BRIEF_ROLES, BriefError, briefFile, promptHash, renderBrief, sectionTexts, type BriefRole, type Slots } from '../lib/review-brief';
|
||||
import { collect, readJsonl, readLedger, readerKey, recordJudgment, recordSent, appendEvent, verify, type JudgedLine, type MergedLine, type VerifyRow } from '../lib/review-ledger';
|
||||
import { TARGETED_RULES, parseClassList, targetedBriefSha } from '../lib/review-targeted';
|
||||
import { TARGETED_RULES, parseClassList, targetedBriefSha, targetedPatchId } from '../lib/review-targeted';
|
||||
|
||||
const HELP = `gstack-review-dispatch — verbatim reviewer briefs and loss-free collection for /ship
|
||||
|
||||
@@ -82,13 +82,16 @@ function render(): number {
|
||||
fs.writeFileSync(file, text, { mode: 0o600 });
|
||||
const ledger = flag('--ledger') ?? path.join(out, 'review-ledger.jsonl');
|
||||
const sha = promptHash(text);
|
||||
const targeted = brief === undefined ? {} : { classes: parseClassList(flag('--classes')!), rules: TARGETED_RULES, brief_sha: targetedBriefSha(checklist) };
|
||||
const targeted = brief === undefined ? {} : { classes: parseClassList(flag('--classes')!), rules: TARGETED_RULES, brief_sha: targetedBriefSha(checklist), diff_base: diffBase ?? null, patch_id: diffBase ? targetedPatchId(process.cwd(), diffBase) : 'none' };
|
||||
appendEvent(ledger, { event: 'rendered', reader: readerKey(role, name), role, name: name ?? null, file, sha256: sha, bytes: Buffer.byteLength(text), ...targeted });
|
||||
process.stdout.write(`BRIEF: reader=${readerKey(role, name)} sha256=${sha} bytes=${Buffer.byteLength(text)} file=${file}\n`);
|
||||
return EXIT.ok;
|
||||
}
|
||||
|
||||
/** The receipt's review_targeted record from the newest targeted dispatch: done only when its brief was sent verbatim and its output collected. */
|
||||
/**
|
||||
* The receipt's review_targeted record from the newest targeted dispatch: done only when its brief was sent verbatim, its output
|
||||
* collected, and the committed diff it was rendered on is still the one at verify (targetedPatchId; uncommitted never proves it).
|
||||
*/
|
||||
function targetedRecord(ledger: string, rows: VerifyRow[]): { classes: string[]; rules: string; brief_sha: string; status: 'done' | 'failed'; findings: number; raised_by: string } | null {
|
||||
const events = readLedger(ledger);
|
||||
const render = events.filter(e => e.event === 'rendered' && e.reader === 'targeted').at(-1);
|
||||
@@ -96,7 +99,8 @@ function targetedRecord(ledger: string, rows: VerifyRow[]): { classes: string[];
|
||||
const label = rows.filter(r => r.kind === 'BRIEF' && r.reader.split('@')[0] === 'targeted').at(-1)?.reader;
|
||||
const brief = rows.filter(r => r.kind === 'BRIEF' && r.reader === label).at(-1);
|
||||
const output = rows.filter(r => r.kind === 'OUTPUT' && r.reader === label).at(-1);
|
||||
const done = brief?.status === 'MATCH' && output?.status === 'MATCH';
|
||||
const read = typeof render.patch_id === 'string' && !['uncommitted', 'none'].includes(render.patch_id) && typeof render.diff_base === 'string' && targetedPatchId(process.cwd(), render.diff_base) === render.patch_id;
|
||||
const done = brief?.status === 'MATCH' && output?.status === 'MATCH' && read;
|
||||
const ids = events.filter(e => e.event === 'output' && e.reader === 'targeted').at(-1)?.ids;
|
||||
return { classes: (render.classes as string[]) ?? [], rules: String(render.rules), brief_sha: String(render.brief_sha), status: done ? 'done' : 'failed', findings: done && Array.isArray(ids) ? ids.length : 0, raised_by: 'targeted' };
|
||||
}
|
||||
|
||||
+9
-11
@@ -5,7 +5,7 @@
|
||||
* gstack-review-plan --base <ref> [--pass <full passes run>] [--last none|full|fix-check] [--fixed-blocking] [--fixed-below]
|
||||
* [--blocking-open] [--touched-gate] [--critical-found] [--adversarial-ran] [--gate-tagged <n>] [--gated a,b] [--force a,b|all]
|
||||
* [--reviewed-numbers <fingerprint>] [--depth-floor standard|full] [--range-base <sha>] [--pass1-depth light|standard|full] [--no-tiers]
|
||||
* [--targeted-ran <product_patch_id>] [--json]
|
||||
* [--targeted-ran <targeted_patch_id>] [--json]
|
||||
* gstack-review-plan --fix-check-prompt --fix-list <file> --hunks <file> --tests <file> [--callers <file>]
|
||||
*
|
||||
* Reads the diff against the merge base with <ref> (committed, tracked uncommitted
|
||||
@@ -24,8 +24,8 @@ import { spawnSync } from 'node:child_process';
|
||||
import { EXIT, exitTableHelp } from '../lib/headless-artifacts';
|
||||
import { runScriptSync } from '../lib/script-invocation';
|
||||
import { collectCommit, collectLive } from '../lib/review-collect';
|
||||
import { classifyDiff, classifyPath, diffCommand, planReview, reviewDepthLine, reviewPlanLine, scanNumbers, SCOPED_RANGE_LINES, type Depth, type History } from '../lib/review-depth';
|
||||
import { classList, targetedBriefSha, targetedClasses } from '../lib/review-targeted';
|
||||
import { classifyDiff, diffCommand, planReview, reviewDepthLine, reviewPlanLine, scanNumbers, SCOPED_RANGE_LINES, type Depth, type History } from '../lib/review-depth';
|
||||
import { classList, targetedBriefSha, targetedClasses, targetedPatchId } from '../lib/review-targeted';
|
||||
import { renderFixCheckPrompt } from '../scripts/resolvers/fix-check';
|
||||
|
||||
const HELP = `gstack-review-plan — the review depth of the next /ship step (docs/review-policy.md#review-depth)
|
||||
@@ -52,8 +52,8 @@ const HELP = `gstack-review-plan — the review depth of the next /ship step (do
|
||||
--no-tiers specialists, audits and watchers at the host's default effort too (as gstack-config review_effort_tiers off does)
|
||||
--json the plan as JSON (depth, runtime_lines, rules, errors with input and repair, gate_scan, per-dispatch exclude)
|
||||
--pass1-depth <d> pass 1's REVIEW_DEPTH depth: full, or absent, keeps pass 2 full
|
||||
--targeted-ran <id> the TARGETED line's product_patch_id when the targeted reader last read the diff: the same product
|
||||
patch skips it; any other id, or uncommitted edits, dispatches it again
|
||||
--targeted-ran <id> the TARGETED line's targeted_patch_id (the whole committed diff, tests included) when the targeted
|
||||
reader last read it: the same patch skips it; any other id, none, or uncommitted edits dispatch it again
|
||||
--fix-check-prompt print the fix-check reviewer's prompt from --fix-list, --hunks, --tests and --callers files
|
||||
|
||||
Depth: light (runtime lines <= 50), standard (above 50), full (gate class or any DEPTH_ERROR). TARGETED: the money,
|
||||
@@ -138,23 +138,21 @@ function main(): number {
|
||||
}
|
||||
}
|
||||
const targeted = targetedClasses({ ...collected, scope, gateTagged });
|
||||
const product = paths.filter(p => classifyPath(p, root) === 'code');
|
||||
const patch = git(['diff', '--quiet', 'HEAD', '--']) === null || collected.untracked.length ? 'uncommitted'
|
||||
: !product.length ? 'none' : spawnSync('git', ['patch-id', '--verbatim'], { input: git(['diff', '--no-color', '--no-ext-diff', collected.mergeBase!, 'HEAD', '--', ...product]) ?? '', encoding: 'utf8', timeout: 60_000 }).stdout?.trim().split(/\s+/)[0] || 'none';
|
||||
const patch = collected.mergeBase ? targetedPatchId(process.cwd(), collected.mergeBase) : 'none';
|
||||
const brief = targetedBriefSha();
|
||||
const plan = planReview({
|
||||
...collected, scope, gateTagged, targeted: { classes: targeted.classes, ran: patch !== 'uncommitted' && flag('--targeted-ran') === patch }, numbers: scanNumbers(added), force, gated: list('--gated'), floor, tiers: auditEffort !== 'host',
|
||||
...collected, scope, gateTagged, targeted: { classes: targeted.classes, ran: !['uncommitted', 'none'].includes(patch) && flag('--targeted-ran') === patch }, numbers: scanNumbers(added), force, gated: list('--gated'), floor, tiers: auditEffort !== 'host',
|
||||
criticalFound: has('--critical-found'), adversarialRan: has('--adversarial-ran'), reviewedNumbers: flag('--reviewed-numbers'),
|
||||
history: { fullPasses: pass, last, fixedBlocking: has('--fixed-blocking'), fixedBelow: has('--fixed-below') && last !== 'fix-check', blockingOpen: has('--blocking-open'), touchedGate: has('--touched-gate') || !!fixRange?.touched_gate, pass1Depth, rangeRuntimeLines: fixRange?.runtime_lines },
|
||||
});
|
||||
const gateScan = { tagged: gateTagged, head };
|
||||
if (has('--json')) {
|
||||
const { runtimeLines, stepDepth, depthReasons, ...rest } = plan;
|
||||
process.stdout.write(JSON.stringify({ ...rest, targeted: { ...targeted, brief, product_patch_id: patch }, step_depth: stepDepth, runtime_lines: runtimeLines, depth_reasons: depthReasons, depth_floor: floor ?? null, gate_scan: gateScan, fix_range: fixRange ?? null, registry: { sha: registry.sha, outputs: registry.outputs }, per_path: perPath, diff_lines: diffLines, pass, audit_effort: auditEffort, scope, gate_tagged: gateTagged, paths, deleted, stamp_only: stampOnly, consumed: collected.consumed }) + '\n');
|
||||
process.stdout.write(JSON.stringify({ ...rest, targeted: { ...targeted, brief, targeted_patch_id: patch }, step_depth: stepDepth, runtime_lines: runtimeLines, depth_reasons: depthReasons, depth_floor: floor ?? null, gate_scan: gateScan, fix_range: fixRange ?? null, registry: { sha: registry.sha, outputs: registry.outputs }, per_path: perPath, diff_lines: diffLines, pass, audit_effort: auditEffort, scope, gate_tagged: gateTagged, paths, deleted, stamp_only: stampOnly, consumed: collected.consumed }) + '\n');
|
||||
return EXIT.ok;
|
||||
}
|
||||
const out = [`REVIEW_CLASS: ${plan.class} (${plan.reasons.slice(0, 3).join('; ') || 'no signals'})`, reviewDepthLine(plan), `GATE_SCAN: ${gateTagged === null ? 'unavailable' : `tagged=${gateTagged}`} head=${head}`,
|
||||
`TARGETED: classes=${classList(targeted.classes)} rules=${targeted.rules} brief=${brief} product_patch_id=${patch}${targeted.reasons.length ? ` reasons=${targeted.reasons.join('; ')}` : ''}`];
|
||||
`TARGETED: classes=${classList(targeted.classes)} rules=${targeted.rules} brief=${brief} targeted_patch_id=${patch}${targeted.reasons.length ? ` reasons=${targeted.reasons.join('; ')}` : ''}`];
|
||||
if (fixRange) out.push(`FIX_RANGE: ${fixRange.runtime_lines === null ? `unread (${fixRange.error})` : `runtime_lines=${fixRange.runtime_lines}`} base=${fixRange.base}`);
|
||||
out.push(reviewPlanLine(plan, pass + (plan.next.step === 'full' || plan.next.step === 'scoped' ? 1 : 0)), `NEXT: ${plan.next.step} (${plan.next.reason})`);
|
||||
for (const e of plan.errors) out.push(`DEPTH_ERROR: ${e.code} input=${e.input} detail=${e.detail}; fix: ${e.repair}${e.result ? ` (${e.result})` : ''}`);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
**Diffs that touch money, gate, auth or secret code now get one more reviewer, a targeted reader with a short brief for that kind of code, and the /ship receipt records whether it ran (GST-257, part 1 of 2).** The merge queue reports the result and refuses nothing yet; enforcement comes in a later release.
|
||||
|
||||
### Added
|
||||
- **`lib/review-targeted.ts` names the classes a diff touches.** It reads added and deleted lines, so removing a spend cap, a check or a redaction counts. Money is a cost, spend, budget, ledger, billing, price, fee or invoice path, an identifier ending in `_usd` or `Usd`, any `spend_cap` spelling, or a call to a paid model provider. Gate is a hunk the gate scan tagged. Auth is `SCOPE_AUTH` on an auth, session, jwt, permission or role path. Secret is a secrets, credentials, keychain or token path, or a line that reads a key-named environment variable or a credential store. Matching is token-bounded, docs and tests never count, and an input the classifier cannot read gives all four classes.
|
||||
- **`gstack-review-plan` prints `TARGETED: classes=… rules=… brief=… product_patch_id=…`** and, when a class hit, `DISPATCH: targeted targeted effort=full` at every depth, light included. `--targeted-ran <product_patch_id>` skips the reader only when the committed product patch is the one it already read.
|
||||
- **`lib/review-targeted.ts` names the classes a diff touches.** It reads added and deleted lines, so removing a spend cap, a check or a redaction counts. Money is a cost, spend, budget, ledger, billing, price, fee or invoice path, an identifier ending in `_usd` or `Usd`, any `spend_cap` spelling, or a call to a paid model provider. Gate is a hunk the gate scan tagged. Auth is `SCOPE_AUTH` on an auth, session, jwt, permission or role path. Secret is a secrets, credentials, keychain or token path, or a line that reads a key-named environment variable or a credential store. Matching is token-bounded, paths review depth counts as docs or tests never count (agent prose such as skill sections and docs/review-policy.md counts as code there, so it counts here too), and an input the classifier cannot read gives all four classes.
|
||||
- **`gstack-review-plan` prints `TARGETED: classes=… rules=… brief=… targeted_patch_id=…`** and, when a class hit, `DISPATCH: targeted targeted effort=full` at every depth, light included. `--targeted-ran <targeted_patch_id>` skips the reader only when the whole committed diff, tests included, is the one it already read.
|
||||
- **`review/specialists/targeted.md` is the reader's brief:** one block per class, the blocking floor in one sentence, and the checklist's Forced-CRITICAL rule on uncapped spend in the money block. The reader is read-only, and `NO FINDINGS` is a result.
|
||||
- **`gstack-review-dispatch render --role targeted --classes …`** renders the brief with only the blocks that hit; `verify --targeted-out <file>` writes the record `gstack-ship-receipt write --review-targeted @<file>` stores as `review_targeted`, bound to the receipt's `product_patch_id`. A carry keeps it while the product patch is unchanged.
|
||||
- **`gstack-review-dispatch render --role targeted --classes …`** renders the brief with only the blocks that hit; `verify --targeted-out <file>` writes the record (done only when the reader's output was collected and the committed diff it was rendered on is unchanged) `gstack-ship-receipt write --review-targeted @<file>` stores as `review_targeted`, bound to the receipt's `product_patch_id`. A carry keeps it while the product patch is unchanged.
|
||||
- **`gstack-ship-queue ready` and `train build` print `REVIEW_TARGETED: required=… receipt=… verdict=ok|missing`.** Nothing is refused on it in this release.
|
||||
|
||||
### Precision
|
||||
|
||||
@@ -312,11 +312,13 @@ The classifier and the brief are gate paths, so an edit that weakens them gets
|
||||
full review.
|
||||
|
||||
The plan prints `TARGETED: classes=<money,gate,auth,secret|none> rules=<hash>
|
||||
brief=<sha256 of the brief> product_patch_id=<id>` and, when a class hit,
|
||||
brief=<sha256 of the brief> targeted_patch_id=<id>` and, when a class hit,
|
||||
`DISPATCH: targeted targeted effort=full`. After a fix, pass
|
||||
`--targeted-ran <product_patch_id>` from the line the reader ran on: the same
|
||||
committed product patch skips the reader, and a changed one or uncommitted
|
||||
edits dispatch it again.
|
||||
`--targeted-ran <targeted_patch_id>` from the line the reader ran on: the same
|
||||
committed patch skips the reader, and a changed one, uncommitted edits or
|
||||
`none` dispatch it again. `targeted_patch_id` is the patch-id of the whole
|
||||
committed diff, tests included, so a test-only fix that adds a gate hunk
|
||||
dispatches it again; it is not the receipt's `product_patch_id`.
|
||||
|
||||
The brief states the blocking floor in one sentence, and its money block cites
|
||||
the checklist's Forced-CRITICAL rule on uncapped spend, so a money finding can
|
||||
@@ -351,7 +353,7 @@ puts in the receipt:
|
||||
| `review_loop.fix_checks` | fix-checks this ship ran (never passes) |
|
||||
| `review_loop.dispatches` | reviewer dispatches by role: `specialist`, `red-team`, `adversarial-native`, `outside-voice`, `fix-check`, `targeted` |
|
||||
| `review_loop.est_input_tokens` | prompt plus diff bytes the dispatches read, divided by 4: a relative estimate for comparing runs, not a bill |
|
||||
| `review_targeted` | the [targeted pass](#targeted-pass-on-money-gate-auth-and-secret-code): the `classes` it covered, `rules`, `brief_sha`, the receipt's own `product_patch_id`, `status` (`done` only with its output collected, else `failed`; absent when write got no record, which the queue reports as `legacy`), `findings` and `raised_by`; a carry keeps it while the product patch is unchanged |
|
||||
| `review_targeted` | the [targeted pass](#targeted-pass-on-money-gate-auth-and-secret-code): the `classes` it covered, `rules`, `brief_sha`, the receipt's own `product_patch_id`, `status` (`done` only with its output collected and the committed diff it was rendered on still the one at verify, else `failed`; absent when write got no record, which the queue reports as `legacy`), `findings` and `raised_by`; a carry keeps it while the product patch is unchanged |
|
||||
|
||||
Each review row may carry `dispatches: [{role, effort, prompt_bytes,
|
||||
diff_bytes, status}]`, and each finding `raised_by` (the reviewer that raised it,
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
* loosened check), not GATE_PATH: over the last 100 non-release commits on main, GATE_PATH
|
||||
* alone hit 43 (the precision gate allows 25), and those paths already get full depth.
|
||||
*/
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { createHash } from 'node:crypto';
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
@@ -98,6 +99,19 @@ export function targetedBriefSha(file = TARGETED_BRIEF_FILE): string {
|
||||
try { return createHash('sha256').update(fs.readFileSync(file)).digest('hex'); } catch { return 'missing'; }
|
||||
}
|
||||
|
||||
/**
|
||||
* The patch the targeted reader reads: the patch-id of the whole committed diff from `mergeBase`, tests included, so a
|
||||
* test-only fix that adds a gate hunk changes it. `uncommitted` with tracked edits or untracked files, `none` for an empty
|
||||
* or unreadable diff; neither proves what a reader read. Not the receipt's product_patch_id (that leaves tests out).
|
||||
*/
|
||||
export function targetedPatchId(root: string, mergeBase: string): string {
|
||||
const git = (args: string[], input?: string) => spawnSync('git', args, { cwd: root, input, encoding: 'utf8', timeout: 60_000, maxBuffer: 256 * 1024 * 1024 });
|
||||
if (git(['diff', '--quiet', 'HEAD', '--']).status !== 0 || git(['ls-files', '--others', '--exclude-standard']).stdout?.trim()) return 'uncommitted';
|
||||
const diff = git(['diff', '--no-color', '--no-ext-diff', mergeBase, 'HEAD']);
|
||||
if (diff.status !== 0 || !diff.stdout.trim()) return 'none';
|
||||
return git(['patch-id', '--verbatim'], diff.stdout).stdout?.trim().split(/\s+/)[0] || 'none';
|
||||
}
|
||||
|
||||
/** A class list as printed and recorded: comma-joined, or `none`. */
|
||||
export const classList = (classes: readonly string[]) => (classes.length ? classes.join(',') : 'none');
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ describe('targetedClasses: the four classes from added and deleted lines', () =>
|
||||
expect(classesOf(input(['lib/pregate-check.ts']))).toEqual([]);
|
||||
expect(classesOf(input(['src/auth/session.ts'], { scope: { SCOPE_AUTH: 'true' } }))).toEqual(['auth']);
|
||||
expect(classesOf(input(['src/auth/session.ts']))).toEqual([]);
|
||||
expect(classesOf(input(['lib/authentication.ts', 'src/authorizer/index.ts'], { scope: { SCOPE_AUTH: 'true' } }))).toEqual(['auth']);
|
||||
expect(classesOf(input(['src/client.ts'], { added: { 'src/client.ts': ['const key = process.env.OPENAI_API_KEY;'] } }))).toEqual(['secret']);
|
||||
expect(classesOf(input(['src/log.ts'], { removed: { 'src/log.ts': [' console.log(s.replaceAll(process.env.ANTHROPIC_API_KEY ?? "", "***"));'] } }))).toEqual(['secret']);
|
||||
expect(classesOf(input(['bin/run.sh'], { added: { 'bin/run.sh': ['curl -H "Authorization: $GH_TOKEN" "$URL"'] } }))).toEqual(['secret']);
|
||||
@@ -208,7 +209,7 @@ describe('gstack-review-plan prints TARGETED and dispatches the targeted reader'
|
||||
const first = repo.run([]);
|
||||
expect(field(first.stdout, 'TARGETED:', 'rules')).toBe(TARGETED_RULES);
|
||||
expect(field(first.stdout, 'TARGETED:', 'brief')).toBe(targetedBriefSha());
|
||||
const patch = field(first.stdout, 'TARGETED:', 'product_patch_id')!;
|
||||
const patch = field(first.stdout, 'TARGETED:', 'targeted_patch_id')!;
|
||||
expect(patch).toMatch(/^[0-9a-f]{40}$/);
|
||||
const same = repo.run(['--pass', '1', '--last', 'full', '--fixed-below', '--targeted-ran', patch]);
|
||||
expect(same.stdout).not.toContain('DISPATCH: targeted');
|
||||
@@ -218,10 +219,18 @@ describe('gstack-review-plan prints TARGETED and dispatches the targeted reader'
|
||||
expect(other.stdout).toContain('DIFF_EXCLUDE: targeted none');
|
||||
repo.write({ 'src/charge.ts': 'export const spend_cap_usd = 6;\n' });
|
||||
const dirty = repo.run(['--pass', '1', '--last', 'full', '--fixed-below', '--targeted-ran', patch]);
|
||||
expect(field(dirty.stdout, 'TARGETED:', 'product_patch_id')).toBe('uncommitted');
|
||||
expect(field(dirty.stdout, 'TARGETED:', 'targeted_patch_id')).toBe('uncommitted');
|
||||
expect(dirty.stdout).toContain('DISPATCH: targeted targeted effort=full');
|
||||
expect(JSON.parse(repo.run(['--json']).stdout).targeted).toMatchObject({ classes: ['money'], rules: TARGETED_RULES, product_patch_id: 'uncommitted' });
|
||||
}, 60_000);
|
||||
expect(JSON.parse(repo.run(['--json']).stdout).targeted).toMatchObject({ classes: ['money'], rules: TARGETED_RULES, targeted_patch_id: 'uncommitted' });
|
||||
const read = planRepo(FIXTURES['money-light']!);
|
||||
const readId = field(read.run([]).stdout, 'TARGETED:', 'targeted_patch_id')!;
|
||||
read.write({ 'test/charge.test.ts': 'test("cap", () => {});\n' });
|
||||
for (const args of [['add', '.'], ['commit', '-qm', 'test-only fix']]) expect(spawnSync('git', args, { cwd: read.dir, encoding: 'utf8', timeout: 30_000 }).status).toBe(0);
|
||||
const testFix = read.run(['--gate-tagged', '1', '--pass', '1', '--last', 'full', '--fixed-below', '--targeted-ran', readId]);
|
||||
expect(field(testFix.stdout, 'TARGETED:', 'classes')).toBe('money,gate');
|
||||
expect(field(testFix.stdout, 'TARGETED:', 'targeted_patch_id')).not.toBe(readId);
|
||||
expect(testFix.stdout).toContain('DISPATCH: targeted targeted effort=full');
|
||||
}, 90_000);
|
||||
});
|
||||
|
||||
// --- production path: render, sent, collect, judge, verify, receipt write, summary and the queue ------------------------
|
||||
@@ -268,6 +277,11 @@ describe('the targeted role end to end on a fixture repository', () => {
|
||||
expect(verify.status, verify.out).toBe(0);
|
||||
expect(verify.out).toContain(`TARGETED_RECORD: classes=money status=done findings=1 brief=${targetedBriefSha().slice(0, 12)} rules=${TARGETED_RULES}`);
|
||||
expect(verify.out).toContain('VERIFY: clean');
|
||||
fs.writeFileSync(path.join(repo.work, 'src', 'charge.ts'), 'export const spend_cap_usd = 50;\n');
|
||||
repo.git('commit', '-qam', 'later product change the reader never read');
|
||||
expect(runBin(repo, 'gstack-review-dispatch', ['verify', '--ledger', ledger]).out).toContain('TARGETED_RECORD: classes=money status=failed findings=0');
|
||||
repo.git('reset', '-q', '--hard', head);
|
||||
expect(runBin(repo, 'gstack-review-dispatch', ['verify', '--ledger', ledger]).out).toContain('TARGETED_RECORD: classes=money status=done findings=1');
|
||||
|
||||
const body = path.join(repo.root, 'body.md');
|
||||
const write = runBin(repo, 'gstack-ship-receipt', ['write', '--base', 'main', '--session-kind', 'unattended', '--review-targeted', `@${record}`, '--body', body, '--json']);
|
||||
|
||||
@@ -383,7 +383,7 @@ describe('variant E: the review segment and the docs reader (GSTA-167 replay 5)'
|
||||
expect(settled.out).toMatch(new RegExp(`^DRIFT: id=${id} kind=dispatch next=dispatch:docs projected=${K.per_dispatch} actual=2000000 `, 'm'));
|
||||
const step = check(h, s.run, '14.5', '15', ['--reading', '3500000']);
|
||||
expect(step.out).toMatch(/^DRIFT: id=\S+ kind=step next=14\.5 projected=\d+ actual=400000 /m);
|
||||
expect(h.sb(['check', '--run', s.run, '--step', '15', '--next', 'dispatch:doc', '--reading', '3600000']).err).toContain('valid: specialist, red-team, adversarial-native, outside-voice, fix-check, docs');
|
||||
expect(h.sb(['check', '--run', s.run, '--step', '15', '--next', 'dispatch:doc', '--reading', '3600000']).err).toContain('valid: specialist, red-team, adversarial-native, outside-voice, fix-check, targeted, docs');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user