* fix(#2494): capture stderr and guard empty output on the claude and gemini reviewer legs The gemini and claude blocks in gsd-core/workflows/review.md were the only two of the ten prompt-fed reviewer legs with both `2>/dev/null` and no empty-output guard. Any failure that wrote no stdout — CLI missing, unauthenticated, rate-limited, crashed — left a zero-byte review file with the only diagnostic evidence already discarded. write_reviews substitutes each file's raw content into a `## <Reviewer> Review` section with no marker separating "empty/failed" from "ran cleanly, nothing to report", so a failed lane silently degraded the advertised N-reviewer consensus to N-1 while present_results reported success. Both /gsd:review and /gsd:plan-review-convergence share the invoke_reviewers step, so both were affected. Both legs now redirect stderr to a `.err` sidecar and write a diagnostic stub with the captured stderr appended when the review file comes back empty — the same shape the codex and cursor legs already use. Scope limit, noted in the block comment: the guard only runs if the block itself completes. A host Bash-tool timeout that kills the whole block skips it, the same hard bound the OpenCode block already documents. The existing timeout guidance (#2194) covers that case and is unchanged. Regression test extracts the two dispatch blocks verbatim from the workflow and runs them under bash against a failing CLI stub, asserting the review file is non-empty and carries a diagnosable message plus the captured stderr. It fails against pre-fix review.md (4 of 5 cases). Golden install-parity fixtures and the workflow size baseline are regenerated: one review.md hash per fixture, one size entry. Fixes #2494 * fix(#2494): stamp the changeset fragment with the filed PR number The fragment was authored with the documented `pr: 0` placeholder because the PR number does not exist until `gh pr create` returns. Now that this PR is #2592, stamp it — scripts/changeset/parse.cjs requires pr > 0, so the placeholder would fail the Changeset Required check.
This commit is contained in:
5
.changeset/2494-review-claude-gemini-empty-guard.md
Normal file
5
.changeset/2494-review-claude-gemini-empty-guard.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Fixed
|
||||
pr: 2592
|
||||
---
|
||||
**The Gemini and Claude reviewer legs now fail loudly instead of silently dropping out of the cross-AI review** — both blocks capture stderr to a `.err` sidecar instead of discarding it to `/dev/null`, and write a diagnostic stub with the captured error when the lane produces no output. Previously they were the only two of the ten prompt-fed reviewer legs with neither guard, so any failure that wrote no stdout (CLI missing, unauthenticated, rate-limited, crashed) left a zero-byte review file that `write_reviews` rendered as a reviewer that had run cleanly with nothing to report — quietly degrading an N-reviewer consensus to N-1 while `present_results` reported success. The guard matches the shape the Codex and Cursor legs already use. (#2494)
|
||||
@@ -277,19 +277,39 @@ For each selected CLI, invoke in sequence (not parallel — avoid rate limits):
|
||||
|
||||
**Gemini:**
|
||||
```bash
|
||||
# #2494: capture stderr to a .err sidecar (not /dev/null) and stub an empty
|
||||
# result, mirroring the Codex and Cursor blocks. Without the guard a failed
|
||||
# lane — CLI missing, unauthenticated, rate-limited, crashed, or any exit that
|
||||
# writes no stdout — leaves a zero-byte file that write_reviews renders as a
|
||||
# reviewer that ran cleanly with nothing to report, silently dropping a lane
|
||||
# from the cross-AI consensus while present_results reports success.
|
||||
# Scope limit: the guard only runs if this block completes. A host Bash-tool
|
||||
# timeout that kills the whole block skips it, the same hard bound the
|
||||
# OpenCode block documents below — per the timeout guidance above, treat an
|
||||
# empty result on a slow lane as a dropped lane rather than a crash.
|
||||
if [ -n "$GEMINI_MODEL" ] && [ "$GEMINI_MODEL" != "null" ]; then
|
||||
cat {run_dir}/gsd-review-prompt.md | gemini -m "$GEMINI_MODEL" -p - 2>/dev/null > {run_dir}/gsd-review-gemini.md
|
||||
cat {run_dir}/gsd-review-prompt.md | gemini -m "$GEMINI_MODEL" -p - 2>{run_dir}/gsd-review-gemini.err > {run_dir}/gsd-review-gemini.md
|
||||
else
|
||||
cat {run_dir}/gsd-review-prompt.md | gemini -p - 2>/dev/null > {run_dir}/gsd-review-gemini.md
|
||||
cat {run_dir}/gsd-review-prompt.md | gemini -p - 2>{run_dir}/gsd-review-gemini.err > {run_dir}/gsd-review-gemini.md
|
||||
fi
|
||||
if [ ! -s {run_dir}/gsd-review-gemini.md ]; then
|
||||
echo "Gemini review failed or returned empty output. stderr:" > {run_dir}/gsd-review-gemini.md
|
||||
cat {run_dir}/gsd-review-gemini.err >> {run_dir}/gsd-review-gemini.md
|
||||
fi
|
||||
```
|
||||
|
||||
**Claude (separate session):**
|
||||
```bash
|
||||
# #2494: same guard as the Gemini block above — stderr to a .err sidecar
|
||||
# instead of /dev/null, and a diagnostic stub when the lane produces nothing.
|
||||
if [ -n "$CLAUDE_MODEL" ] && [ "$CLAUDE_MODEL" != "null" ]; then
|
||||
cat {run_dir}/gsd-review-prompt.md | claude --model "$CLAUDE_MODEL" $CLAUDE_EFFORT_ARGS -p - 2>/dev/null > {run_dir}/gsd-review-claude.md
|
||||
cat {run_dir}/gsd-review-prompt.md | claude --model "$CLAUDE_MODEL" $CLAUDE_EFFORT_ARGS -p - 2>{run_dir}/gsd-review-claude.err > {run_dir}/gsd-review-claude.md
|
||||
else
|
||||
cat {run_dir}/gsd-review-prompt.md | claude $CLAUDE_EFFORT_ARGS -p - 2>/dev/null > {run_dir}/gsd-review-claude.md
|
||||
cat {run_dir}/gsd-review-prompt.md | claude $CLAUDE_EFFORT_ARGS -p - 2>{run_dir}/gsd-review-claude.err > {run_dir}/gsd-review-claude.md
|
||||
fi
|
||||
if [ ! -s {run_dir}/gsd-review-claude.md ]; then
|
||||
echo "Claude review failed or returned empty output. stderr:" > {run_dir}/gsd-review-claude.md
|
||||
cat {run_dir}/gsd-review-claude.err >> {run_dir}/gsd-review-claude.md
|
||||
fi
|
||||
```
|
||||
|
||||
|
||||
204
tests/fix-2494-review-claude-gemini-empty-guard.test.cjs
Normal file
204
tests/fix-2494-review-claude-gemini-empty-guard.test.cjs
Normal file
@@ -0,0 +1,204 @@
|
||||
// allow-test-rule: source-text-is-the-product (see #2494)
|
||||
// The Gemini and Claude reviewer dispatch blocks in gsd-core/workflows/review.md
|
||||
// ARE the runtime contract — the workflow's text is what the reviewing agent
|
||||
// executes. This suite extracts those two shell blocks verbatim from the
|
||||
// workflow and runs them under a real bash against a failing CLI stub, so the
|
||||
// shipped guard is what gets exercised rather than a reimplementation of it.
|
||||
// The assertions on the produced review file are assertions on that guard's
|
||||
// documented output contract (the stub line the consensus step must be able to
|
||||
// tell apart from a clean empty review), not incidental string matching.
|
||||
|
||||
/**
|
||||
* Regression tests for #2494 — the claude and gemini reviewer legs discarded
|
||||
* stderr with no empty-output guard.
|
||||
*
|
||||
* Before the fix both blocks were `... 2>/dev/null > {run_dir}/gsd-review-<leg>.md`
|
||||
* with nothing after: a non-zero exit that wrote no stdout (CLI missing,
|
||||
* unauthenticated, rate-limited, timeout-killed, crashed) left a ZERO-BYTE
|
||||
* review file with the only diagnostic evidence — stderr — already discarded.
|
||||
* The write_reviews step then substituted that empty file into a
|
||||
* `## <Reviewer> Review` section indistinguishable from "ran cleanly, nothing
|
||||
* to report", silently degrading the advertised N-reviewer consensus to N-1.
|
||||
*
|
||||
* These tests fail against pre-fix review.md: the produced file is zero-byte,
|
||||
* so both the non-empty and the diagnosable-message assertions trip.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const { describe, test, before, after } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const { spawnSync } = require('node:child_process');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const { createTempDir, cleanup } = require('./helpers.cjs');
|
||||
|
||||
const ROOT = path.join(__dirname, '..');
|
||||
const REVIEW_PATH = path.join(ROOT, 'gsd-core', 'workflows', 'review.md');
|
||||
|
||||
// Normalize CRLF: on a Windows git-autocrlf checkout every line carries a
|
||||
// trailing \r, which would leave the extracted block's shebang/redirect tokens
|
||||
// mangled and defeat the fence regexes below.
|
||||
const WORKFLOW = fs.readFileSync(REVIEW_PATH, 'utf-8').replace(/\r\n/g, '\n');
|
||||
|
||||
// This suite executes the extracted blocks with a real bash and a POSIX CLI
|
||||
// stub on PATH. Gate to non-Windows, mirroring the opencode reconstruction
|
||||
// suite's win32 skip — the guard logic is platform-independent and is asserted
|
||||
// in full on every macOS/Linux CI leg.
|
||||
const skipReason = process.platform === 'win32'
|
||||
? 'extracted block is POSIX shell; guard logic is platform-independent and asserted on macOS/Linux'
|
||||
: false;
|
||||
|
||||
/**
|
||||
* Extract a reviewer dispatch block verbatim from the workflow. If review.md
|
||||
* changes the block's shape these throw and the test fails loudly — intended
|
||||
* coupling, the same contract the opencode suite pins for its jq programs.
|
||||
*/
|
||||
function extractBlock(headingRe, label) {
|
||||
const re = new RegExp(`${headingRe}\\n\`\`\`bash\\n([\\s\\S]*?)\\n\`\`\``);
|
||||
const m = WORKFLOW.match(re);
|
||||
assert.ok(m, `review.md must define the ${label} reviewer dispatch as a bash block (#2494)`);
|
||||
return m[1];
|
||||
}
|
||||
|
||||
const GEMINI_BLOCK = extractBlock('\\*\\*Gemini:\\*\\*', 'Gemini');
|
||||
const CLAUDE_BLOCK = extractBlock('\\*\\*Claude \\(separate session\\):\\*\\*', 'Claude');
|
||||
|
||||
const STUB_STDERR = 'gsd-2494-stub: command not found / not authenticated';
|
||||
|
||||
let sandbox;
|
||||
|
||||
before(() => {
|
||||
sandbox = createTempDir('gsd-2494-');
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cleanup(sandbox);
|
||||
});
|
||||
|
||||
/**
|
||||
* Run one extracted block with `{run_dir}` pointed at a fresh run directory and
|
||||
* `stubBody` installed on PATH under the reviewer's binary name.
|
||||
*
|
||||
* Single exec site for the whole suite so the Windows guard lives in one place:
|
||||
* Git Bash (msys2) ignores Node's chmod exec bit for PATH-executed
|
||||
* extension-less scripts (DEFECT.WINDOWS-TEST-PORTABILITY), and every suite
|
||||
* below is skipped on win32 — this early return keeps the exec unreachable
|
||||
* there rather than relying on the skip alone.
|
||||
*/
|
||||
function runBlockWithStub({ binName, block, stubBody, env = {} }) {
|
||||
if (process.platform === 'win32') return null;
|
||||
|
||||
const caseDir = fs.mkdtempSync(path.join(sandbox, 'run-'));
|
||||
const runDir = path.join(caseDir, 'run');
|
||||
const binDir = path.join(caseDir, 'bin');
|
||||
fs.mkdirSync(runDir);
|
||||
fs.mkdirSync(binDir);
|
||||
|
||||
const stub = path.join(binDir, binName);
|
||||
fs.writeFileSync(stub, stubBody);
|
||||
fs.chmodSync(stub, 0o755);
|
||||
|
||||
fs.writeFileSync(path.join(runDir, 'gsd-review-prompt.md'), '# review prompt\n');
|
||||
|
||||
const script = block.split('{run_dir}').join(runDir);
|
||||
const result = spawnSync('bash', ['-c', script], {
|
||||
encoding: 'utf8',
|
||||
timeout: 30000,
|
||||
killSignal: 'SIGKILL',
|
||||
env: { ...process.env, PATH: `${binDir}${path.delimiter}${process.env.PATH}`, ...env },
|
||||
});
|
||||
|
||||
const reviewPath = path.join(runDir, `gsd-review-${binName}.md`);
|
||||
return {
|
||||
result,
|
||||
reviewPath,
|
||||
errPath: path.join(runDir, `gsd-review-${binName}.err`),
|
||||
review: fs.existsSync(reviewPath) ? fs.readFileSync(reviewPath, 'utf-8') : null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The issue's own repro harness: a CLI that writes STUB_STDERR to stderr,
|
||||
* nothing to stdout, and exits non-zero.
|
||||
*/
|
||||
function runLeg({ binName, block, env = {} }) {
|
||||
return runBlockWithStub({
|
||||
binName,
|
||||
block,
|
||||
stubBody: `#!/bin/sh\necho "${STUB_STDERR}" >&2\nexit 127\n`,
|
||||
env,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The guard's contract: the review file exists, is NOT empty, names the leg as
|
||||
* failed-or-empty, and carries the captured stderr. The last assertion is what
|
||||
* separates this fix from a bare `[ ! -s ]` stub — the evidence has to survive.
|
||||
*/
|
||||
function assertDiagnosable({ review, reviewPath, errPath }, legLabel) {
|
||||
assert.ok(review !== null, `${legLabel}: review file must exist after a failed lane (#2494)`);
|
||||
assert.notStrictEqual(review.trim(), '', `${legLabel}: review file must not be empty after a failed lane (#2494)`);
|
||||
assert.match(
|
||||
review,
|
||||
new RegExp(`${legLabel} review failed or returned empty output`, 'i'),
|
||||
`${legLabel}: review file must carry a diagnosable failure line, distinguishable at consensus synthesis from "ran cleanly, nothing to report" (#2494)`,
|
||||
);
|
||||
assert.ok(
|
||||
review.includes(STUB_STDERR),
|
||||
`${legLabel}: captured stderr must be appended to the review file, not discarded to /dev/null (#2494)`,
|
||||
);
|
||||
assert.ok(fs.existsSync(errPath), `${legLabel}: stderr must be captured to a .err sidecar (#2494)`);
|
||||
assert.ok(reviewPath.endsWith('.md'), `${legLabel}: review output path unchanged`);
|
||||
}
|
||||
|
||||
describe('#2494 — gemini reviewer leg fails loudly', { skip: skipReason }, () => {
|
||||
test('a failing gemini CLI produces a diagnosable stub, not an empty file (model configured)', () => {
|
||||
const out = runLeg({ binName: 'gemini', block: GEMINI_BLOCK, env: { GEMINI_MODEL: 'gemini-test-model' } });
|
||||
assertDiagnosable(out, 'Gemini');
|
||||
});
|
||||
|
||||
test('a failing gemini CLI produces a diagnosable stub, not an empty file (no model configured)', () => {
|
||||
const out = runLeg({ binName: 'gemini', block: GEMINI_BLOCK, env: { GEMINI_MODEL: '' } });
|
||||
assertDiagnosable(out, 'Gemini');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#2494 — claude reviewer leg fails loudly', { skip: skipReason }, () => {
|
||||
test('a failing claude CLI produces a diagnosable stub, not an empty file (model configured)', () => {
|
||||
const out = runLeg({
|
||||
binName: 'claude',
|
||||
block: CLAUDE_BLOCK,
|
||||
env: { CLAUDE_MODEL: 'claude-test-model', CLAUDE_EFFORT_ARGS: '' },
|
||||
});
|
||||
assertDiagnosable(out, 'Claude');
|
||||
});
|
||||
|
||||
test('a failing claude CLI produces a diagnosable stub, not an empty file (no model configured)', () => {
|
||||
const out = runLeg({
|
||||
binName: 'claude',
|
||||
block: CLAUDE_BLOCK,
|
||||
env: { CLAUDE_MODEL: '', CLAUDE_EFFORT_ARGS: '' },
|
||||
});
|
||||
assertDiagnosable(out, 'Claude');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#2494 — the guard does not swallow a successful review', { skip: skipReason }, () => {
|
||||
test('gemini stdout is preserved verbatim when the CLI succeeds', () => {
|
||||
const out = runBlockWithStub({
|
||||
binName: 'gemini',
|
||||
block: GEMINI_BLOCK,
|
||||
stubBody: '#!/bin/sh\necho "## Real Review"\necho "Looks good."\nexit 0\n',
|
||||
env: { GEMINI_MODEL: '' },
|
||||
});
|
||||
|
||||
const { review } = out;
|
||||
assert.ok(review !== null, 'a successful review must produce a review file (#2494)');
|
||||
assert.ok(review.includes('Looks good.'), 'a successful review must pass through untouched (#2494)');
|
||||
assert.ok(
|
||||
!/failed or returned empty output/i.test(review),
|
||||
'the stub must not fire on a non-empty review (#2494)',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -299,7 +299,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "23b9eb0858a2535e",
|
||||
"gsd-core/workflows/remove-workspace.md": "1058d1d3160eb120",
|
||||
"gsd-core/workflows/resume-project.md": "98e2cf8908e73a52",
|
||||
"gsd-core/workflows/review.md": "cb4ed7b2b77bba35",
|
||||
"gsd-core/workflows/review.md": "73ea346aebf61b77",
|
||||
"gsd-core/workflows/scan.md": "46c5a73f6f682023",
|
||||
"gsd-core/workflows/secure-phase.md": "9564c529052d1d36",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -370,7 +370,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
|
||||
"gsd-core/workflows/remove-workspace.md": "f67860c795fb4bfe",
|
||||
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
|
||||
"gsd-core/workflows/review.md": "d4f26b05b3eff7f6",
|
||||
"gsd-core/workflows/review.md": "ed04746925ea035f",
|
||||
"gsd-core/workflows/scan.md": "e1c12d542e61720d",
|
||||
"gsd-core/workflows/secure-phase.md": "fda2361739e511ee",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -369,7 +369,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "8effc8742d58a11a",
|
||||
"gsd-core/workflows/remove-workspace.md": "64b73daff58b9aec",
|
||||
"gsd-core/workflows/resume-project.md": "af9761bcec0f6fe9",
|
||||
"gsd-core/workflows/review.md": "9eb81c7c61cfe50e",
|
||||
"gsd-core/workflows/review.md": "7838e12644bf808a",
|
||||
"gsd-core/workflows/scan.md": "686e787d3704db90",
|
||||
"gsd-core/workflows/secure-phase.md": "a7272ff8163a61ac",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -298,7 +298,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",
|
||||
"gsd-core/workflows/remove-workspace.md": "015cde237c75be39",
|
||||
"gsd-core/workflows/resume-project.md": "7f8dc986f0f35d96",
|
||||
"gsd-core/workflows/review.md": "bba666ebc5e13130",
|
||||
"gsd-core/workflows/review.md": "fa72c8f343102dba",
|
||||
"gsd-core/workflows/scan.md": "a71e3009998cfc57",
|
||||
"gsd-core/workflows/secure-phase.md": "ba807b4862d7f3c9",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -302,7 +302,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "e336350f8113a328",
|
||||
"gsd-core/workflows/remove-workspace.md": "79d3669cc9eb0b10",
|
||||
"gsd-core/workflows/resume-project.md": "e23981178fa37b3d",
|
||||
"gsd-core/workflows/review.md": "b1c5ebc05398ca45",
|
||||
"gsd-core/workflows/review.md": "2a95ced731e6d14c",
|
||||
"gsd-core/workflows/scan.md": "f0bd2f64f5530598",
|
||||
"gsd-core/workflows/secure-phase.md": "1a2e389991fc6263",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -370,7 +370,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
|
||||
"gsd-core/workflows/remove-workspace.md": "f67860c795fb4bfe",
|
||||
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
|
||||
"gsd-core/workflows/review.md": "d4f26b05b3eff7f6",
|
||||
"gsd-core/workflows/review.md": "ed04746925ea035f",
|
||||
"gsd-core/workflows/scan.md": "e1c12d542e61720d",
|
||||
"gsd-core/workflows/secure-phase.md": "fda2361739e511ee",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -405,7 +405,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "9ee0fddd11a0d9d4",
|
||||
"gsd-core/workflows/remove-workspace.md": "d0160c5d05bcb2bb",
|
||||
"gsd-core/workflows/resume-project.md": "9965f87eb278f7f8",
|
||||
"gsd-core/workflows/review.md": "6c74f7151f41df8b",
|
||||
"gsd-core/workflows/review.md": "9987e9fd8f7e5adb",
|
||||
"gsd-core/workflows/scan.md": "dcd6aac25ef39251",
|
||||
"gsd-core/workflows/secure-phase.md": "3ba89719288dd3f3",
|
||||
"gsd-core/workflows/session-report.md": "dd8fa011c9394075",
|
||||
|
||||
@@ -300,7 +300,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "e262654e319d1bc4",
|
||||
"gsd-core/workflows/remove-workspace.md": "e7e5b5b1e0cc9d81",
|
||||
"gsd-core/workflows/resume-project.md": "40db7f350f5866d8",
|
||||
"gsd-core/workflows/review.md": "31d6ec81b8c8dddc",
|
||||
"gsd-core/workflows/review.md": "6aad3c5254932206",
|
||||
"gsd-core/workflows/scan.md": "76aad4e70281c364",
|
||||
"gsd-core/workflows/secure-phase.md": "d60aa4053154e52f",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -370,7 +370,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",
|
||||
"gsd-core/workflows/remove-workspace.md": "0572a83d71650937",
|
||||
"gsd-core/workflows/resume-project.md": "7f8dc986f0f35d96",
|
||||
"gsd-core/workflows/review.md": "28a82741e3e8616d",
|
||||
"gsd-core/workflows/review.md": "f5972bbe92dc5f0c",
|
||||
"gsd-core/workflows/scan.md": "a71e3009998cfc57",
|
||||
"gsd-core/workflows/secure-phase.md": "b008216148d2afac",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -299,7 +299,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "fce799aae3ab2715",
|
||||
"gsd-core/workflows/remove-workspace.md": "42029a7559f1d8fb",
|
||||
"gsd-core/workflows/resume-project.md": "a0443839f1f83c2d",
|
||||
"gsd-core/workflows/review.md": "9a7fd3c1c2bd617d",
|
||||
"gsd-core/workflows/review.md": "b7aa706def0b93d6",
|
||||
"gsd-core/workflows/scan.md": "5c2370d6a8118b6c",
|
||||
"gsd-core/workflows/secure-phase.md": "c087131f1dd12901",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -370,7 +370,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",
|
||||
"gsd-core/workflows/remove-workspace.md": "f5dc82bb63e2efa4",
|
||||
"gsd-core/workflows/resume-project.md": "7f8dc986f0f35d96",
|
||||
"gsd-core/workflows/review.md": "9299d96d44252a8a",
|
||||
"gsd-core/workflows/review.md": "c5c59f2bb70396ab",
|
||||
"gsd-core/workflows/scan.md": "c039d3e40d26b606",
|
||||
"gsd-core/workflows/secure-phase.md": "5a8fbf603d218100",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -327,7 +327,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
|
||||
"gsd-core/workflows/remove-workspace.md": "f67860c795fb4bfe",
|
||||
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
|
||||
"gsd-core/workflows/review.md": "d4f26b05b3eff7f6",
|
||||
"gsd-core/workflows/review.md": "ed04746925ea035f",
|
||||
"gsd-core/workflows/scan.md": "e1c12d542e61720d",
|
||||
"gsd-core/workflows/secure-phase.md": "fda2361739e511ee",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -363,7 +363,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
|
||||
"gsd-core/workflows/remove-workspace.md": "f67860c795fb4bfe",
|
||||
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
|
||||
"gsd-core/workflows/review.md": "d4f26b05b3eff7f6",
|
||||
"gsd-core/workflows/review.md": "ed04746925ea035f",
|
||||
"gsd-core/workflows/scan.md": "e1c12d542e61720d",
|
||||
"gsd-core/workflows/secure-phase.md": "fda2361739e511ee",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -370,7 +370,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "dea4661e8f89596f",
|
||||
"gsd-core/workflows/remove-workspace.md": "21a8581add5f31ae",
|
||||
"gsd-core/workflows/resume-project.md": "ad9f06a10bab8cc0",
|
||||
"gsd-core/workflows/review.md": "a2c5d053c8ef6e27",
|
||||
"gsd-core/workflows/review.md": "486bc0f9ab4c24f4",
|
||||
"gsd-core/workflows/scan.md": "cbfb79df855e5e61",
|
||||
"gsd-core/workflows/secure-phase.md": "ef09f40dfd4d2424",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
2
tests/fixtures/golden-install-parity/pi.json
vendored
2
tests/fixtures/golden-install-parity/pi.json
vendored
@@ -266,7 +266,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
|
||||
"gsd-core/workflows/remove-workspace.md": "f67860c795fb4bfe",
|
||||
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
|
||||
"gsd-core/workflows/review.md": "d4f26b05b3eff7f6",
|
||||
"gsd-core/workflows/review.md": "ed04746925ea035f",
|
||||
"gsd-core/workflows/scan.md": "e1c12d542e61720d",
|
||||
"gsd-core/workflows/secure-phase.md": "fda2361739e511ee",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -299,7 +299,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "e8ae4fbbfac700f0",
|
||||
"gsd-core/workflows/remove-workspace.md": "ae520235f4d1f4a5",
|
||||
"gsd-core/workflows/resume-project.md": "7f20769f302e5427",
|
||||
"gsd-core/workflows/review.md": "5b59389282a276fc",
|
||||
"gsd-core/workflows/review.md": "3db704f47d7a5bde",
|
||||
"gsd-core/workflows/scan.md": "b7efd0d381a3b8a5",
|
||||
"gsd-core/workflows/secure-phase.md": "f7bfa7175102af31",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -299,7 +299,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "a46c2fe853bf4e86",
|
||||
"gsd-core/workflows/remove-workspace.md": "8ddc5f04f7c48d6c",
|
||||
"gsd-core/workflows/resume-project.md": "f242e4c8aba18ea2",
|
||||
"gsd-core/workflows/review.md": "2e10545b307c867c",
|
||||
"gsd-core/workflows/review.md": "b02dae5c743b1797",
|
||||
"gsd-core/workflows/scan.md": "3b14bcb51d3a4de8",
|
||||
"gsd-core/workflows/secure-phase.md": "267393d5b02b5334",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -299,7 +299,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "e7a6af429b36e77b",
|
||||
"gsd-core/workflows/remove-workspace.md": "30ca05fe4a3efc25",
|
||||
"gsd-core/workflows/resume-project.md": "82cfe1b8cb17c085",
|
||||
"gsd-core/workflows/review.md": "ac0c02f11e2ae1dc",
|
||||
"gsd-core/workflows/review.md": "f3fdf577dbc08f5f",
|
||||
"gsd-core/workflows/scan.md": "4a910da5e34f2685",
|
||||
"gsd-core/workflows/secure-phase.md": "d5d811bc468ce7f2",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -370,7 +370,7 @@
|
||||
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
|
||||
"gsd-core/workflows/remove-workspace.md": "f67860c795fb4bfe",
|
||||
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
|
||||
"gsd-core/workflows/review.md": "d4f26b05b3eff7f6",
|
||||
"gsd-core/workflows/review.md": "ed04746925ea035f",
|
||||
"gsd-core/workflows/scan.md": "e1c12d542e61720d",
|
||||
"gsd-core/workflows/secure-phase.md": "fda2361739e511ee",
|
||||
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
"remove-phase.md": 8513,
|
||||
"remove-workspace.md": 7916,
|
||||
"resume-project.md": 17270,
|
||||
"review.md": 51210,
|
||||
"review.md": 52646,
|
||||
"scan.md": 8314,
|
||||
"secure-phase.md": 14627,
|
||||
"session-report.md": 4044,
|
||||
|
||||
Reference in New Issue
Block a user