* test(#4183): reproduce root phase Fallow base failure * fix(#4183): resolve root phase Fallow base Fall back to the phase root commit when its parent is unresolvable. * chore(#4183): add release note * chore(#4183): bind changeset to fork PR * docs(#4183): describe root fallback precisely * chore(#4183): bind changeset to upstream PR --------- Co-authored-by: CI Rebase Check <ci@gsd-redux> Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
This commit is contained in:
committed by
GitHub
parent
eedb6b5431
commit
d0d542e478
5
.changeset/bold-otters-run.md
Normal file
5
.changeset/bold-otters-run.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Fixed
|
||||
pr: 4215
|
||||
---
|
||||
**Structural pre-pass no longer aborts for phases introduced in the repository's root commit** — Fallow uses the root commit itself when no parent exists instead of receiving an invalid parent revision. (#4183)
|
||||
@@ -39,7 +39,11 @@ if [ \"$FALLOW_SCOPE\" = \"phase\" ]; then
|
||||
# same-numbered phases and tail -1 selected the oldest.
|
||||
FALLOW_PHASE_START=$(git log --format=\"%H\" --diff-filter=A -- \"${PHASE_DIR}\" 2>/dev/null | tail -1)
|
||||
if [ -n \"$FALLOW_PHASE_START\" ]; then
|
||||
if git rev-parse \"${FALLOW_PHASE_START}^\" >/dev/null 2>&1; then
|
||||
FALLOW_BASE=\"${FALLOW_PHASE_START}^\"
|
||||
else
|
||||
FALLOW_BASE=\"${FALLOW_PHASE_START}\"
|
||||
fi
|
||||
FALLOW_SCOPE_ARGS=(--changed-since \"$FALLOW_BASE\")
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -990,6 +990,59 @@ describe('Bug 5 (#3191) — same anchored, portable phase-scope grep at all thre
|
||||
}
|
||||
);
|
||||
|
||||
test(
|
||||
'root commit: fallow phase scope uses a resolvable root SHA',
|
||||
SKIP_WIN32,
|
||||
() => {
|
||||
const repo = createTempDir('gsd-4183-fallow-root-');
|
||||
try {
|
||||
gitOrThrow(['init', '-b', 'main'], { cwd: repo, timeoutMs: GIT_TIMEOUT_MS });
|
||||
gitOrThrow(['config', 'user.email', 'test@test.com'], { cwd: repo, timeoutMs: GIT_TIMEOUT_MS });
|
||||
gitOrThrow(['config', 'user.name', 'Test'], { cwd: repo, timeoutMs: GIT_TIMEOUT_MS });
|
||||
gitOrThrow(['config', 'commit.gpgsign', 'false'], { cwd: repo, timeoutMs: GIT_TIMEOUT_MS });
|
||||
|
||||
const phaseFile = path.join(repo, '.planning', 'phases', '06-ctx', 'PLAN.md');
|
||||
fs.mkdirSync(path.dirname(phaseFile), { recursive: true });
|
||||
fs.writeFileSync(phaseFile, '# phase context\n');
|
||||
gitOrThrow(['add', '.planning'], { cwd: repo, timeoutMs: GIT_TIMEOUT_MS });
|
||||
gitOrThrow(['commit', '-m', 'docs(06): initial phase context'], { cwd: repo, timeoutMs: GIT_TIMEOUT_MS });
|
||||
const rootSha = gitOrThrow(['rev-parse', 'HEAD'], { cwd: repo, timeoutMs: GIT_TIMEOUT_MS }).trim();
|
||||
|
||||
fs.writeFileSync(path.join(repo, 'index.js'), 'module.exports = 1;\n');
|
||||
gitOrThrow(['add', 'index.js'], { cwd: repo, timeoutMs: GIT_TIMEOUT_MS });
|
||||
gitOrThrow(['commit', '-m', 'feat: add source'], { cwd: repo, timeoutMs: GIT_TIMEOUT_MS });
|
||||
|
||||
const result = runDerivation(repo, extractFallowDerivation(), '06');
|
||||
assert.equal(result.status, 0, `snippet exited ${result.status}; stderr=${result.stderr}`);
|
||||
const fallowBase = parseSentinel(result.stdout, 'FALLOW_BASE');
|
||||
assert.deepStrictEqual(
|
||||
fallowBase,
|
||||
[rootSha],
|
||||
`root-parent FALLOW_BASE regression: expected ${rootSha}, got ${JSON.stringify(fallowBase)}`,
|
||||
);
|
||||
assert.equal(
|
||||
gitOrThrow(['rev-parse', '--verify', `${fallowBase[0]}^{commit}`], { cwd: repo, timeoutMs: GIT_TIMEOUT_MS }).trim(),
|
||||
rootSha,
|
||||
'FALLOW_BASE must resolve to the root commit',
|
||||
);
|
||||
|
||||
if (process.env.CI) {
|
||||
const { requireFallowBinary } = require('../gsd-core/bin/lib/fallow-runner.cjs');
|
||||
const { execTool } = require('../gsd-core/bin/lib/shell-command-projection.cjs');
|
||||
const audit = execTool(
|
||||
requireFallowBinary({ cwd: ROOT, envPath: '' }),
|
||||
['audit', '--changed-since', fallowBase[0], '--format', 'json'],
|
||||
{ cwd: repo, timeout: 120000 },
|
||||
);
|
||||
assert.ok([0, 1].includes(audit.exitCode), `fallow root audit exit=${audit.exitCode}; stderr=${audit.stderr}`);
|
||||
console.log(`fallow-root-audit normal-exit=${audit.exitCode}`);
|
||||
}
|
||||
} finally {
|
||||
cleanup(repo);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'T5: with no genuine phase scope commit, every derivation yields NO base (fail-closed preserved)',
|
||||
SKIP_WIN32,
|
||||
|
||||
Reference in New Issue
Block a user