mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-01 22:45:02 +01:00
fix: pre-landing review fixes (Step 11)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
5ddd0efacb
commit
b553ec96df
4 files changed
+13
-3
No files matched your search
+4
-1
@@ -42,6 +42,7 @@ export interface Manifest {
|
||||
export interface Extra { name: string; prompt_file: string; first?: boolean; model?: PassModel }
|
||||
/** Step 9.2.1's own first line: its probes are the parent's, so a QA subagent only refuses (C4-36, C5-36: "parent-only"). */
|
||||
export const PARENT_ONLY_QA = 'Only the parent runs report-only discovery.';
|
||||
const qaRefusal = (name: string) => new BriefError(`Step 9.2.1 is the parent's report-only discovery (ship/sections/review-army.md: "${PARENT_ONLY_QA}"), never an audit reader: ${name}`);
|
||||
|
||||
/** Brief characters per transport chunk: even with every character escaped, one chunk line stays far under 1 MiB. */
|
||||
export const CHUNK_CHARS = 65_536;
|
||||
@@ -89,7 +90,9 @@ export function buildManifest(o: {
|
||||
}
|
||||
for (const e of o.extras ?? []) {
|
||||
if (!/^[a-z0-9-]+$/.test(e.name)) throw new BriefError(`an audit reader's name is lowercase letters, digits and dashes: ${e.name}`);
|
||||
if (/(^|-)qa(-|$)/.test(e.name) || (fs.existsSync(e.prompt_file) && fs.readFileSync(e.prompt_file, 'utf8').includes(PARENT_ONLY_QA))) throw new BriefError(`Step 9.2.1 is the parent's report-only discovery (ship/sections/review-army.md: "${PARENT_ONLY_QA}"), never an audit reader: ${e.name}`);
|
||||
let brief = '';
|
||||
try { brief = fs.readFileSync(e.prompt_file, 'utf8'); } catch { brief = ''; }
|
||||
if (/(^|-)qa(-|$)/.test(e.name) || brief.includes(PARENT_ONLY_QA)) throw qaRefusal(e.name);
|
||||
readers.push({ reader: `audit/${e.name}`, stage: e.first ? 'first' : 'specialists', title: `ship ${o.pass} audit/${e.name}`, state: 'planned', prompt_file: path.resolve(e.prompt_file), model: e.model });
|
||||
}
|
||||
if (new Set(readers.map(r => r.reader)).size !== readers.length) throw new BriefError('two readers share a key in this pass');
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ there, come back. Unattended, a question with a recommended option takes it (log
|
||||
disposition and every approval stay pending and end the run. Keep a private invocation record (the
|
||||
`--record` file): write its six headings at Phase 1, then fill each as its phase ends: **Checks**
|
||||
(Phases 2-4: lanes, pregate, evals, `Coverage:`, the plan checks, `Exploratory QA:`, `Scope Check:`), **Decisions** (any
|
||||
phase: each answered or auto-taken question), **Reviews** (Phases 3-5: the plan line,
|
||||
phase: each answered or auto-taken question, in its words), **Reviews** (Phases 3-5: the plan line,
|
||||
start tokens, `Pre-Landing Review:`, `Gate edits:`, the reviewed tree), **Release** (Phases 5-7:
|
||||
version, CHANGELOG, commits), **Documentation** (Step 14.5) and **Next steps** (every phase: the next act).
|
||||
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ there, come back. Unattended, a question with a recommended option takes it (log
|
||||
disposition and every approval stay pending and end the run. Keep a private invocation record (the
|
||||
`--record` file): write its six headings at Phase 1, then fill each as its phase ends: **Checks**
|
||||
(Phases 2-4: lanes, pregate, evals, `Coverage:`, the plan checks, `Exploratory QA:`, `Scope Check:`), **Decisions** (any
|
||||
phase: each answered or auto-taken question), **Reviews** (Phases 3-5: the plan line,
|
||||
phase: each answered or auto-taken question, in its words), **Reviews** (Phases 3-5: the plan line,
|
||||
start tokens, `Pre-Landing Review:`, `Gate edits:`, the reviewed tree), **Release** (Phases 5-7:
|
||||
version, CHANGELOG, commits), **Documentation** (Step 14.5) and **Next steps** (every phase: the next act).
|
||||
|
||||
|
||||
@@ -87,6 +87,13 @@ describe('pass manifest', () => {
|
||||
expect(m.readers[0]).toMatchObject({ state: 'created', path: '4' });
|
||||
});
|
||||
|
||||
// Value: protects=an unreadable extra prompt stops as a BriefError naming the reader; fails_when=a directory prompt_file throws a raw
|
||||
// EISDIR from the QA guard's read (GST-450 adversarial #4); seam=none
|
||||
test('a directory extra prompt stops as an unreadable prompt file, not a raw read error', () => {
|
||||
const d = manifest([spec('testing')], { extras: [{ name: 'plan', prompt_file: tmp('rp-qa-') }] });
|
||||
expect(() => renderStage(d, 'specialists', texts, () => '')).toThrow(/cannot read audit\/plan's prompt file/);
|
||||
});
|
||||
|
||||
test('a brief over 1 MiB with invalid UTF-8 in its diff travels in bounded chunks that rejoin to the rendered sha', () => {
|
||||
const m = manifest([{ role: 'outside-voice', name: 'outside-voice', effort: 'host', exclude: [] }]);
|
||||
const diff = Buffer.concat([Buffer.from('+line \u00e9\n'.repeat(90_000)), Buffer.from([0xff, 0xfe, 0x0a]), Buffer.from('+"quoted" \\ back\n'.repeat(20_000))]).toString('utf8');
|
||||
|
||||
Reference in new issue
Block a user