mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-03 01:46:55 +02:00
* feat: add surface-aware exploratory QA and ship documentation gates * test: preserve delegated QA setup authority after main integration * fix(qa): clarify exploration order and preserve report artifacts * test(qa): follow the shared setup reference directly * refactor(ship): make verification and recovery routes explicit * test(ship): align evidence and review guards with explicit routes * fix(workflows): clarify ship recovery and functional QA evidence * fix(workflows): clarify approval recovery and full QA coverage * refactor(workflows): order review transactions and clarify ship state * fix(ship): clarify final verification and fail closed at publication * fix(evals): attribute native atomic documentation writes * fix(ship): clarify recovery and documentation lifecycle guidance * fix(test): preserve observed native placeholder styling in CI * fix(codex): report watchdog timeouts without a process-exit race * Checkpoint functional QA implementation and workflow validation repairs * Fix documentation and shared-review fixture contracts * docs: clarify judge reuse and evaluation supervision * test: align review evidence and selected case contracts * test: verify append-only documentation checkpoints and recovery * fix: qualify QA workflows and CI validation repairs * fix: launch shared-libs fixture scripts on Windows * fix: qualify QA deadlines, fixture isolation, and shard cleanup * fix: preserve qualified QA and cancellation repairs * fix: enforce functional fixture authority and share strict event decoding * fix: retain free-test evidence and explain recovery * fix: reject malformed native evidence after decoder consolidation * test: use reliable capture for telemetry privacy filters * test: refresh measured quick coverage and document validation costs * Fix native fixture receipts and preserve VM validation evidence * Align negative judge controls with upstream clarity policy * Fix report-only QA preparation and public evidence handling * Clarify QA-only preparation and current-report preservation * Stream Ship quality judgments with an explicit 64k response contract * Validate compact judge reasoning locally with supported wire schema * Align functional QA fixture instructions with evidence acceptance * Bind native browser diagnostics to execution evidence and align review verdicts * Preserve native diagnostic line boundaries * Serialize functional QA evidence from native captures * Keep large QA evidence fixture payload out of Windows argv
71 lines
3.8 KiB
TypeScript
71 lines
3.8 KiB
TypeScript
import * as path from 'node:path';
|
|
|
|
export interface DocsCompletion {
|
|
schema_version: 1;
|
|
audit_id: string;
|
|
status: 'updated' | 'current' | 'blocked';
|
|
files_updated: string[];
|
|
files_reviewed: string[];
|
|
documentation_section: string;
|
|
blockers: string[];
|
|
decisions: string[];
|
|
}
|
|
|
|
const keys = ['schema_version', 'audit_id', 'status', 'files_updated', 'files_reviewed', 'documentation_section', 'blockers', 'decisions'];
|
|
|
|
export function parseDocsCompletion(output: string, auditId: string): DocsCompletion {
|
|
const result = JSON.parse(output.trimEnd().split('\n').at(-1)!);
|
|
if (!result || Array.isArray(result) || typeof result !== 'object' ||
|
|
Object.keys(result).sort().join() !== [...keys].sort().join()) throw new Error('completion fields');
|
|
if (result.schema_version !== 1 || result.audit_id !== auditId) throw new Error('completion identity');
|
|
if (!['updated', 'current', 'blocked'].includes(result.status)) throw new Error('completion status');
|
|
for (const field of ['files_updated', 'files_reviewed', 'blockers', 'decisions']) {
|
|
if (!Array.isArray(result[field]) || result[field].some((v: unknown) => typeof v !== 'string' || !v.trim())) {
|
|
throw new Error(`completion ${field}`);
|
|
}
|
|
}
|
|
if (typeof result.documentation_section !== 'string' || !result.documentation_section.trim()) throw new Error('completion markdown');
|
|
for (const field of ['files_updated', 'files_reviewed']) {
|
|
const paths: string[] = result[field];
|
|
if (new Set(paths).size !== paths.length || paths.some(p => path.posix.isAbsolute(p) || p.includes('\\') ||
|
|
p.split('/').some(part => !part || part === '.' || part === '..') || /[*?\[\]]/.test(p))) throw new Error('completion paths');
|
|
}
|
|
if (result.status === 'blocked' ? result.blockers.length === 0 : result.blockers.length !== 0) throw new Error('completion blockers');
|
|
if (result.status === 'current' && result.files_updated.length !== 0 ||
|
|
result.status === 'updated' && result.files_updated.length === 0) throw new Error('completion edits');
|
|
return result;
|
|
}
|
|
|
|
export function vetDocsCompletion(result: DocsCompletion, evidence: {
|
|
settled: boolean;
|
|
markerSeen: boolean;
|
|
headUnchanged: boolean;
|
|
indexUnchanged: boolean;
|
|
candidateUnchanged: boolean;
|
|
readOnly: boolean;
|
|
changedPaths: string[];
|
|
allowedDocs: string[];
|
|
}): void {
|
|
if (!evidence.settled || !evidence.markerSeen) throw new Error('unsettled or unmarked child');
|
|
if (!evidence.headUnchanged || !evidence.indexUnchanged) throw new Error('Git ownership violation');
|
|
if (!evidence.candidateUnchanged) throw new Error('stale candidate');
|
|
if (evidence.readOnly && evidence.changedPaths.length) throw new Error('read-only mutation');
|
|
if ([...evidence.changedPaths].sort().join('\0') !== [...result.files_updated].sort().join('\0')) throw new Error('unreported edits');
|
|
const forbidden = /(?:^|\/)(?:VERSION|CHANGELOG(?:\.[^/]*)?|TODOS(?:\.[^/]*)?|package(?:-lock)?\.json|[^/]*lock[^/]*|manifest\.json)$/i;
|
|
if (evidence.changedPaths.some(p => forbidden.test(p) || !evidence.allowedDocs.includes(p))) throw new Error('non-doc mutation');
|
|
}
|
|
|
|
export function extractDocsDispatch(section: string): string {
|
|
const begin = section.indexOf('**Subagent prompt:**');
|
|
const end = section.indexOf('**Parent processing:**');
|
|
if (begin < 0 || end <= begin) throw new Error('documentation dispatch markers moved');
|
|
return section.slice(begin + '**Subagent prompt:**'.length, end)
|
|
.split('\n').map(line => line.replace(/^> ?/, '')).join('\n').trim();
|
|
}
|
|
|
|
export function docsDispatchIndex(calls: Array<{ tool: string; input: unknown }>): number {
|
|
return calls.findIndex(call => ['Agent', 'Task'].includes(call.tool) &&
|
|
/document-release\/SKILL\.md|executing the \/document-release workflow/i.test(JSON.stringify(call.input)) &&
|
|
!/Parent processing:|## Step 19: Create PR\/MR/.test(JSON.stringify(call.input)));
|
|
}
|