@context-forge/core 0.8.6 → 0.9.1

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.
Files changed (61) hide show
  1. package/dist/config/ConfigKeys.d.ts.map +1 -1
  2. package/dist/config/ConfigKeys.js +57 -2
  3. package/dist/config/ConfigKeys.js.map +1 -1
  4. package/dist/introspection/ConsistencyChecker.d.ts +24 -1
  5. package/dist/introspection/ConsistencyChecker.d.ts.map +1 -1
  6. package/dist/introspection/ConsistencyChecker.js +191 -31
  7. package/dist/introspection/ConsistencyChecker.js.map +1 -1
  8. package/dist/introspection/ProjectModelBuilder.d.ts.map +1 -1
  9. package/dist/introspection/ProjectModelBuilder.js +27 -10
  10. package/dist/introspection/ProjectModelBuilder.js.map +1 -1
  11. package/dist/introspection/WorkflowNavigator.d.ts +34 -1
  12. package/dist/introspection/WorkflowNavigator.d.ts.map +1 -1
  13. package/dist/introspection/WorkflowNavigator.js +231 -52
  14. package/dist/introspection/WorkflowNavigator.js.map +1 -1
  15. package/dist/introspection/index.d.ts +2 -0
  16. package/dist/introspection/index.d.ts.map +1 -1
  17. package/dist/introspection/index.js +2 -0
  18. package/dist/introspection/index.js.map +1 -1
  19. package/dist/introspection/parsers/documentDetector.d.ts +1 -1
  20. package/dist/introspection/parsers/documentDetector.d.ts.map +1 -1
  21. package/dist/introspection/parsers/documentDetector.js +30 -10
  22. package/dist/introspection/parsers/documentDetector.js.map +1 -1
  23. package/dist/introspection/parsers/slicePlanParser.d.ts.map +1 -1
  24. package/dist/introspection/parsers/slicePlanParser.js +5 -2
  25. package/dist/introspection/parsers/slicePlanParser.js.map +1 -1
  26. package/dist/introspection/parsers/statusNormalizer.d.ts +10 -2
  27. package/dist/introspection/parsers/statusNormalizer.d.ts.map +1 -1
  28. package/dist/introspection/parsers/statusNormalizer.js +25 -16
  29. package/dist/introspection/parsers/statusNormalizer.js.map +1 -1
  30. package/dist/introspection/parsers/taskFileParser.d.ts +6 -2
  31. package/dist/introspection/parsers/taskFileParser.d.ts.map +1 -1
  32. package/dist/introspection/parsers/taskFileParser.js +27 -18
  33. package/dist/introspection/parsers/taskFileParser.js.map +1 -1
  34. package/dist/introspection/reviewGate.d.ts +62 -0
  35. package/dist/introspection/reviewGate.d.ts.map +1 -0
  36. package/dist/introspection/reviewGate.js +165 -0
  37. package/dist/introspection/reviewGate.js.map +1 -0
  38. package/dist/introspection/statusDerivation.d.ts +23 -0
  39. package/dist/introspection/statusDerivation.d.ts.map +1 -0
  40. package/dist/introspection/statusDerivation.js +19 -0
  41. package/dist/introspection/statusDerivation.js.map +1 -0
  42. package/dist/introspection/types.d.ts +22 -3
  43. package/dist/introspection/types.d.ts.map +1 -1
  44. package/dist/introspection/types.js +7 -1
  45. package/dist/introspection/types.js.map +1 -1
  46. package/dist/node.d.ts +1 -1
  47. package/dist/node.d.ts.map +1 -1
  48. package/dist/node.js +1 -1
  49. package/dist/node.js.map +1 -1
  50. package/dist/schema/frontmatterSchema.d.ts.map +1 -1
  51. package/dist/schema/frontmatterSchema.js +6 -0
  52. package/dist/schema/frontmatterSchema.js.map +1 -1
  53. package/dist/schema/projectSchema.d.ts +4 -0
  54. package/dist/schema/projectSchema.d.ts.map +1 -1
  55. package/dist/schema/projectSchema.js +2 -0
  56. package/dist/schema/projectSchema.js.map +1 -1
  57. package/dist/schema/resolveFileByIndex.d.ts +12 -0
  58. package/dist/schema/resolveFileByIndex.d.ts.map +1 -1
  59. package/dist/schema/resolveFileByIndex.js +29 -0
  60. package/dist/schema/resolveFileByIndex.js.map +1 -1
  61. 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,CA4C3D,CAAC"}
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,10 +25,65 @@ export const CONFIG_KEYS = {
25
25
  default: false,
26
26
  description: 'Automatically apply non-destructive corrections when running consistency checks',
27
27
  },
