@context-forge/core 0.8.6 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/ConfigKeys.d.ts.map +1 -1
- package/dist/config/ConfigKeys.js +55 -0
- package/dist/config/ConfigKeys.js.map +1 -1
- package/dist/introspection/ConsistencyChecker.d.ts +24 -1
- package/dist/introspection/ConsistencyChecker.d.ts.map +1 -1
- package/dist/introspection/ConsistencyChecker.js +191 -31
- package/dist/introspection/ConsistencyChecker.js.map +1 -1
- package/dist/introspection/ProjectModelBuilder.d.ts.map +1 -1
- package/dist/introspection/ProjectModelBuilder.js +27 -10
- package/dist/introspection/ProjectModelBuilder.js.map +1 -1
- package/dist/introspection/WorkflowNavigator.d.ts +34 -1
- package/dist/introspection/WorkflowNavigator.d.ts.map +1 -1
- package/dist/introspection/WorkflowNavigator.js +231 -52
- package/dist/introspection/WorkflowNavigator.js.map +1 -1
- package/dist/introspection/index.d.ts +2 -0
- package/dist/introspection/index.d.ts.map +1 -1
- package/dist/introspection/index.js +2 -0
- package/dist/introspection/index.js.map +1 -1
- package/dist/introspection/parsers/documentDetector.d.ts +1 -1
- package/dist/introspection/parsers/documentDetector.d.ts.map +1 -1
- package/dist/introspection/parsers/documentDetector.js +30 -10
- package/dist/introspection/parsers/documentDetector.js.map +1 -1
- package/dist/introspection/parsers/slicePlanParser.d.ts.map +1 -1
- package/dist/introspection/parsers/slicePlanParser.js +5 -2
- package/dist/introspection/parsers/slicePlanParser.js.map +1 -1
- package/dist/introspection/parsers/statusNormalizer.d.ts +10 -2
- package/dist/introspection/parsers/statusNormalizer.d.ts.map +1 -1
- package/dist/introspection/parsers/statusNormalizer.js +25 -16
- package/dist/introspection/parsers/statusNormalizer.js.map +1 -1
- package/dist/introspection/parsers/taskFileParser.d.ts +6 -2
- package/dist/introspection/parsers/taskFileParser.d.ts.map +1 -1
- package/dist/introspection/parsers/taskFileParser.js +27 -18
- package/dist/introspection/parsers/taskFileParser.js.map +1 -1
- package/dist/introspection/reviewGate.d.ts +62 -0
- package/dist/introspection/reviewGate.d.ts.map +1 -0
- package/dist/introspection/reviewGate.js +164 -0
- package/dist/introspection/reviewGate.js.map +1 -0
- package/dist/introspection/statusDerivation.d.ts +23 -0
- package/dist/introspection/statusDerivation.d.ts.map +1 -0
- package/dist/introspection/statusDerivation.js +19 -0
- package/dist/introspection/statusDerivation.js.map +1 -0
- package/dist/introspection/types.d.ts +22 -3
- package/dist/introspection/types.d.ts.map +1 -1
- package/dist/introspection/types.js +7 -1
- package/dist/introspection/types.js.map +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +1 -1
- package/dist/node.js.map +1 -1
- package/dist/schema/frontmatterSchema.d.ts.map +1 -1
- package/dist/schema/frontmatterSchema.js +4 -0
- package/dist/schema/frontmatterSchema.js.map +1 -1
- package/dist/schema/projectSchema.d.ts +4 -0
- package/dist/schema/projectSchema.d.ts.map +1 -1
- package/dist/schema/projectSchema.js +2 -0
- package/dist/schema/projectSchema.js.map +1 -1
- package/dist/schema/resolveFileByIndex.d.ts +12 -0
- package/dist/schema/resolveFileByIndex.d.ts.map +1 -1
- package/dist/schema/resolveFileByIndex.js +29 -0
- package/dist/schema/resolveFileByIndex.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfigKeys.d.ts","sourceRoot":"","sources":["../../src/config/ConfigKeys.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IACtC,OAAO,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CAChE;AAED,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,
|
|
1
|
+
{"version":3,"file":"ConfigKeys.d.ts","sourceRoot":"","sources":["../../src/config/ConfigKeys.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IACtC,OAAO,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CAChE;AAED,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAuG3D,CAAC"}
|
|
@@ -25,6 +25,61 @@ export const CONFIG_KEYS = {
|
|
|
25
25
|
default: false,
|
|
26
26
|
description: 'Automatically apply non-destructive corrections when running consistency checks',
|
|
27
27
|
},
|
|
28
|
+
'workflow.review_enabled': {
|
|
29
|
+
type: 'boolean',
|
|
30
|
+
default: false,
|
|
31
|
+
description: 'Enable review gating in the workflow navigator (off by default; no behavior change when false)',
|
|
32
|
+
},
|
|
33
|
+
'workflow.review_threshold': {
|
|
34
|
+
type: 'string',
|
|
35
|
+
default: 'concerns',
|
|
36
|
+
description: 'Verdict floor that clears a review gate: "pass" requires PASS; "concerns" clears on PASS or CONCERNS',
|
|
37
|
+
enum: ['pass', 'concerns'],
|
|
38
|
+
},
|
|
39
|
+
'workflow.review_unknown_as': {
|
|
40
|
+
type: 'string',
|
|
41
|
+
default: 'fail',
|
|
42
|
+
description: 'How to treat an UNKNOWN/absent/unparseable verdict: "fail" blocks, "concerns" treats as CONCERNS, "pass" clears',
|
|
43
|
+
enum: ['fail', 'concerns', 'pass'],
|
|
44
|
+
},
|
|
45
|
+
'workflow.review_gates.code.threshold': {
|
|
46
|
+
type: 'string',
|
|
47
|
+
default: '',
|
|
48
|
+
description: 'Per-gate override: verdict floor for the code (pre-advance) review gate (empty = use workflow.review_threshold)',
|
|
49
|
+
enum: ['', 'pass', 'concerns'],
|
|
50
|
+
},
|
|
51
|
+
'workflow.review_gates.arch.threshold': {
|
|
52
|
+
type: 'string',
|
|
53
|
+
default: '',
|
|
54
|
+
description: 'Per-gate override: verdict floor for the arch (pre-slice-plan) review gate (empty = use workflow.review_threshold)',
|
|
55
|
+
enum: ['', 'pass', 'concerns'],
|
|
56
|
+
},
|
|
57
|
+
'workflow.review_gates.slice.threshold': {
|
|
58
|
+
type: 'string',
|
|
59
|
+
default: '',
|
|
60
|
+
description: 'Per-gate override: verdict floor for the slice (pre-tasks) review gate (empty = use workflow.review_threshold)',
|
|
61
|
+
enum: ['', 'pass', 'concerns'],
|
|
62
|
+
},
|
|
63
|
+
'workflow.review_gates.tasks.threshold': {
|
|
64
|
+
type: 'string',
|
|
65
|
+
default: '',
|
|
66
|
+
description: 'Per-gate override: verdict floor for the tasks (pre-implementation) review gate (empty = use workflow.review_threshold)',
|
|
67
|
+
enum: ['', 'pass', 'concerns'],
|
|
68
|
+
},
|
|
69
|
+
'workflow.review_gate_effective_date': {
|
|
70
|
+
type: 'string',
|
|
71
|
+
default: '',
|
|
72
|
+
description: 'Grandfathers slices/architecture designed before this date (YYYYMMDD, compared against the artifact\'s own dateCreated) out of every review gate boundary. Empty (default) applies no cutoff — every slice is subject to gating. Lets a project turn on review_enabled without retroactively demanding reviews for work completed before the gate existed.',
|
|
73
|
+
validate: (value) => {
|
|
74
|
+
if (typeof value !== 'string')
|
|
75
|
+
return 'must be a string';
|
|
76
|
+
if (value === '')
|
|
77
|
+
return null; // empty = no cutoff (identity default)
|
|
78
|
+
if (!/^\d{8}$/.test(value))
|
|
79
|
+
return 'must be in YYYYMMDD format (e.g. "20260701")';
|
|
80
|
+
return null;
|
|
81
|
+
},
|
|
82
|
+
},
|
|
28
83
|
'git.branch_root': {
|
|
29
84
|
type: 'string',
|
|
30
85
|
default: '',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfigKeys.js","sourceRoot":"","sources":["../../src/config/ConfigKeys.ts"],"names":[],"mappings":"AAQA,MAAM,CAAC,MAAM,WAAW,GAAwC;IAC9D,mBAAmB,EAAE;QACnB,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,KAAK;QACd,WAAW,EAAE,sDAAsD;KACpE;IACD,cAAc,EAAE;QACd,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,4CAA4C;KAC1D;IACD,oBAAoB,EAAE;QACpB,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,WAAW;QACpB,WAAW,EAAE,oDAAoD;QACjE,IAAI,EAAE,CAAC,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC;KACvC;IACD,uBAAuB,EAAE;QACvB,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,KAAK;QACd,WAAW,EAAE,qDAAqD;KACnE;IACD,mBAAmB,EAAE;QACnB,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,KAAK;QACd,WAAW,EAAE,iFAAiF;KAC/F;IACD,iBAAiB,EAAE;QACjB,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,EAAE;QACX,WAAW,EACT,4LAA4L;QAC9L,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;YAClB,IAAI,OAAO,KAAK,KAAK,QAAQ;gBAAE,OAAO,kBAAkB,CAAC;YACzD,IAAI,KAAK,KAAK,EAAE;gBAAE,OAAO,IAAI,CAAC,CAAC,uCAAuC;YACtE,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,OAAO,gCAAgC,CAAC;YACnE,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBACvD,OAAO,gDAAgD,CAAC;YAC1D,CAAC;YACD,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,OAAO,gCAAgC,CAAC;YACjF,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,OAAO,gCAAgC,CAAC;YACzF,OAAO,IAAI,CAAC;QACd,CAAC;KACF;CACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"ConfigKeys.js","sourceRoot":"","sources":["../../src/config/ConfigKeys.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import type { ProjectData } from '../types/project.js';
|
|
2
2
|
import type { IArtifactIntrospector } from './interfaces.js';
|
|
3
3
|
import type { ConsistencyCheckResult, ConsistencyFixResult } from './types.js';
|
|
4
|
+
import type { ConfigManager } from '../config/ConfigManager.js';
|
|
4
5
|
/**
|
|
5
6
|
* Compares related artifact states within a project and flags mismatches.
|
|
6
7
|
* Optionally applies non-destructive corrections to fixable findings.
|
|
7
8
|
*/
|
|
8
9
|
export declare class ConsistencyChecker {
|
|
9
10
|
private readonly introspector;
|
|
10
|
-
|
|
11
|
+
private readonly config?;
|
|
12
|
+
constructor(introspector: IArtifactIntrospector, config?: ConfigManager | undefined);
|
|
11
13
|
/**
|
|
12
14
|
* Run detection rules against the active slice and return structured findings.
|
|
13
15
|
*/
|
|
@@ -39,10 +41,31 @@ export declare class ConsistencyChecker {
|
|
|
39
41
|
private rulePlanVsFrontmatter;
|
|
40
42
|
/** Rule 5: Task file frontmatter status vs. computed task completion */
|
|
41
43
|
private ruleTaskFileStatus;
|
|
44
|
+
/** Rule: slice marked complete in the plan but its code review is absent or failing. */
|
|
45
|
+
/**
|
|
46
|
+
* Wraps a single evaluateReviewGate() call so a throw (e.g. malformed review-gate
|
|
47
|
+
* frontmatter) degrades to one error-severity finding instead of aborting the whole
|
|
48
|
+
* check/checkAll run. Follows the safe*() convention used elsewhere in this class.
|
|
49
|
+
*/
|
|
50
|
+
private safeEvaluateGate;
|
|
51
|
+
/**
|
|
52
|
+
* Builds the pending-review/review-failed finding for a resolved (non-null, non-error)
|
|
53
|
+
* gate evaluation. Shared by ruleReviewGate (slice boundaries) and ruleArchReviewGate
|
|
54
|
+
* (arch boundary) — they differ only in entity label and pending-review location.
|
|
55
|
+
*/
|
|
56
|
+
private buildGateFinding;
|
|
57
|
+
private ruleReviewGate;
|
|
42
58
|
/** Rule 6: Duplicate slice index detection */
|
|
43
59
|
private ruleDuplicateIndex;
|
|
44
60
|
/** Rule 7: Plan status vs. all-entries-complete */
|
|
45
61
|
private rulePlanStatusVsEntries;
|
|
62
|
+
/**
|
|
63
|
+
* Rule 9: pre-slice-plan (arch) review gate — checkAll() only. The arch boundary
|
|
64
|
+
* is keyed by arch index, not slice index, so it doesn't belong in per-slice
|
|
65
|
+
* ruleReviewGate(); it audits every discovered architecture file the same way
|
|
66
|
+
* ruleArchStatusVsPlans does.
|
|
67
|
+
*/
|
|
68
|
+
private ruleArchReviewGate;
|
|
46
69
|
/** Rule 8: Architecture status vs. all-plans-complete */
|
|
47
70
|
private ruleArchStatusVsPlans;
|
|
48
71
|
/** Find the initiative plan file for a project. Delegates to shared utility. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConsistencyChecker.d.ts","sourceRoot":"","sources":["../../src/introspection/ConsistencyChecker.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"ConsistencyChecker.d.ts","sourceRoot":"","sources":["../../src/introspection/ConsistencyChecker.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAE7D,OAAO,KAAK,EAEV,sBAAsB,EACtB,oBAAoB,EAKrB,MAAM,YAAY,CAAC;AAIpB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAmBhE;;;GAGG;AACH,qBAAa,kBAAkB;IAE3B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;gBADP,YAAY,EAAE,qBAAqB,EACnC,MAAM,CAAC,EAAE,aAAa,YAAA;IAGzC;;OAEG;IACG,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAsBlE;;OAEG;IACG,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAwGrE;;OAEG;IACG,GAAG,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAK9D;;;OAGG;IACG,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAKjE,oEAAoE;IAC9D,UAAU,CAAC,WAAW,EAAE,sBAAsB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAoCpF,gFAAgF;YAClE,UAAU;IA2CxB,sDAAsD;IACtD,OAAO,CAAC,cAAc;IAgEtB,oDAAoD;IACpD,OAAO,CAAC,yBAAyB;IAuEjC,gDAAgD;IAChD,OAAO,CAAC,oBAAoB;IAuC5B,yDAAyD;IACzD,OAAO,CAAC,qBAAqB;IAoD7B,wEAAwE;IACxE,OAAO,CAAC,kBAAkB;IAiD1B,wFAAwF;IACxF;;;;OAIG;YACW,gBAAgB;IA6B9B;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;YA6BV,cAAc;IA4C5B,8CAA8C;IAC9C,OAAO,CAAC,kBAAkB;IA6B1B,mDAAmD;YACrC,uBAAuB;IAyDrC;;;;;OAKG;YACW,kBAAkB;IAqChC,yDAAyD;YAC3C,qBAAqB;IAsDnC,gFAAgF;YAClE,kBAAkB;IAIhC,kEAAkE;YACpD,yBAAyB;IA0FvC,0EAA0E;YAC5D,iCAAiC;IAuD/C,kFAAkF;YACpE,qBAAqB;IAqDnC,mFAAmF;IACnF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAOnC;IAEF,iEAAiE;YACnD,oBAAoB;IAqBlC,gEAAgE;YAClD,qBAAqB;IA8CnC,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,WAAW;IAoBnB,mEAAmE;YACrD,qBAAqB;IAanC,qEAAqE;YACvD,oBAAoB;IAalC,iFAAiF;IACjF,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAM/B;;;OAGG;YACW,qBAAqB;IA8CnC,OAAO,CAAC,oBAAoB;YAMd,mBAAmB;YAYnB,kBAAkB;YAalB,iBAAiB;YAajB,oBAAoB;YAYpB,4BAA4B;CAY3C"}
|
|
@@ -2,9 +2,11 @@ import { join, relative } from 'node:path';
|
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
3
|
import { readdir } from 'node:fs/promises';
|
|
4
4
|
import { validateFrontmatter } from '../schema/frontmatterSchema.js';
|
|
5
|
+
import { STATUS } from './types.js';
|
|
5
6
|
import { resolveArtifactPath } from '../schema/resolveFileByIndex.js';
|
|
6
7
|
import { updateCheckbox, updateFrontmatterField } from './writers/markdownWriter.js';
|
|
7
8
|
import { resolveInitiativePlanPath } from './ArtifactIntrospector.js';
|
|
9
|
+
import { resolveGateConfig, evaluateReviewGate, positionToReviewType, } from './reviewGate.js';
|
|
8
10
|
/**
|
|
9
11
|
* Extract numeric slice index from a fileSlice value like "165-slice.workflow-navigator".
|
|
10
12
|
*/
|
|
@@ -20,8 +22,10 @@ function extractSliceIndex(fileSlice) {
|
|
|
20
22
|
*/
|
|
21
23
|
export class ConsistencyChecker {
|
|
22
24
|
introspector;
|
|
23
|
-
|
|
25
|
+
config;
|
|
26
|
+
constructor(introspector, config) {
|
|
24
27
|
this.introspector = introspector;
|
|
28
|
+
this.config = config;
|
|
25
29
|
}
|
|
26
30
|
/**
|
|
27
31
|
* Run detection rules against the active slice and return structured findings.
|
|
@@ -37,7 +41,8 @@ export class ConsistencyChecker {
|
|
|
37
41
|
}
|
|
38
42
|
const slicePlanResult = await this.safeParseSlicePlan(project, projectPath);
|
|
39
43
|
const slicePlanPath = this.resolveSlicePlanPath(project, projectPath);
|
|
40
|
-
const
|
|
44
|
+
const resolvedGate = this.config ? await resolveGateConfig(this.config) : null;
|
|
45
|
+
const findings = await this.checkSlice(projectPath, sliceIndex, slicePlanResult, slicePlanPath, resolvedGate);
|
|
41
46
|
return this.buildResult(projectPath, findings);
|
|
42
47
|
}
|
|
43
48
|
/**
|
|
@@ -63,6 +68,12 @@ export class ConsistencyChecker {
|
|
|
63
68
|
const planResult = await this.introspector.parseSlicePlan(planPath);
|
|
64
69
|
parsedPlans.set(planPath, planResult);
|
|
65
70
|
for (const entry of planResult.entries) {
|
|
71
|
+
// Fallback (unindexed-format) indices are per-file sequential counters,
|
|
72
|
+
// not real project-wide slice indices — they must never enter the
|
|
73
|
+
// cross-plan index space, or they collide with real indices from other
|
|
74
|
+
// plans (see slice 913 TD-1).
|
|
75
|
+
if (entry.indexSource === 'fallback')
|
|
76
|
+
continue;
|
|
66
77
|
if (!uniqueEntries.has(entry.index)) {
|
|
67
78
|
uniqueEntries.set(entry.index, { entry, planPath, planResult });
|
|
68
79
|
}
|
|
@@ -73,9 +84,10 @@ export class ConsistencyChecker {
|
|
|
73
84
|
}
|
|
74
85
|
}
|
|
75
86
|
const allFindings = [];
|
|
87
|
+
const resolvedGate = this.config ? await resolveGateConfig(this.config) : null;
|
|
76
88
|
// Run rules 1-5 for each unique slice entry across all plans
|
|
77
89
|
for (const [, { entry, planPath, planResult }] of uniqueEntries) {
|
|
78
|
-
const sliceFindings = await this.checkSlice(projectPath, entry.index, planResult, planPath);
|
|
90
|
+
const sliceFindings = await this.checkSlice(projectPath, entry.index, planResult, planPath, resolvedGate);
|
|
79
91
|
// Prefix each finding's description with [sliceIndex] for attribution
|
|
80
92
|
for (const finding of sliceFindings) {
|
|
81
93
|
finding.description = `[${entry.index}] ${finding.description}`;
|
|
@@ -92,6 +104,8 @@ export class ConsistencyChecker {
|
|
|
92
104
|
for (const { archPath, planResult } of archPlanPairs) {
|
|
93
105
|
allFindings.push(...await this.ruleArchStatusVsPlans(archPath, planResult));
|
|
94
106
|
}
|
|
107
|
+
// Rule 9: pre-slice-plan (arch) review gate — every discovered arch file, not just paired ones
|
|
108
|
+
allFindings.push(...await this.ruleArchReviewGate(project, projectPath, resolvedGate));
|
|
95
109
|
// Rule 10: stale worktree paths
|
|
96
110
|
allFindings.push(...await this.ruleStaleWorktreePath(project, projectPath));
|
|
97
111
|
// Rules 13 & 14: initiative plan checks
|
|
@@ -158,7 +172,7 @@ export class ConsistencyChecker {
|
|
|
158
172
|
}
|
|
159
173
|
// --- Per-slice check logic ---
|
|
160
174
|
/** Run rules 1-5 against a single slice, returning raw findings (no prefix). */
|
|
161
|
-
async checkSlice(projectPath, sliceIndex, slicePlanResult, slicePlanPath) {
|
|
175
|
+
async checkSlice(projectPath, sliceIndex, slicePlanResult, slicePlanPath, resolvedGate) {
|
|
162
176
|
const docs = await this.safeDetectDocuments(projectPath, sliceIndex);
|
|
163
177
|
const taskResult = await this.safeParseTaskFile(docs?.taskFile, projectPath);
|
|
164
178
|
const sliceFrontmatter = await this.safeParseFrontmatter(docs?.sliceDesign, projectPath);
|
|
@@ -171,6 +185,7 @@ export class ConsistencyChecker {
|
|
|
171
185
|
findings.push(...this.rulePlanVsFrontmatter(planEntry, sliceFrontmatter, slicePlanPath, sliceDesignRel, projectPath));
|
|
172
186
|
const taskFileFrontmatter = await this.safeParseTaskFileFrontmatter(docs?.taskFile, projectPath);
|
|
173
187
|
findings.push(...this.ruleTaskFileStatus(taskResult, taskFileFrontmatter));
|
|
188
|
+
findings.push(...await this.ruleReviewGate(planEntry, sliceIndex, projectPath, slicePlanPath, resolvedGate, docs));
|
|
174
189
|
return findings;
|
|
175
190
|
}
|
|
176
191
|
// --- Detection Rules ---
|
|
@@ -179,7 +194,7 @@ export class ConsistencyChecker {
|
|
|
179
194
|
const findings = [];
|
|
180
195
|
if (!taskResult || !planEntry || !slicePlanPath || !slicePlanResult)
|
|
181
196
|
return findings;
|
|
182
|
-
const tasksComplete = taskResult.inferredStatus ===
|
|
197
|
+
const tasksComplete = taskResult.inferredStatus === STATUS.Complete;
|
|
183
198
|
const sliceChecked = planEntry.isChecked;
|
|
184
199
|
if (tasksComplete && !sliceChecked) {
|
|
185
200
|
findings.push({
|
|
@@ -211,6 +226,20 @@ export class ConsistencyChecker {
|
|
|
211
226
|
},
|
|
212
227
|
});
|
|
213
228
|
}
|
|
229
|
+
// Not-started boundary (TD-4): tasks have started (any checked, not all) but
|
|
230
|
+
// the plan entry is still unchecked. Not fixable — there is no single correct
|
|
231
|
+
// auto-fix for "in progress" (don't check the box; there's no in-progress
|
|
232
|
+
// checkbox state to write).
|
|
233
|
+
if (taskResult.inferredStatus === STATUS.InProgress && !sliceChecked) {
|
|
234
|
+
findings.push({
|
|
235
|
+
rule: 'task-vs-plan',
|
|
236
|
+
severity: 'warning',
|
|
237
|
+
location: slicePlanPath,
|
|
238
|
+
description: `Tasks in progress (${taskResult.completedTasks}/${taskResult.totalTasks}) but slice ${sliceIndex} is unchecked in plan`,
|
|
239
|
+
suggestedFix: `No single auto-fix for in-progress — leave the slice plan entry for (${sliceIndex}) unchecked until tasks complete`,
|
|
240
|
+
fixable: false,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
214
243
|
return findings;
|
|
215
244
|
}
|
|
216
245
|
/** Rule 2: Frontmatter status vs. computed state */
|
|
@@ -221,11 +250,11 @@ export class ConsistencyChecker {
|
|
|
221
250
|
if (!taskResult)
|
|
222
251
|
return findings;
|
|
223
252
|
const fmStatus = frontmatter.data.status.toLowerCase();
|
|
224
|
-
const tasksComplete = taskResult.inferredStatus ===
|
|
253
|
+
const tasksComplete = taskResult.inferredStatus === STATUS.Complete;
|
|
225
254
|
const sliceDesignFullPath = sliceDesignRelPath
|
|
226
255
|
? join(projectPath, sliceDesignRelPath)
|
|
227
256
|
: frontmatter.filePath;
|
|
228
|
-
if (fmStatus ===
|
|
257
|
+
if (fmStatus === STATUS.Complete && !tasksComplete) {
|
|
229
258
|
findings.push({
|
|
230
259
|
rule: 'frontmatter-vs-computed',
|
|
231
260
|
severity: 'error',
|
|
@@ -236,11 +265,11 @@ export class ConsistencyChecker {
|
|
|
236
265
|
fixAction: {
|
|
237
266
|
type: 'update-frontmatter',
|
|
238
267
|
filePath: sliceDesignFullPath,
|
|
239
|
-
detail: { key: 'status', value:
|
|
268
|
+
detail: { key: 'status', value: STATUS.InProgress },
|
|
240
269
|
},
|
|
241
270
|
});
|
|
242
271
|
}
|
|
243
|
-
if ((fmStatus ===
|
|
272
|
+
if ((fmStatus === STATUS.InProgress || fmStatus === STATUS.NotStarted) && tasksComplete) {
|
|
244
273
|
findings.push({
|
|
245
274
|
rule: 'frontmatter-vs-computed',
|
|
246
275
|
severity: 'warning',
|
|
@@ -251,7 +280,25 @@ export class ConsistencyChecker {
|
|
|
251
280
|
fixAction: {
|
|
252
281
|
type: 'update-frontmatter',
|
|
253
282
|
filePath: sliceDesignFullPath,
|
|
254
|
-
detail: { key: 'status', value:
|
|
283
|
+
detail: { key: 'status', value: STATUS.Complete },
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
// Not-started boundary (TD-4): tasks have started but design frontmatter
|
|
288
|
+
// still says not-started. Mirrors the branch above — same fix-action shape,
|
|
289
|
+
// different target value.
|
|
290
|
+
if (fmStatus === STATUS.NotStarted && taskResult.inferredStatus === STATUS.InProgress) {
|
|
291
|
+
findings.push({
|
|
292
|
+
rule: 'frontmatter-vs-computed',
|
|
293
|
+
severity: 'warning',
|
|
294
|
+
location: sliceDesignFullPath,
|
|
295
|
+
description: `Frontmatter status is "not-started" but tasks are in progress (${taskResult.completedTasks}/${taskResult.totalTasks})`,
|
|
296
|
+
suggestedFix: 'Update frontmatter status to "in-progress"',
|
|
297
|
+
fixable: true,
|
|
298
|
+
fixAction: {
|
|
299
|
+
type: 'update-frontmatter',
|
|
300
|
+
filePath: sliceDesignFullPath,
|
|
301
|
+
detail: { key: 'status', value: STATUS.InProgress },
|
|
255
302
|
},
|
|
256
303
|
});
|
|
257
304
|
}
|
|
@@ -298,7 +345,7 @@ export class ConsistencyChecker {
|
|
|
298
345
|
const sliceDesignFullPath = sliceDesignRelPath
|
|
299
346
|
? join(projectPath, sliceDesignRelPath)
|
|
300
347
|
: frontmatter.filePath;
|
|
301
|
-
if (planChecked && fmStatus !==
|
|
348
|
+
if (planChecked && fmStatus !== STATUS.Complete) {
|
|
302
349
|
findings.push({
|
|
303
350
|
rule: 'plan-vs-frontmatter',
|
|
304
351
|
severity: 'warning',
|
|
@@ -309,11 +356,11 @@ export class ConsistencyChecker {
|
|
|
309
356
|
fixAction: {
|
|
310
357
|
type: 'update-frontmatter',
|
|
311
358
|
filePath: sliceDesignFullPath,
|
|
312
|
-
detail: { key: 'status', value:
|
|
359
|
+
detail: { key: 'status', value: STATUS.Complete },
|
|
313
360
|
},
|
|
314
361
|
});
|
|
315
362
|
}
|
|
316
|
-
if (!planChecked && fmStatus ===
|
|
363
|
+
if (!planChecked && fmStatus === STATUS.Complete && slicePlanPath) {
|
|
317
364
|
findings.push({
|
|
318
365
|
rule: 'plan-vs-frontmatter',
|
|
319
366
|
severity: 'warning',
|
|
@@ -336,11 +383,11 @@ export class ConsistencyChecker {
|
|
|
336
383
|
if (!taskResult || !taskFrontmatter?.found || !taskFrontmatter.data.status)
|
|
337
384
|
return findings;
|
|
338
385
|
const fmStatus = taskFrontmatter.data.status.toLowerCase().replace(/_/g, '-');
|
|
339
|
-
const computed = taskResult.inferredStatus;
|
|
386
|
+
const computed = taskResult.inferredStatus;
|
|
340
387
|
const taskFilePath = taskFrontmatter.filePath;
|
|
341
388
|
// Map task file status values to normalized form for comparison
|
|
342
389
|
// Task files use not_started/in_progress/complete in frontmatter
|
|
343
|
-
if (fmStatus ===
|
|
390
|
+
if (fmStatus === STATUS.Complete && computed !== STATUS.Complete) {
|
|
344
391
|
findings.push({
|
|
345
392
|
rule: 'task-file-status',
|
|
346
393
|
severity: 'error',
|
|
@@ -355,7 +402,7 @@ export class ConsistencyChecker {
|
|
|
355
402
|
},
|
|
356
403
|
});
|
|
357
404
|
}
|
|
358
|
-
if (fmStatus !==
|
|
405
|
+
if (fmStatus !== STATUS.Complete && computed === STATUS.Complete) {
|
|
359
406
|
findings.push({
|
|
360
407
|
rule: 'task-file-status',
|
|
361
408
|
severity: 'warning',
|
|
@@ -366,12 +413,93 @@ export class ConsistencyChecker {
|
|
|
366
413
|
fixAction: {
|
|
367
414
|
type: 'update-frontmatter',
|
|
368
415
|
filePath: taskFilePath,
|
|
369
|
-
detail: { key: 'status', value:
|
|
416
|
+
detail: { key: 'status', value: STATUS.Complete },
|
|
370
417
|
},
|
|
371
418
|
});
|
|
372
419
|
}
|
|
373
420
|
return findings;
|
|
374
421
|
}
|
|
422
|
+
/** Rule: slice marked complete in the plan but its code review is absent or failing. */
|
|
423
|
+
/**
|
|
424
|
+
* Wraps a single evaluateReviewGate() call so a throw (e.g. malformed review-gate
|
|
425
|
+
* frontmatter) degrades to one error-severity finding instead of aborting the whole
|
|
426
|
+
* check/checkAll run. Follows the safe*() convention used elsewhere in this class.
|
|
427
|
+
*/
|
|
428
|
+
async safeEvaluateGate(projectPath, index, boundary, resolvedGate, fallbackLocation) {
|
|
429
|
+
try {
|
|
430
|
+
const gate = await evaluateReviewGate(projectPath, index, boundary, this.config, resolvedGate);
|
|
431
|
+
return { gate, errorFinding: null };
|
|
432
|
+
}
|
|
433
|
+
catch (err) {
|
|
434
|
+
const reviewType = positionToReviewType(boundary);
|
|
435
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
436
|
+
return {
|
|
437
|
+
gate: null,
|
|
438
|
+
errorFinding: {
|
|
439
|
+
rule: 'review-gate',
|
|
440
|
+
severity: 'error',
|
|
441
|
+
location: fallbackLocation,
|
|
442
|
+
description: `Failed to evaluate the ${reviewType} review gate for ${index}: ${message}`,
|
|
443
|
+
suggestedFix: `Fix the frontmatter for ${index} and re-run the check`,
|
|
444
|
+
fixable: false,
|
|
445
|
+
},
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Builds the pending-review/review-failed finding for a resolved (non-null, non-error)
|
|
451
|
+
* gate evaluation. Shared by ruleReviewGate (slice boundaries) and ruleArchReviewGate
|
|
452
|
+
* (arch boundary) — they differ only in entity label and pending-review location.
|
|
453
|
+
*/
|
|
454
|
+
buildGateFinding(result, boundary, entityLabel, index, projectPath, pendingLocation) {
|
|
455
|
+
const reviewType = positionToReviewType(boundary);
|
|
456
|
+
if (result.status === 'pending-review') {
|
|
457
|
+
return {
|
|
458
|
+
rule: 'review-gate',
|
|
459
|
+
severity: 'warning',
|
|
460
|
+
location: pendingLocation,
|
|
461
|
+
description: result.rationale,
|
|
462
|
+
suggestedFix: `Run the ${reviewType} review for ${entityLabel} ${index}`,
|
|
463
|
+
fixable: false,
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
return {
|
|
467
|
+
rule: 'review-gate',
|
|
468
|
+
severity: 'error',
|
|
469
|
+
location: join(projectPath, result.artifactPath),
|
|
470
|
+
description: result.rationale,
|
|
471
|
+
suggestedFix: `Resolve the review findings or rerun the ${reviewType} review for ${entityLabel} ${index}`,
|
|
472
|
+
fixable: false,
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
async ruleReviewGate(planEntry, sliceIndex, projectPath, slicePlanPath, resolvedGate, docs) {
|
|
476
|
+
if (!this.config || resolvedGate === null)
|
|
477
|
+
return [];
|
|
478
|
+
if (slicePlanPath === null)
|
|
479
|
+
return [];
|
|
480
|
+
const findings = [];
|
|
481
|
+
const boundaries = [
|
|
482
|
+
{ boundary: 'preTasks', guard: docs?.sliceDesign !== null && docs?.sliceDesign !== undefined },
|
|
483
|
+
{ boundary: 'preImplementation', guard: !!docs?.taskFile && docs.taskFile.length > 0 },
|
|
484
|
+
{
|
|
485
|
+
boundary: 'preAdvance',
|
|
486
|
+
guard: !!planEntry?.isChecked && docs?.sliceDesign !== null && docs?.sliceDesign !== undefined,
|
|
487
|
+
},
|
|
488
|
+
];
|
|
489
|
+
for (const { boundary, guard } of boundaries) {
|
|
490
|
+
if (!guard)
|
|
491
|
+
continue;
|
|
492
|
+
const { gate: result, errorFinding } = await this.safeEvaluateGate(projectPath, sliceIndex, boundary, resolvedGate, slicePlanPath);
|
|
493
|
+
if (errorFinding) {
|
|
494
|
+
findings.push(errorFinding);
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
if (result === null)
|
|
498
|
+
continue;
|
|
499
|
+
findings.push(this.buildGateFinding(result, boundary, 'slice', sliceIndex, projectPath, slicePlanPath));
|
|
500
|
+
}
|
|
501
|
+
return findings;
|
|
502
|
+
}
|
|
375
503
|
// --- Aggregate Rules (checkAll only) ---
|
|
376
504
|
/** Rule 6: Duplicate slice index detection */
|
|
377
505
|
ruleDuplicateIndex(entries, slicePlanPath) {
|
|
@@ -413,7 +541,7 @@ export class ConsistencyChecker {
|
|
|
413
541
|
if (!planFrontmatter.data.status)
|
|
414
542
|
return findings;
|
|
415
543
|
const planStatus = planFrontmatter.data.status.toLowerCase();
|
|
416
|
-
if (planStatus ===
|
|
544
|
+
if (planStatus === STATUS.Complete && !allComplete) {
|
|
417
545
|
findings.push({
|
|
418
546
|
rule: 'plan-status-vs-entries',
|
|
419
547
|
severity: 'warning',
|
|
@@ -424,11 +552,11 @@ export class ConsistencyChecker {
|
|
|
424
552
|
fixAction: {
|
|
425
553
|
type: 'update-frontmatter',
|
|
426
554
|
filePath: slicePlanPath,
|
|
427
|
-
detail: { key: 'status', value:
|
|
555
|
+
detail: { key: 'status', value: STATUS.InProgress },
|
|
428
556
|
},
|
|
429
557
|
});
|
|
430
558
|
}
|
|
431
|
-
if (planStatus !==
|
|
559
|
+
if (planStatus !== STATUS.Complete && allComplete && slicePlanResult.totalSlices > 0) {
|
|
432
560
|
findings.push({
|
|
433
561
|
rule: 'plan-status-vs-entries',
|
|
434
562
|
severity: 'warning',
|
|
@@ -439,12 +567,44 @@ export class ConsistencyChecker {
|
|
|
439
567
|
fixAction: {
|
|
440
568
|
type: 'update-frontmatter',
|
|
441
569
|
filePath: slicePlanPath,
|
|
442
|
-
detail: { key: 'status', value:
|
|
570
|
+
detail: { key: 'status', value: STATUS.Complete },
|
|
443
571
|
},
|
|
444
572
|
});
|
|
445
573
|
}
|
|
446
574
|
return findings;
|
|
447
575
|
}
|
|
576
|
+
/**
|
|
577
|
+
* Rule 9: pre-slice-plan (arch) review gate — checkAll() only. The arch boundary
|
|
578
|
+
* is keyed by arch index, not slice index, so it doesn't belong in per-slice
|
|
579
|
+
* ruleReviewGate(); it audits every discovered architecture file the same way
|
|
580
|
+
* ruleArchStatusVsPlans does.
|
|
581
|
+
*/
|
|
582
|
+
async ruleArchReviewGate(project, projectPath, resolvedGate) {
|
|
583
|
+
if (!this.config || resolvedGate === null)
|
|
584
|
+
return [];
|
|
585
|
+
const archFiles = await this.discoverAllArchFiles(projectPath);
|
|
586
|
+
const allArchPaths = new Set(archFiles);
|
|
587
|
+
if (project.fileArch) {
|
|
588
|
+
const configuredArchRel = resolveArtifactPath('fileArch', project.fileArch);
|
|
589
|
+
if (configuredArchRel)
|
|
590
|
+
allArchPaths.add(join(projectPath, configuredArchRel));
|
|
591
|
+
}
|
|
592
|
+
const findings = [];
|
|
593
|
+
for (const archPath of allArchPaths) {
|
|
594
|
+
const archIndex = ConsistencyChecker.extractFileIndex(archPath);
|
|
595
|
+
if (archIndex === null)
|
|
596
|
+
continue;
|
|
597
|
+
const { gate: result, errorFinding } = await this.safeEvaluateGate(projectPath, archIndex, 'preSlicePlan', resolvedGate, archPath);
|
|
598
|
+
if (errorFinding) {
|
|
599
|
+
findings.push(errorFinding);
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
if (result === null)
|
|
603
|
+
continue;
|
|
604
|
+
findings.push(this.buildGateFinding(result, 'preSlicePlan', 'architecture', archIndex, projectPath, archPath));
|
|
605
|
+
}
|
|
606
|
+
return findings;
|
|
607
|
+
}
|
|
448
608
|
/** Rule 8: Architecture status vs. all-plans-complete */
|
|
449
609
|
async ruleArchStatusVsPlans(archPath, slicePlanResult) {
|
|
450
610
|
const findings = [];
|
|
@@ -459,7 +619,7 @@ export class ConsistencyChecker {
|
|
|
459
619
|
return findings;
|
|
460
620
|
const archStatus = archFrontmatter.data.status.toLowerCase();
|
|
461
621
|
const allComplete = slicePlanResult.completedSlices === slicePlanResult.totalSlices;
|
|
462
|
-
if (archStatus ===
|
|
622
|
+
if (archStatus === STATUS.Complete && !allComplete) {
|
|
463
623
|
findings.push({
|
|
464
624
|
rule: 'arch-status-vs-plans',
|
|
465
625
|
severity: 'warning',
|
|
@@ -470,11 +630,11 @@ export class ConsistencyChecker {
|
|
|
470
630
|
fixAction: {
|
|
471
631
|
type: 'update-frontmatter',
|
|
472
632
|
filePath: archPath,
|
|
473
|
-
detail: { key: 'status', value:
|
|
633
|
+
detail: { key: 'status', value: STATUS.InProgress },
|
|
474
634
|
},
|
|
475
635
|
});
|
|
476
636
|
}
|
|
477
|
-
if (archStatus !==
|
|
637
|
+
if (archStatus !== STATUS.Complete && allComplete && slicePlanResult.totalSlices > 0) {
|
|
478
638
|
findings.push({
|
|
479
639
|
rule: 'arch-status-vs-plans',
|
|
480
640
|
severity: 'warning',
|
|
@@ -485,7 +645,7 @@ export class ConsistencyChecker {
|
|
|
485
645
|
fixAction: {
|
|
486
646
|
type: 'update-frontmatter',
|
|
487
647
|
filePath: archPath,
|
|
488
|
-
detail: { key: 'status', value:
|
|
648
|
+
detail: { key: 'status', value: STATUS.Complete },
|
|
489
649
|
},
|
|
490
650
|
});
|
|
491
651
|
}
|
|
@@ -520,7 +680,7 @@ export class ConsistencyChecker {
|
|
|
520
680
|
if (!archFrontmatter.found || !archFrontmatter.data.status)
|
|
521
681
|
continue;
|
|
522
682
|
const archStatus = archFrontmatter.data.status.toLowerCase();
|
|
523
|
-
const archComplete = archStatus ===
|
|
683
|
+
const archComplete = archStatus === STATUS.Complete;
|
|
524
684
|
if (archComplete && !entry.isChecked) {
|
|
525
685
|
findings.push({
|
|
526
686
|
rule: 'initiative-entry-vs-arch',
|
|
@@ -547,7 +707,7 @@ export class ConsistencyChecker {
|
|
|
547
707
|
fixAction: {
|
|
548
708
|
type: 'update-frontmatter',
|
|
549
709
|
filePath: archPath,
|
|
550
|
-
detail: { key: 'status', value:
|
|
710
|
+
detail: { key: 'status', value: STATUS.Complete },
|
|
551
711
|
},
|
|
552
712
|
});
|
|
553
713
|
}
|
|
@@ -591,7 +751,7 @@ export class ConsistencyChecker {
|
|
|
591
751
|
const planStatus = planFrontmatter.data.status.toLowerCase();
|
|
592
752
|
const allComplete = initiativePlanResult.totalSlices > 0 &&
|
|
593
753
|
initiativePlanResult.completedSlices === initiativePlanResult.totalSlices;
|
|
594
|
-
if (planStatus ===
|
|
754
|
+
if (planStatus === STATUS.Complete && !allComplete) {
|
|
595
755
|
findings.push({
|
|
596
756
|
rule: 'initiative-plan-status-vs-entries',
|
|
597
757
|
severity: 'warning',
|
|
@@ -602,11 +762,11 @@ export class ConsistencyChecker {
|
|
|
602
762
|
fixAction: {
|
|
603
763
|
type: 'update-frontmatter',
|
|
604
764
|
filePath: initiativePlanPath,
|
|
605
|
-
detail: { key: 'status', value:
|
|
765
|
+
detail: { key: 'status', value: STATUS.InProgress },
|
|
606
766
|
},
|
|
607
767
|
});
|
|
608
768
|
}
|
|
609
|
-
if (planStatus !==
|
|
769
|
+
if (planStatus !== STATUS.Complete && allComplete) {
|
|
610
770
|
findings.push({
|
|
611
771
|
rule: 'initiative-plan-status-vs-entries',
|
|
612
772
|
severity: 'warning',
|
|
@@ -617,7 +777,7 @@ export class ConsistencyChecker {
|
|
|
617
777
|
fixAction: {
|
|
618
778
|
type: 'update-frontmatter',
|
|
619
779
|
filePath: initiativePlanPath,
|
|
620
|
-
detail: { key: 'status', value:
|
|
780
|
+
detail: { key: 'status', value: STATUS.Complete },
|
|
621
781
|
},
|
|
622
782
|
});
|
|
623
783
|
}
|