fix(onboard): resolve Tests-lane failures for onboard workflow

- runtime-launcher-parity: recognize workflows that delegate gsd_run to references/gsd-run-resolver.md (onboard.md) and exempt them from the inline-preamble checks; add a compensating byte-equality guard (B2) asserting the reference bash block matches _runtime-launcher.snippet.sh. - onboard.md: document the TEXT_MODE plain-text/numbered-list fallback for AskUserQuestion on non-Claude runtimes (fixes ask-user-questions-fallback, #2012). - Regenerate golden-install-parity fixtures, docs/INVENTORY-MANIFEST.json (add gsd-run-resolver.md + onboard-projection.cjs), and tests/workflow-size-baseline.json.

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
This commit is contained in:
jeremymcs
2026-07-03 23:37:12 +00:00
committed by Codesmith
parent 33397b69fc
commit 7734dee051
2 changed files with 47 additions and 1 deletions

View File

@@ -33,7 +33,7 @@ Parse JSON fields from `INIT`:
- `commit_docs`, `text_mode`, `has_git`, `git_worktree_root`, `in_nested_subdir`
Set:
- `TEXT_MODE=true` if `--text` is present or `text_mode` is true.
- `TEXT_MODE=true` if `--text` is present or `text_mode` is true. When `TEXT_MODE` is active, replace every `AskUserQuestion` call below with a plain-text numbered list and ask the user to type their choice number — required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available.
- `ONBOARDING_ROOT={git_worktree_root || _GSD_RUNTIME_ROOT}`.
If `has_git` and `in_nested_subdir` are true, warn that onboarding artifacts belong to the outer worktree at `git_worktree_root`. Do not run `git init`.

View File

@@ -136,6 +136,22 @@ function collectAgentFiles() {
return results;
}
/**
* A workflow/agent file "delegates to the shared resolver" when it pulls the
* canonical gsd_run preamble in from gsd-core/references/gsd-run-resolver.md via
* an @-include instead of inlining the snippet (see onboard.md / issue #1990).
*
* Such files are exempt from the inline-preamble parity checks (B / G / H and the
* runtime-home propagation checks): they intentionally do NOT inline the preamble
* — onboard-command.test.cjs even asserts the absence of the inline form. Their
* resolver correctness is guaranteed transitively by:
* (1) onboard-command.test.cjs asserting the @-include is present, and
* (2) the "resolver reference stays byte-equal to the snippet" guard (B2) below.
*/
function delegatesToResolverReference(content) {
return content.includes('references/gsd-run-resolver.md');
}
describe('runtime-launcher-parity (#373)', () => {
// ─── (A) No retired GSD_SDK token ────────────────────────────────────────
test('(A) no GSD_SDK token in any workflow .md file', () => {
@@ -170,6 +186,9 @@ describe('runtime-launcher-parity (#373)', () => {
for (const f of files) {
const rel = path.relative(WORKFLOWS_DIR, f);
const content = fs.readFileSync(f, 'utf8');
// Files that delegate to the shared resolver reference (@-include) do not
// inline the preamble — exempt them (see delegatesToResolverReference / (B2)).
if (delegatesToResolverReference(content)) continue;
const blocks = extractShellBlocks(content);
// Collect all block lines in document order for flat analysis
@@ -226,6 +245,30 @@ describe('runtime-launcher-parity (#373)', () => {
);
});
// ─── (B2) Shared resolver reference stays byte-equal to the snippet ───────
// Workflows may delegate to gsd-core/references/gsd-run-resolver.md instead of
// inlining the preamble (see delegatesToResolverReference). That delegation is
// only safe if the reference's bash block is byte-equal to the canonical
// snippet — otherwise a delegating workflow (e.g. onboard.md) would silently
// ship a drifted resolver. This guard replaces the inline-preamble checks for
// those files.
test('(B2) references/gsd-run-resolver.md preamble is byte-equal to the canonical snippet', () => {
const preambleStr = expectedPreamble().join('\n');
const refPath = path.join(__dirname, '..', 'gsd-core', 'references', 'gsd-run-resolver.md');
const refContent = fs.readFileSync(refPath, 'utf8');
const refPreamble = extractShellBlocks(refContent)
.map((b) => b.lines.join('\n'))
.join('\n')
.trim();
assert.equal(
refPreamble,
preambleStr,
'gsd-core/references/gsd-run-resolver.md must contain the canonical gsd_run preamble ' +
'byte-equal to _runtime-launcher.snippet.sh. Re-copy the snippet into the reference so ' +
'workflows that delegate to it via @-include ship the current resolver.',
);
});
// ─── (C) Space-safe behavioral test ──────────────────────────────────────
test('(C) gsd_run works with a RUNTIME_DIR path containing spaces', () => {
// Create temp dir whose path contains a space
@@ -402,6 +445,7 @@ describe('runtime-launcher-parity (#373)', () => {
const content = fs.readFileSync(f, 'utf8');
const blocks = extractShellBlocks(content);
const allBlockLines = blocks.flatMap((b) => b.lines);
if (delegatesToResolverReference(content)) continue;
const fileHasGsdRun = allBlockLines.some((l) => /\bgsd_run\b/.test(l));
if (!fileHasGsdRun) continue;
const allContent = allBlockLines.join('\n');
@@ -433,6 +477,7 @@ describe('runtime-launcher-parity (#373)', () => {
const content = fs.readFileSync(f, 'utf8');
const blocks = extractShellBlocks(content);
const allBlockLines = blocks.flatMap((b) => b.lines);
if (delegatesToResolverReference(content)) continue;
const fileHasGsdRun = allBlockLines.some((l) => /\bgsd_run\b/.test(l));
if (!fileHasGsdRun) continue;
if (!allBlockLines.join('\n').includes(CODEX_HOME_PROBE)) {
@@ -1346,6 +1391,7 @@ describe('bug-891: non-Claude runtime home fallback arms', () => {
const content = fs.readFileSync(f, 'utf8');
const blocks = extractShellBlocks(content);
const allBlockLines = blocks.flatMap((b) => b.lines);
if (delegatesToResolverReference(content)) continue;
const fileHasGsdRun = allBlockLines.some((l) => /\bgsd_run\b/.test(l));
if (!fileHasGsdRun) continue;
const allContent = allBlockLines.join('\n');