28
- 'git.branch_root': {
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
+ },
83
+ 'git.integration_branch': {
29
84
  type: 'string',
30
85
  default: '',
31
- description: 'Optional path prefix prepended to work branch names (e.g. "myroot" yields "myroot/910-slice.foo"). Empty means no prefix. Relative and contained: must not be absolute or escape via "..".',
86
+ description: 'Optional long-lived integration branch that work branches fork from and merge into instead of main (e.g. "dev/erik" yields "dev/erik/910-slice.foo", forked from and merged into "dev/erik"). Empty means no integration branch — work branches fork from and merge into main directly. Relative and contained: must not be absolute or escape via "..".',
32
87
  validate: (value) => {
33
88
  if (typeof value !== 'string')
34
89
  return 'must be a string';
@@ -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
- constructor(introspector: IArtifactIntrospector);
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;AAC7D,OAAO,KAAK,EAEV,sBAAsB,EACtB,oBAAoB,EAKrB,MAAM,YAAY,CAAC;AAcpB;;;GAGG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAwB;gBAEzC,YAAY,EAAE,qBAAqB;IAI/C;;OAEG;IACG,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAqBlE;;OAEG;IACG,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,sBAAsB,CAAC;IA6FrE;;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;IAsCxB,sDAAsD;IACtD,OAAO,CAAC,cAAc;IAiDtB,oDAAoD;IACpD,OAAO,CAAC,yBAAyB;IAoDjC,gDAAgD;IAChD,OAAO,CAAC,oBAAoB;IAuC5B,yDAAyD;IACzD,OAAO,CAAC,qBAAqB;IAoD7B,wEAAwE;IACxE,OAAO,CAAC,kBAAkB;IAmD1B,8CAA8C;IAC9C,OAAO,CAAC,kBAAkB;IA6B1B,mDAAmD;YACrC,uBAAuB;IAyDrC,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"}
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
- constructor(introspector) {
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 findings = await this.checkSlice(projectPath, sliceIndex, slicePlanResult, slicePlanPath);
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 === 'complete';
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 === 'complete';
253
+ const tasksComplete = taskResult.inferredStatus === STATUS.Complete;
225
254
  const sliceDesignFullPath = sliceDesignRelPath
226
255
  ? join(projectPath, sliceDesignRelPath)
227
256
  : frontmatter.filePath;
228
- if (fmStatus === 'complete' && !tasksComplete) {
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: 'in-progress' },
268
+ detail: { key: 'status', value: STATUS.InProgress },
240
269
  },
241
270
  });
242
271
  }
243
- if ((fmStatus === 'in-progress' || fmStatus === 'not-started') && tasksComplete) {
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: 'complete' },
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 !== 'complete') {
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: 'complete' },
359
+ detail: { key: 'status', value: STATUS.Complete },
313
360
  },
314
361
  });
315
362
  }
316
- if (!planChecked && fmStatus === 'complete' && slicePlanPath) {
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; // 'complete' | 'in-progress' | 'not-started'
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 === 'complete' && computed !== 'complete') {
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 !== 'complete' && computed === 'complete') {
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: 'complete' },
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 === 'complete' && !allComplete) {
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: 'in-progress' },
555
+ detail: { key: 'status', value: STATUS.InProgress },
428
556
  },
429
557
  });
430
558
  }
431
- if (planStatus !== 'complete' && allComplete && slicePlanResult.totalSlices > 0) {
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: 'complete' },
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 === 'complete' && !allComplete) {
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: 'in-progress' },
633
+ detail: { key: 'status', value: STATUS.InProgress },
474
634
  },
475
635
  });
476
636
  }
477
- if (archStatus !== 'complete' && allComplete && slicePlanResult.totalSlices > 0) {
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: 'complete' },
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 === 'complete';
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: 'complete' },
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 === 'complete' && !allComplete) {
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: 'in-progress' },
765
+ detail: { key: 'status', value: STATUS.InProgress },
606
766
  },
607
767
  });
608
768
  }
609
- if (planStatus !== 'complete' && allComplete) {
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: 'complete' },
780
+ detail: { key: 'status', value: STATUS.Complete },
621
781
  },
622
782
  });
623
783
  }