* test(#4014): add failing-first coverage for unreadable-vs-empty directory scope (epic #3473 B4) * fix(#4014): an unreadable directory must not report as an empty one (epic #3473 B4) * test(#4014): update hardcoded generateSlugInternal closing-brace line after import shift src/core-utils.cts's new #4014 import block shifted every subsequent line by 6, moving generateSlugInternal's real closing brace from line 193 to 199. tests/slug-derivation-drift-guard.test.cjs's MAJOR-1 fixture hardcodes that line number to plant a synthetic violation immediately after the function's real body; the guard script itself locates the boundary dynamically via brace-matching and needed no change. * docs(#4014): document the unreadable-directory scope signal and add changeset * docs(#4014): backfill changeset PR number to #4163 * test(#4014): kill pre-existing core-utils.cjs mutation-score gap, unrelated to this issue's diff --------- Co-authored-by: sim <sim@local>
This commit is contained in:
37
src/init.cts
37
src/init.cts
@@ -31,6 +31,10 @@ import phaseId = require('./phase-id.cjs');
|
||||
import worktreeSafety = require('./worktree-safety.cjs');
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports -- planning-workspace.cjs is an export= CommonJS module
|
||||
import planningWorkspace = require('./planning-workspace.cjs');
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
import planningScopeMod = require('./planning-scope.cjs');
|
||||
const { SCOPE } = planningScopeMod;
|
||||
type Scope = planningScopeMod.Scope;
|
||||
import { maskIfSecret } from './secrets.cjs';
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports -- plan-scan.cjs is an export= CommonJS module
|
||||
import scanPhasePlans = require('./plan-scan.cjs');
|
||||
@@ -1131,6 +1135,11 @@ function cmdInitPlanPhase(
|
||||
|
||||
has_research: phaseInfo?.['has_research'] || false,
|
||||
has_context: phaseInfo?.['has_context'] || false,
|
||||
// #4014 (epic #3473 B4-unreadable): additive scope signal adjacent to
|
||||
// has_context — SCOPE.COMPLETE by default (no phase directory to read is
|
||||
// a genuine, not-unreadable answer), overwritten below to whatever
|
||||
// findContextMdIn(phaseDirFull) reports once a directory is known.
|
||||
context_scope: SCOPE.COMPLETE,
|
||||
has_reviews: phaseInfo?.['has_reviews'] || false,
|
||||
has_plans: ((phaseInfo?.['plans'] as unknown[] | undefined)?.length || 0) > 0,
|
||||
plan_count: (phaseInfo?.['plans'] as unknown[] | undefined)?.length || 0,
|
||||
@@ -1149,6 +1158,13 @@ function cmdInitPlanPhase(
|
||||
|
||||
if (phaseInfo?.['directory']) {
|
||||
const phaseDirFull = path.join(cwd, phaseInfo['directory'] as string);
|
||||
// #4014 (epic #3473 B4-unreadable): findContextMdIn's directory-string
|
||||
// form never throws, so this can record the real scope BEFORE the
|
||||
// pre-existing `fs.readdirSync(phaseDirFull)` immediately below
|
||||
// (unchanged) throws on the same unreadable directory and is caught
|
||||
// exactly as before — additive only, the failure control-flow for
|
||||
// context_path/research_path/etc. is untouched.
|
||||
result['context_scope'] = findContextMdIn(phaseDirFull).scope;
|
||||
try {
|
||||
const files = fs.readdirSync(phaseDirFull);
|
||||
const phaseDirName = path.basename(phaseDirFull);
|
||||
@@ -2038,6 +2054,9 @@ function cmdInitPhaseOp(cwd: string, phase: string, raw: boolean): void {
|
||||
|
||||
has_research: phaseInfo?.['has_research'] || false,
|
||||
has_context: phaseInfo?.['has_context'] || false,
|
||||
// #4014 (epic #3473 B4-unreadable): see the parallel field in
|
||||
// cmdInitPlanPhase — additive scope signal adjacent to has_context.
|
||||
context_scope: SCOPE.COMPLETE,
|
||||
has_plans: ((phaseInfo?.['plans'] as unknown[] | undefined)?.length || 0) > 0,
|
||||
has_verification: phaseInfo?.['has_verification'] || false,
|
||||
has_reviews: phaseInfo?.['has_reviews'] || false,
|
||||
@@ -2055,6 +2074,9 @@ function cmdInitPhaseOp(cwd: string, phase: string, raw: boolean): void {
|
||||
|
||||
if (phaseInfo?.['directory']) {
|
||||
const phaseDirFull = path.join(cwd, phaseInfo['directory'] as string);
|
||||
// #4014 (epic #3473 B4-unreadable): see the parallel site in
|
||||
// cmdInitPlanPhase — additive only, failure control-flow below unchanged.
|
||||
result['context_scope'] = findContextMdIn(phaseDirFull).scope;
|
||||
try {
|
||||
const files = fs.readdirSync(phaseDirFull);
|
||||
const phaseDirName = path.basename(phaseDirFull);
|
||||
@@ -2414,6 +2436,9 @@ function cmdInitManager(cwd: string, raw: boolean): void {
|
||||
let hasResearch = false;
|
||||
let lastActivity: string | null = null;
|
||||
let isActive = false;
|
||||
// #4014 (epic #3473 B4-unreadable): default COMPLETE — no directory at
|
||||
// all (dirMatch not found) is a genuine, not-unreadable answer.
|
||||
let contextScope: Scope = SCOPE.COMPLETE;
|
||||
let completion = buildPhaseCompletionProjection(
|
||||
cwd,
|
||||
phaseNum,
|
||||
@@ -2433,6 +2458,17 @@ function cmdInitManager(cwd: string, raw: boolean): void {
|
||||
if (dirMatch) {
|
||||
const fullDir = path.join(phasesDir, dirMatch);
|
||||
const phaseDirRel = toPosixPath(path.relative(cwd, fullDir));
|
||||
// #4014 (epic #3473 B4-unreadable): this whole block used to swallow
|
||||
// ANY readdirSync failure below into the bare `catch { /* empty */ }`
|
||||
// at the bottom — an unreadable phase directory reported the exact
|
||||
// same `has_context: false` / `disk_status: 'no_directory'` as a
|
||||
// directory that never existed. findContextMdIn's directory-string
|
||||
// form never throws, so it can record the real scope (COMPLETE vs
|
||||
// UNREADABLE) here, BEFORE the pre-existing `fs.readdirSync(fullDir)`
|
||||
// immediately below (unchanged) throws on the exact same unreadable
|
||||
// directory and is caught exactly as before — this line is additive
|
||||
// only, the failure control-flow is untouched.
|
||||
contextScope = findContextMdIn(fullDir).scope;
|
||||
const phaseFiles = fs.readdirSync(fullDir);
|
||||
planCount = listPhasePlanFiles(fullDir).length;
|
||||
summaryCount = listPhaseSummaryFiles(fullDir).length;
|
||||
@@ -2510,6 +2546,7 @@ function cmdInitManager(cwd: string, raw: boolean): void {
|
||||
...completion,
|
||||
last_activity: lastActivity,
|
||||
is_active: isActive,
|
||||
context_scope: contextScope,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user