Merge remote-tracking branch 'origin/main' into gst-508-review-exclude-paths

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

# Conflicts:
#	bin/gstack-review-dispatch
#	bin/gstack-ship-receipt
#	lib/review-pass.ts
This commit is contained in:
garrytan committed 2026-11-01 14:26:07 +00:00
commit c204be95fb
259 files changed
+17703 -6496

No files matched your search

File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+63
View File
@@ -258,6 +258,26 @@ export const KNOWN_WINDOWS_INCOMPATIBLE: Array<{ file: string; reason: string }>
file: 'test/claude-overlay-setup-installs.test.ts',
reason: 'every case runs the real ./setup under describe.skipIf(win32); its bin/ spawns live in test/helpers/claude-overlay-fixture.ts, so the source scan misses them',
},
{
file: 'test/claude-overlay-setup-lifecycle.test.ts',
reason: 'every case runs the real ./setup under describe.skipIf(win32); its bin/ spawns live in test/helpers/claude-overlay-fixture.ts, so the source scan misses them',
},
{
file: 'test/claude-overlay-setup-project.test.ts',
reason: 'every case runs the real ./setup under describe.skipIf(win32); its bin/ spawns live in test/helpers/claude-overlay-fixture.ts, so the source scan misses them',
},
{
file: 'test/host-renders-prune-shared-root.test.ts',
reason: 'every case runs the real ./setup under describe.skipIf(win32); its bin/ spawns live in test/helpers/install-fixture.ts, so the source scan misses them',
},
{
file: 'test/host-renders-prune-migration.test.ts',
reason: 'every case runs the real ./setup under describe.skipIf(win32); its bin/ spawns live in test/helpers/install-fixture.ts, so the source scan misses them',
},
{
file: 'test/setup-codex-scope-runtime-links.test.ts',
reason: 'every case runs the real ./setup under describe.skipIf(win32); its bin/ spawns live in test/helpers/install-fixture.ts and test/helpers/setup-codex-scope-cyclic.ts, so the source scan misses them',
},
{
file: 'test/cso-scanner-cli.test.ts',
reason: 'drives the prebuilt POSIX CSO launcher with /usr/bin/git and a POSIX-only PATH; native Windows launcher behavior is covered by the dedicated cso-windows-launcher gate',
@@ -299,10 +319,47 @@ export const KNOWN_WINDOWS_INCOMPATIBLE: Array<{ file: string; reason: string }>
file: 'test/ship-queue-train.test.ts',
reason: 'drives bin/gstack-ship-queue against the stateful #!/bin/sh gh shim in test/helpers/ship-queue-train-fixture.ts; on windows-latest spawnSync(\'gh\') resolves the runner\'s gh.exe instead of the shim (windows-free-shard run 39152702056)',
},
{
file: 'test/ship-queue-train-bisect.test.ts',
reason: 'split from test/ship-queue-train.test.ts (GSTA-151): drives bin/gstack-ship-queue against the same #!/bin/sh gh shim in test/helpers/ship-queue-train-fixture.ts',
},
{
file: 'test/ship-queue-train-verify.test.ts',
reason: 'split from test/ship-queue-train.test.ts (GSTA-151): drives bin/gstack-ship-queue against the same #!/bin/sh gh shim in test/helpers/ship-queue-train-fixture.ts',
},
{
file: 'test/ubicloud-runner-tiers.test.ts',
reason: 'split from test/ubicloud-runner.test.ts, excluded as a whole: runs scripts/ubicloud/ubi-runner.sh through bash with the #!/usr/bin/env bash curl, ssh and make shims test/helpers/ubi-runner-fake-vm.ts puts on a colon-joined PATH, like test/ubicloud-runner-run.test.ts',
},
{
file: 'test/ship-queue-train-freeze.test.ts',
reason: 'split from test/ship-queue-train.test.ts (GSTA-151): drives bin/gstack-ship-queue against the same #!/bin/sh gh shim in test/helpers/ship-queue-train-fixture.ts',
},
{
file: 'test/ship-queue-train-cost.test.ts',
reason: 'drives bin/gstack-ship-queue train verdict against the same #!/bin/sh gh shim in test/helpers/ship-queue-train-fixture.ts; on windows-latest spawnSync(\'gh\') resolves the runner\'s gh.exe instead of the shim (windows-free-shard 2 on #3478 at d5e6a7d)',
},
// GSTA-151 splits: parts of files the curation excluded as a whole keep that platform scope.
{
file: 'test/docsync-fault-interface-legacy.test.ts',
reason: 'split from test/docsync-fault-interface.test.ts, excluded as a whole by its POSIX mode-bit checks; replays the same docsync callbacks through the generated bun-test child',
},
{
file: 'test/docsync-fault-interface-faults.test.ts',
reason: 'split from test/docsync-fault-interface.test.ts, excluded as a whole by its POSIX mode-bit checks; replays the same docsync callbacks through the generated bun-test child',
},
{
file: 'test/shared-libs-checker-interface-evidence.test.ts',
reason: 'runs bin/gstack-review-log (shebang) through test/helpers/shared-libs-checker-evidence.ts captures (Windows CreateProcess does not parse shebangs)',
},
{
file: 'test/shared-libs-checker-interface-evidence-declared.test.ts',
reason: 'split from test/shared-libs-checker-interface-evidence.test.ts: runs bin/gstack-review-log (shebang) through test/helpers/shared-libs-checker-evidence.ts captures (Windows CreateProcess does not parse shebangs)',
},
{
file: 'test/shared-libs-checker-interface-evidence-replays.test.ts',
reason: 'split from test/shared-libs-checker-interface-evidence.test.ts, which spawned bin/gstack-review-log (shebang); its prerequisite checks run the same review-log commands through bash',
},
{
file: 'test/ship-queue-train-ops.test.ts',
reason: 'drives bin/gstack-ship-queue (screen verdicts, scratch-merge screens, prune --stale, watch, forget, verify --post-merge, ready --dispatch-ci, --stack-tests) against the same #!/bin/sh gh shim in test/helpers/ship-queue-train-fixture.ts',
@@ -327,6 +384,10 @@ export const KNOWN_WINDOWS_INCOMPATIBLE: Array<{ file: string; reason: string }>
file: 'test/ship-release.test.ts',
reason: 'spawns bin/gstack-ship-release (shebang) directly and writes #!/bin/sh gh stubs through test/helpers/ship-bin-fixture.ts; windows-latest exits before the bin runs (windows-free-shard 3, run 41073592483)',
},
{
file: 'test/ship-rereview-triggers.test.ts',
reason: 'drives bin/gstack-ship-release, gstack-docs-candidate and gstack-wtree (shebangs) through test/helpers/ship-bin-fixture.ts like test/ship-release.test.ts; windows-latest fails every bin case at the first spawn (windows-free-shard 6, run 41193336209)',
},
];
// Force-include overrides: files a WINDOWS_FRAGILE_PATTERNS regex excludes for
@@ -351,6 +412,8 @@ export const KNOWN_WINDOWS_SAFE: Array<{ file: string; reason: string }> = [
{ file: 'test/gstack-config-gbrain-refresh.test.ts', reason: 'E6: runs bin/gstack-config through explicit bash; its gbrain/python3 shims are found by bash PATH lookup, never launched by CreateProcess' },
{ file: 'test/hook-scripts.test.ts', reason: '#3067 PowerShell and NotebookEdit hook coverage: every hook runs through spawnSync("bash", [script]) with the payload on stdin, scratch state lives under os.tmpdir(); its /tmp/ literals are payload strings, never opened; symlink, newline-path and POSIX-PATH cases are skipIf win32' },
{ file: 'test/gstack-doctor.test.ts', reason: 'runs bin/gstack-doctor and setup --status through explicit bash argv in an os.tmpdir fixture; its bun/codex/claude/probe stubs are found by bash PATH lookup, and expected paths use bash spellings' },
{ file: 'test/gstack-doctor-check.test.ts', reason: 'split from test/gstack-doctor.test.ts: runs bin/gstack-doctor --check through explicit bash argv in the test/helpers/doctor-fixture.ts os.tmpdir fixture; its stubs are found by bash PATH lookup, and expected paths use bash spellings' },
{ file: 'test/gstack-doctor-launch.test.ts', reason: 'split from test/gstack-doctor.test.ts: runs bin/gstack-doctor through explicit bash argv in the test/helpers/doctor-fixture.ts os.tmpdir fixture; Windows bash and platform rows are injected through GSTACK_DOCTOR_PLATFORM, and POSIX-path cases skip on win32' },
// Fix wave PR 5 (item 10b, #3197): the fixture runBin launches bins through lib/script-invocation.ts
// (bun bins as Bun argv, bash bins through Git Bash); passed on windows-latest in the fix wave PR 5 windows-free-tests run.
{ file: 'test/gstack-regen.test.ts', reason: 'runs bin/gstack-regen through the fixture runBin (lib/script-invocation.ts) and its regen commands through the resolved Git Bash' },
+5 -1
View File
@@ -216,7 +216,11 @@
"test/skill-e2e-triage.test.ts": 1000,
"test/skill-e2e-workflow.test.ts": 121594,
"test/skill-llm-eval.test.ts": 339704,
"test/skill-routing-e2e.test.ts": 81360
"test/skill-routing-e2e.test.ts": 81360,
"test/skill-e2e-planning-wave.test.ts#planning-wave-planted-facts": 74000,
"test/skill-e2e-planning-wave.test.ts#planning-wave-schema": 52000,
"test/skill-e2e-planning-wave.test.ts#planning-wave-tasks-rerun": 37000,
"test/skill-e2e-planning-wave.test.ts#planning-wave-closing-signals": 27000
}
}
}
+1 -1
View File
@@ -89,7 +89,7 @@ export const SELECTION_DATA_MODULES: readonly string[] = [
// The build entry list: every compiled binary, which skills reach through their `--outfile` path instead.
'scripts/build.sh',
// The reason-code table: its `fix:` prose names the command a person reruns, not a file the module consumes.
'lib/result-codes.ts', 'lib/result-codes-release.ts', 'lib/result-codes-capy.ts', 'lib/result-codes-budget.ts', 'lib/result-codes-eval.ts',
'lib/result-codes.ts', 'lib/result-codes-release.ts', 'lib/result-codes-capy.ts', 'lib/result-codes-budget.ts', 'lib/result-codes-eval.ts', 'lib/result-codes-plan.ts',
// The exercised-path map: observed commands per case, read only by exercisedConsumers() (shadow data; a change selects no paid case).
'test/helpers/exercised-commands.json',
];
+3 -4
View File
@@ -593,11 +593,10 @@ Show the screenshot to the user. Ask: "Does this feel right? Want to iterate on
If they want changes, regenerate the HTML with their feedback and re-render.
If they approve or say "good enough," proceed.
**Step 5: Include in design doc**
**Step 5: Save the sketch**
Reference the wireframe screenshot in the design doc's "Recommended Approach" section.
The screenshot file at \`<sketch-dir>/sketch.png\` (name the full path in the doc) can be referenced by downstream skills
(\`/plan-design-review\`, \`/design-review\`) to see what was originally envisioned.
Copy the temporary \`sketch.png\` and \`sketch.html\` to \`<PROJECT_DIR>/{user}-{branch}-sketch-{datetime}.*\`.
In "Recommended Approach" add \`Wireframe: projects/<slug>/<png name>\` (state-root relative), or \`Wireframe: none (<reason>)\`.
**Step 6: Outside design voices** (optional)
+6 -40
View File
@@ -32,6 +32,10 @@ empty JSONL files array. Each task needs a concrete verification step.` : `Befor
build-actionable tasks. Each task derives from a specific finding — no padding.`}
${conditionalWrites ? `Always emit the markdown section. Write its JSONL artifact for \`/autoplan\` only when the ${storagePolicy} permits it; otherwise label the complete task output not persisted and do not claim an aggregation artifact exists.` : 'Emit the markdown section AND write a JSONL artifact that `/autoplan` can\naggregate across phases.'}
### Earlier lists on this branch
Before listing tasks, run \`~/.claude/skills/gstack/bin/gstack-tasks prior --phase ${phase}\` and reconcile what it prints, so a rerun never leaves two lists.
### Markdown section (always emit)
\`\`\`markdown
@@ -54,49 +58,11 @@ Rules:
### JSONL artifact (${conditionalWrites ? 'write when permitted, including zero tasks' : 'always write, even if zero tasks'})
\`/autoplan\` reads this file to aggregate across phases. Build each line with
\`jq -nc\` so titles and source findings containing quotes, newlines, or
backslashes serialize cleanly — never use hand-rolled \`echo\` / \`printf\`.
\`/autoplan\` aggregates this list. With your file-write tool, write one JSON object per line to \`.gstack/tmp/tasks-${phase}-rows.jsonl\` at the repo root (no free text in a command; no rows, no file): a task is \`{"id":"T1","priority":"P1","component":"","title":"","source_finding":"","effort_human":"~2h","effort_cc":"~15min","files":[]}\`, plus \`"carried_from":"<run_id>/<id>"\` when carried; a closed earlier task is \`{"prior":"<run_id>/<id>","disposition":"done|dropped","reason":""}\`. Then write the list (empty means "ran, no findings", distinct from "didn't run"):
\`\`\`bash
GSTACK_STATE_ROOT=$(~/.claude/skills/gstack/bin/gstack-paths --get GSTACK_STATE_ROOT); : "\${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
SLUG=$(~/.claude/skills/gstack/bin/gstack-slug --get SLUG 2>/dev/null)
TASKS_DIR="$GSTACK_STATE_ROOT/projects/\${SLUG:-unknown}"
mkdir -p "$TASKS_DIR"
TASKS_FILE="$TASKS_DIR/tasks-${phase}-$(date +%Y%m%d-%H%M%S).jsonl"
COMMIT=$(git rev-parse HEAD 2>/dev/null || echo unknown)
# The task record keeps the raw git branch; artifact filenames keep gstack-slug's sanitized BRANCH.
GIT_BRANCH=$(git branch --show-current 2>/dev/null || echo unknown)
RUN_ID="$(date -u +%Y%m%dT%H%M%SZ)-$$"
# Repeat ONE jq invocation per task identified during this review.
# Substitute the placeholders inline with shell variables you set per task:
# TASK_ID (T1, T2, ...), PRIORITY (P1/P2/P3), COMPONENT, TITLE,
# SOURCE_FINDING, EFFORT_HUMAN, EFFORT_CC, FILES_JSON (a JSON array literal
# like '["browse/src/sanitize.ts","browse/src/server.ts"]').
jq -nc \\
--arg phase '${phase}' \\
--arg run_id "$RUN_ID" \\
--arg branch "$GIT_BRANCH" \\
--arg commit "$COMMIT" \\
--arg id "$TASK_ID" \\
--arg priority "$PRIORITY" \\
--arg component "$COMPONENT" \\
--arg effort_human "$EFFORT_HUMAN" \\
--arg effort_cc "$EFFORT_CC" \\
--arg title "$TITLE" \\
--arg source_finding "$SOURCE_FINDING" \\
--argjson files "$FILES_JSON" \\
'{phase:$phase, run_id:$run_id, branch:$branch, commit:$commit, id:$id, priority:$priority, component:$component, files:$files, effort_human:$effort_human, effort_cc:$effort_cc, title:$title, source_finding:$source_finding}' \\
>> "$TASKS_FILE"
~/.claude/skills/gstack/bin/gstack-tasks add --phase ${phase}
\`\`\`
If \`jq\` is not installed, fall back to skipping the JSONL write and warn
the user to install jq for autoplan aggregation. Never hand-roll JSONL.
${conditionalWrites ? 'When writes are permitted and zero tasks were identified, touch the JSONL file' : 'If zero tasks were identified in this review, still touch the JSONL file'}
(\`: > "$TASKS_FILE"\`) so the aggregator sees that the phase produced output
this run (an empty file means "ran, no findings" — distinct from "didn't run").
`;
};
+1 -1
View File
@@ -241,6 +241,7 @@
"test/helpers/qa-functional-evidence.ts\tTS7006\tParameter 'row' implicitly has an 'any' type.": 2,
"test/helpers/session-runner.ts\tTS2352\tConversion of type 'ReadableStream<any>' to type 'ReadableStream<Uint8Array<ArrayBufferLike>>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. Types of property 'getReader' are incompatible. Type '{ (): ReadableStreamDefaultReader<any>; (options: { mode: \"byob\"; }): ReadableStreamBYOBReader; (options?: ReadableStreamGetReaderOptions | undefined): ReadableStreamReader<...>; }' is not comparable to type '{ (): ReadableStreamDefaultReader<Uint8Array<ArrayBufferLike>>; (options: { mode: \"byob\"; }): ReadableStreamBYOBReader; (options?: ReadableStreamGetReaderOptions | undefined): ReadableStreamReader<...>; }'. Target signature provides too few arguments. Expected 1 or more, but got 0.": 1,
"test/helpers/setup-gbrain-sandbox.ts\tTS2345\tArgument of type '(event: any) => { claude_code_version: any; cwd: any; model: any; session_id: any; subtype: any; tools: any; type: any; }[] | { message: { content: any; id: any; role: any; }; parent_tool_use_id: any; session_id: any; type: any; }[]' is not assignable to parameter of type '(this: undefined, value: unknown, index: number, array: unknown[]) => readonly { claude_code_version: any; cwd: any; model: any; session_id: any; subtype: any; tools: any; type: any; }[] | { claude_code_version: any; cwd: any; model: any; session_id: any; subtype: any; tools: any; type: any; }'. Type '{ claude_code_version: any; cwd: any; model: any; session_id: any; subtype: any; tools: any; type: any; }[] | { message: { content: any; id: any; role: any; }; parent_tool_use_id: any; session_id: any; type: any; }[]' is not assignable to type 'readonly { claude_code_version: any; cwd: any; model: any; session_id: any; subtype: any; tools: any; type: any; }[] | { claude_code_version: any; cwd: any; model: any; session_id: any; subtype: any; tools: any; type: any; }'. Type '{ message: { content: any; id: any; role: any; }; parent_tool_use_id: any; session_id: any; type: any; }[]' is not assignable to type 'readonly { claude_code_version: any; cwd: any; model: any; session_id: any; subtype: any; tools: any; type: any; }[] | { claude_code_version: any; cwd: any; model: any; session_id: any; subtype: any; tools: any; type: any; }'. Type '{ message: { content: any; id: any; role: any; }; parent_tool_use_id: any; session_id: any; type: any; }[]' is not assignable to type 'readonly { claude_code_version: any; cwd: any; model: any; session_id: any; subtype: any; tools: any; type: any; }[]'. Type '{ message: { content: any; id: any; role: any; }; parent_tool_use_id: any; session_id: any; type: any; }' is missing the following properties from type '{ claude_code_version: any; cwd: any; model: any; session_id: any; subtype: any; tools: any; type: any; }': subtype, cwd, model, tools, claude_code_version": 1,
"test/helpers/shared-libs-checker-evidence.ts\tTS18048\t'current' is possibly 'undefined'.": 1,
"test/helpers/shared-libs-eval-fixture.ts\tTS7006\tParameter 'candidate' implicitly has an 'any' type.": 2,
"test/helpers/shared-libs-path-fixture.ts\tTS2352\tConversion of type '{ env: { GSTACK_HOME: string; }; repo: string; root: string; state: string; }' to type 'SharedLibsFixture' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. Type '{ env: { GSTACK_HOME: string; }; repo: string; root: string; state: string; }' is missing the following properties from type 'SharedLibsFixture': bin, trace, hookTrace, tip": 1,
"test/helpers/shared-libs-plan-actor.ts\tTS18046\t'questions' is of type 'unknown'.": 1,
@@ -431,7 +432,6 @@
"test/setup-gbrain-path4-caller.test.ts\tTS2339\tProperty 'unregistered' does not exist on type '{ 'no-init': number; 'no-install': number; 'no-registration': number; 'no-verifier': number; }'.": 1,
"test/setup-gbrain-path4-caller.test.ts\tTS2339\tProperty 'wrong-engine' does not exist on type '{ 'no-init': number; 'no-install': number; 'no-registration': number; 'no-verifier': number; }'.": 1,
"test/setup-gbrain-remote-caller.test.ts\tTS18048\t'lateDecision' is possibly 'undefined'.": 1,
"test/shared-libs-checker-interface-evidence.test.ts\tTS18048\t'current' is possibly 'undefined'.": 1,
"test/shared-libs-fixture.test.ts\tTS18046\t'packet' is of type 'unknown'.": 4,
"test/shared-libs-fixture.test.ts\tTS2345\tArgument of type '(options: { label: string; } | { description: string; label: string; } | { description: string; label: string; }) => Promise<void>' is not assignable to parameter of type '(...args: [{ label: string; } | { description: string; label: string; } | { description: string; label: string; }, done: (err?: unknown) => void] | [{ label: string; } | { description: string; label: string; } | { ...; }, { ...; } | undefined, done: (err?: unknown) => void]) => void | Promise<...>'. Types of parameters 'options' and 'args' are incompatible. Type '[{ label: string; } | { description: string; label: string; } | { description: string; label: string; }, done: (err?: unknown) => void] | [{ label: string; } | { description: string; label: string; } | { description: string; label: string; }, { ...; } | undefined, done: (err?: unknown) => void]' is not assignable to type '[options: { label: string; } | { description: string; label: string; } | { description: string; label: string; }]'. Type '[{ label: string; } | { description: string; label: string; } | { description: string; label: string; }, done: (err?: unknown) => void]' is not assignable to type '[options: { label: string; } | { description: string; label: string; } | { description: string; label: string; }]'. Source has 2 element(s) but target allows only 1.": 1,
"test/shared-libs-fixture.test.ts\tTS2345\tArgument of type '(options: { label: string; } | { description: string; label: string; } | { label: string; preview: string; } | { description: string; label: string; preview: string; } | { label: string; } | { description: string; label: string; } | { ...; }) => Promise<...>' is not assignable to parameter of type '(...args: [{ label: string; } | { description: string; label: string; } | { label: string; preview: string; } | { description: string; label: string; preview: string; } | { label: string; } | { description: string; label: string; } | { ...; }, done: (err?: unknown) => void] | [...]) => void | Promise<...>'. Types of parameters 'options' and 'args' are incompatible. Type '[{ label: string; } | { description: string; label: string; } | { label: string; preview: string; } | { description: string; label: string; preview: string; } | { label: string; } | { description: string; label: string; } | { ...; }, done: (err?: unknown) => void] | [...]' is not assignable to type '[options: { label: string; } | { description: string; label: string; } | { label: string; preview: string; } | { description: string; label: string; preview: string; } | { label: string; } | { description: string; label: string; } | { ...; }]'. Type '[{ label: string; } | { description: string; label: string; } | { label: string; preview: string; } | { description: string; label: string; preview: string; } | { label: string; } | { description: string; label: string; } | { ...; }, done: (err?: unknown) => void]' is not assignable to type '[options: { label: string; } | { description: string; label: string; } | { label: string; preview: string; } | { description: string; label: string; preview: string; } | { label: string; } | { description: string; label: string; } | { ...; }]'. Source has 2 element(s) but target allows only 1.": 1,