mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-08 01:38:10 +01:00
feat(diff-scope): template UI in .ts/.js counts as frontend; receipt records the static design pass (GST-1718) (#4112)
* feat(diff-scope): template UI in .ts/.js counts as frontend; receipt records the static design pass (GST-1718) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: regenerate ship goldens for the static design-review line (GST-1718) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes Review-Finding: p1/specialist-batched#1 reader=specialist Review-Finding: p1/red-team#1 reader=red-team Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: count edits inside tagged templates and spaced template tags as frontend Review-Finding: v1/outside-voice#1 reader=outside-voice Review-Finding: v1/adversarial-native#1 reader=adversarial-native Review-Finding: v1/outside-voice#2 reader=outside-voice Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: one set comparison per template file instead of a grep per changed line Review-Finding: p2/red-team#2 reader=red-team Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(changelog): GST-1718 fragment covers edits inside tagged templates Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(ship-policy): design record wording matches the in-template rule 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
40071a4237
commit
5862765a20
20 files changed
+273
-14
No files matched your search
@@ -5,6 +5,9 @@
|
||||
// SCOPE_FRONTEND); test/frontend-scope.test.ts pins the two against the same
|
||||
// sample paths so they cannot drift. bin/gstack-design-detect.ts uses this to
|
||||
// derive `scan --changed <base>` targets without consuming a shell-split list.
|
||||
// The bash script's template-UI content arm (GST-1718: Lit or htm templates in
|
||||
// .ts/.js) reads file contents and is not mirrored here; the detector scans
|
||||
// paths, so its targets stay path-only.
|
||||
|
||||
const EXTENSIONS = new Set([
|
||||
'.css', '.scss', '.less', '.sass', '.pcss',
|
||||
|
||||
@@ -109,6 +109,8 @@ export interface ShipReceipt {
|
||||
thread?: string; tree?: string; gate_reuse?: string; policy?: string; queue_mode?: string; preregistration?: string; history?: string;
|
||||
// C-pregate (C8): the pre-gate's verdict against this head and its remote obligations (publication requires their receipts).
|
||||
pregate?: string; requires_remote?: string[];
|
||||
/** GST-1718: the design pass a rendered-UI diff got; phase 1 records only the static design-lite check, with why no rendered pass ran. */
|
||||
design?: { mode: 'static'; reason: string };
|
||||
/** Approval classes (lib/approval-citations.ts): source, status, one record per declared class with hits, report-only built-in hits. */
|
||||
approval?: Record<string, unknown>;
|
||||
// C-pregate (C6): issue links the body carries, by intent.
|
||||
@@ -276,6 +278,7 @@ export const ARTIFACT_SCHEMAS = {
|
||||
review_loop_exception: { type: 'object', required: ['accepted_by', 'date', 'via', 'reason'], properties: { accepted_by: str, date: str, via: str, reason: str, fix_check: { type: 'object', required: ['head', 'findings'], properties: { head: str, findings: { type: 'integer', minimum: 0 }, files: strList } }, second_pass: { type: 'object', required: ['head', 'deferred'], properties: { head: str, deferred: strList } } } },
|
||||
ruled_notes: { type: 'array', items: { type: 'object', required: ['finding', 'head', 'by', 'reason', 'at', 'approval'], properties: { finding: str, head: str, by: str, reason: str, at: str, approval: str } } },
|
||||
mechanical: strList,
|
||||
design: { type: 'object', required: ['mode', 'reason'], properties: { mode: { enum: ['static'] }, reason: str } },
|
||||
no_stop: { type: 'array', items: { type: 'object', required: ['class', 'reason', 'evidence'], properties: { class: { enum: [...NO_STOP_CLASSES] }, reason: str, evidence: str } } },
|
||||
issues: { type: 'object', properties: { fixes: { type: 'array', items: { type: 'integer' } }, refs: { type: 'array', items: { type: 'integer' } }, flakes: { type: 'array', items: { type: 'object', required: ['issue', 'run'], properties: { issue: { type: 'integer' }, run: str } } }, related: { type: 'array', items: { type: 'object', required: ['pr', 'author', 'closes'], properties: { pr: { type: 'integer' }, repo: str, author: str, closes: { type: 'array', items: { type: 'integer' } }, supersedes: { type: 'boolean' }, credit: { type: 'boolean' } } } } } },
|
||||
},
|
||||
|
||||
@@ -113,7 +113,7 @@ function integrate(cwd: string, base: string, run: string | undefined, r: Report
|
||||
const scope = bin(cwd, 'gstack-diff-scope', [`origin/${base}`], 60_000).stdout;
|
||||
const scopeError = /^SCOPE_ERROR=(?!unmatched)(\S+)/m.exec(scope)?.[1];
|
||||
if (scopeError) r.add(`SCOPE: unknown (gstack-diff-scope origin/${base}: SCOPE_ERROR=${scopeError})`);
|
||||
else r.add(`SCOPE: frontend=${/SCOPE_FRONTEND=true/.test(scope)}${/SCOPE_FRONTEND=true/.test(scope) ? ' (Step 9 runs the design lite check; mention /design-review when no design review exists)' : ''}`);
|
||||
else r.add(`SCOPE: frontend=${/SCOPE_FRONTEND=true/.test(scope)}${/SCOPE_FRONTEND=true/.test(scope) ? ' (Step 9 runs the static design lite check only, no preview declared; mention /design-review when no design review exists)' : ''}`);
|
||||
if (!merge(cwd, base, r)) return false;
|
||||
const marker = run && shipRunMarker(cwd);
|
||||
try { if (marker) fs.writeFileSync(marker, `${run}\n`); } catch (e) { r.stop(`cannot record /ship run ${run} in ${marker} (${firstLine((e as Error).message)}): the base is merged, but a later full preflight could merge again under this run's reviews; fix that, then rerun`); return false; }
|
||||
|
||||
@@ -104,7 +104,7 @@ export function receiptDepthAtHead(repoRoot: string, base: string, binDir: strin
|
||||
* receiptDepthAtHead plus the exclusion the head's review read under: the depth classifies excluded paths as docs (GST-508).
|
||||
* `declared` (write --declare-risk, GST-1129) joins the detected risk classes and only raises; the record then keeps both lists.
|
||||
*/
|
||||
export function receiptReviewAtHead(repoRoot: string, base: string, binDir: string, head: string, declared: readonly RiskClass[] = []): { depth: ReceiptDepth; exclude: Exclusion; tagged: ReadonlySet<string> | null } {
|
||||
export function receiptReviewAtHead(repoRoot: string, base: string, binDir: string, head: string, declared: readonly RiskClass[] = []): { depth: ReceiptDepth; exclude: Exclusion; tagged: ReadonlySet<string> | null; frontend: boolean } {
|
||||
const branch = base.replace(/^origin\//, '');
|
||||
const baseSha = baseCommit(repoRoot, `origin/${branch}`);
|
||||
const baseRev = baseSha.ok ? baseSha.out : branch;
|
||||
@@ -124,7 +124,7 @@ export function receiptReviewAtHead(repoRoot: string, base: string, binDir: stri
|
||||
}
|
||||
const exclude = exclusionAt(repoRoot, ref, sha, head, c, paths, 'gstack-ship-receipt');
|
||||
const d = depthOfCommitInput({ ...c, scope, gateTagged: tagged, ...riskPathsOf(review), declared, ...(exclude.state === 'active' ? { excluded: exclude.excluded } : {}) });
|
||||
return { depth: { depth: d.depth, class: d.class, runtime_lines: d.runtimeLines, rules: d.rules, base: baseRev, registry: c.registry.sha, gate_scan: { tagged, head }, reasons: d.reasons.slice(0, 12), risk: d.risk, mode: modeFor(d.depth, review), ...(d.declared.length ? { declared: d.declared, detected: d.detected } : {}) }, exclude, tagged: paths };
|
||||
return { depth: { depth: d.depth, class: d.class, runtime_lines: d.runtimeLines, rules: d.rules, base: baseRev, registry: c.registry.sha, gate_scan: { tagged, head }, reasons: d.reasons.slice(0, 12), risk: d.risk, mode: modeFor(d.depth, review), ...(d.declared.length ? { declared: d.declared, detected: d.detected } : {}) }, exclude, tagged: paths, frontend: scope.SCOPE_FRONTEND === 'true' };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ const KEY_ORDER: Array<keyof ShipReceipt | 'docs'> = [
|
||||
'schema_version', 'pr', 'head', 'version', 'base', 'gated_tree', 'predecessor', 'tree', 'gate_reuse', 'gate', 'ci', 'ci_carried', 'spend_usd',
|
||||
'session_kind', 'artifacts_consumed', 'run', 'thread', 'policy', 'queue_mode', 'preregistration', 'history',
|
||||
'head_tree', 'product_patch_id', 'gstack_version', 'review_depth', 'pregate', 'requires_remote', 'issues', 'approval', 'docs', 'review_loop',
|
||||
'review_loop_exception', 'ruled_notes', 'mechanical', 'review_targeted', 'review_exclude', 'carry', 'base_red', 'flaky_exception', 'no_stop',
|
||||
'review_loop_exception', 'ruled_notes', 'mechanical', 'review_targeted', 'design', 'review_exclude', 'carry', 'base_red', 'flaky_exception', 'no_stop',
|
||||
];
|
||||
|
||||
/** Keys in the fixed order so identifiers lead; undefined values are dropped. */
|
||||
|
||||
Reference in new issue
Block a user