@interf/compiler 0.33.0 → 0.50.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.
Files changed (234) hide show
  1. package/README.md +122 -226
  2. package/dist/cli/commands/agents.js +1 -32
  3. package/dist/cli/commands/benchmark.d.ts +2 -3
  4. package/dist/cli/commands/benchmark.js +1 -31
  5. package/dist/cli/commands/build-plan.js +26 -50
  6. package/dist/cli/commands/build.d.ts +2 -3
  7. package/dist/cli/commands/build.js +1 -31
  8. package/dist/cli/commands/graphs.js +177 -32
  9. package/dist/cli/commands/mcp.d.ts +1 -0
  10. package/dist/cli/commands/mcp.js +223 -126
  11. package/dist/cli/commands/project.js +10 -36
  12. package/dist/cli/commands/reset.d.ts +2 -3
  13. package/dist/cli/commands/reset.js +1 -22
  14. package/dist/cli/commands/runs.js +86 -33
  15. package/dist/cli/commands/status.js +3 -24
  16. package/dist/cli/commands/traces.js +1 -29
  17. package/dist/cli/commands/wizard.js +17 -29
  18. package/dist/cli/lib/http-client.d.ts +39 -0
  19. package/dist/cli/lib/http-client.js +73 -0
  20. package/dist/packages/build-plans/authoring/brief.d.ts +25 -4
  21. package/dist/packages/build-plans/authoring/build-plan-authoring.d.ts +42 -1
  22. package/dist/packages/build-plans/authoring/build-plan-authoring.js +470 -63
  23. package/dist/packages/build-plans/authoring/build-plan-edit-session.d.ts +9 -0
  24. package/dist/packages/build-plans/authoring/build-plan-edit-session.js +27 -10
  25. package/dist/packages/build-plans/authoring/build-plan-improvement.js +62 -8
  26. package/dist/packages/build-plans/authoring/lib/build-plan-edit-utils.d.ts +1 -0
  27. package/dist/packages/build-plans/package/build-plan-definitions.d.ts +0 -1
  28. package/dist/packages/build-plans/package/build-plan-definitions.js +5 -3
  29. package/dist/packages/build-plans/package/build-plan-stage-runner.d.ts +1 -0
  30. package/dist/packages/build-plans/package/build-plan-stage-runner.js +2 -1
  31. package/dist/packages/build-plans/package/builtin-build-plan.d.ts +2 -2
  32. package/dist/packages/build-plans/package/builtin-build-plan.js +3 -3
  33. package/dist/packages/build-plans/package/context-interface.d.ts +3 -0
  34. package/dist/packages/build-plans/package/context-interface.js +5 -5
  35. package/dist/packages/build-plans/package/interf-build-plan-package.js +22 -22
  36. package/dist/packages/build-plans/package/local-build-plans.d.ts +10 -5
  37. package/dist/packages/build-plans/package/local-build-plans.js +57 -32
  38. package/dist/packages/contracts/index.d.ts +4 -3
  39. package/dist/packages/contracts/index.js +2 -1
  40. package/dist/packages/contracts/lib/context-graph-layer.d.ts +161 -0
  41. package/dist/packages/contracts/lib/context-graph-layer.js +216 -0
  42. package/dist/packages/contracts/lib/project-paths.d.ts +7 -0
  43. package/dist/packages/contracts/lib/project-paths.js +9 -0
  44. package/dist/packages/contracts/lib/project-schema.d.ts +264 -1
  45. package/dist/packages/contracts/lib/project-schema.js +38 -13
  46. package/dist/packages/contracts/lib/schema.d.ts +556 -23
  47. package/dist/packages/contracts/lib/schema.js +279 -18
  48. package/dist/packages/contracts/utils/filesystem.d.ts +1 -0
  49. package/dist/packages/contracts/utils/filesystem.js +29 -1
  50. package/dist/packages/projects/lib/schema.d.ts +6 -8
  51. package/dist/packages/projects/lib/schema.js +3 -1
  52. package/dist/packages/projects/source-config.d.ts +0 -5
  53. package/dist/packages/projects/source-config.js +9 -22
  54. package/dist/packages/runtime/actions/fields.d.ts +4 -0
  55. package/dist/packages/runtime/actions/form-builders.js +79 -31
  56. package/dist/packages/runtime/actions/form-validators.js +9 -3
  57. package/dist/packages/runtime/actions/helpers.js +3 -3
  58. package/dist/packages/runtime/actions/registry.d.ts +1 -1
  59. package/dist/packages/runtime/actions/registry.js +1 -1
  60. package/dist/packages/runtime/actions/requests.d.ts +1 -1
  61. package/dist/packages/runtime/actions/requests.js +12 -6
  62. package/dist/packages/runtime/actions/schemas.d.ts +7 -0
  63. package/dist/packages/runtime/actions/schemas.js +1 -0
  64. package/dist/packages/runtime/agent-handoff.js +8 -7
  65. package/dist/packages/runtime/agents/lib/execution-profile.d.ts +14 -0
  66. package/dist/packages/runtime/agents/lib/execution-profile.js +23 -0
  67. package/dist/packages/runtime/agents/lib/execution.js +14 -8
  68. package/dist/packages/runtime/agents/lib/executors.d.ts +1 -0
  69. package/dist/packages/runtime/agents/lib/executors.js +11 -2
  70. package/dist/packages/runtime/agents/lib/logs.d.ts +10 -0
  71. package/dist/packages/runtime/agents/lib/logs.js +32 -8
  72. package/dist/packages/runtime/agents/lib/preflight.js +4 -1
  73. package/dist/packages/runtime/agents/lib/render.d.ts +18 -0
  74. package/dist/packages/runtime/agents/lib/render.js +44 -18
  75. package/dist/packages/runtime/agents/lib/shell-templates.js +105 -63
  76. package/dist/packages/runtime/agents/lib/shells.d.ts +29 -0
  77. package/dist/packages/runtime/agents/lib/shells.js +158 -32
  78. package/dist/packages/runtime/agents/lib/source-context-scan.d.ts +10 -0
  79. package/dist/packages/runtime/agents/lib/source-context-scan.js +388 -0
  80. package/dist/packages/runtime/agents/lib/status.js +1 -14
  81. package/dist/packages/runtime/agents/lib/string-utils.d.ts +16 -0
  82. package/dist/packages/runtime/agents/lib/string-utils.js +36 -0
  83. package/dist/packages/runtime/agents/lib/types.d.ts +1 -0
  84. package/dist/packages/runtime/agents/providers/codex.js +2 -0
  85. package/dist/packages/runtime/agents/role-executors.js +2 -1
  86. package/dist/packages/runtime/auth/session-store.js +11 -3
  87. package/dist/packages/runtime/benchmark-question-draft.d.ts +3 -0
  88. package/dist/packages/runtime/benchmark-question-draft.js +57 -28
  89. package/dist/packages/runtime/build/artifact-status.d.ts +1 -1
  90. package/dist/packages/runtime/build/artifact-status.js +1 -1
  91. package/dist/packages/runtime/build/build-evidence.d.ts +2 -1
  92. package/dist/packages/runtime/build/build-evidence.js +11 -5
  93. package/dist/packages/runtime/build/build-pipeline.js +89 -5
  94. package/dist/packages/runtime/build/build-stage-plan.js +3 -1
  95. package/dist/packages/runtime/build/build-stage-runner.js +169 -32
  96. package/dist/packages/runtime/build/build-target.d.ts +3 -0
  97. package/dist/packages/runtime/build/build-target.js +25 -1
  98. package/dist/packages/runtime/build/check-evaluator.d.ts +1 -1
  99. package/dist/packages/runtime/build/check-evaluator.js +655 -4
  100. package/dist/packages/runtime/build/context-graph-paths.d.ts +13 -0
  101. package/dist/packages/runtime/build/context-graph-paths.js +27 -0
  102. package/dist/packages/runtime/build/index.d.ts +2 -2
  103. package/dist/packages/runtime/build/index.js +2 -2
  104. package/dist/packages/runtime/build/inspect-map.d.ts +10 -0
  105. package/dist/packages/runtime/build/inspect-map.js +270 -0
  106. package/dist/packages/runtime/build/lib/schema.d.ts +246 -53
  107. package/dist/packages/runtime/build/lib/schema.js +173 -15
  108. package/dist/packages/runtime/build/native-entrypoint.d.ts +2 -0
  109. package/dist/packages/runtime/build/native-entrypoint.js +286 -0
  110. package/dist/packages/runtime/build/runtime-contracts.js +9 -3
  111. package/dist/packages/runtime/build/runtime-log-paths.d.ts +3 -0
  112. package/dist/packages/runtime/build/runtime-log-paths.js +16 -0
  113. package/dist/packages/runtime/build/runtime-prompt.js +6 -4
  114. package/dist/packages/runtime/build/runtime-runs.js +63 -10
  115. package/dist/packages/runtime/build/runtime-types.d.ts +4 -1
  116. package/dist/packages/runtime/build/runtime.d.ts +3 -1
  117. package/dist/packages/runtime/build/runtime.js +3 -1
  118. package/dist/packages/runtime/build/source-files.js +11 -2
  119. package/dist/packages/runtime/build/source-inventory.d.ts +1 -0
  120. package/dist/packages/runtime/build/source-inventory.js +246 -7
  121. package/dist/packages/runtime/build/source-manifest.d.ts +11 -0
  122. package/dist/packages/runtime/build/source-manifest.js +30 -2
  123. package/dist/packages/runtime/build/stage-evidence.js +80 -11
  124. package/dist/packages/runtime/build/stage-manifest.d.ts +45 -0
  125. package/dist/packages/runtime/build/stage-manifest.js +1125 -0
  126. package/dist/packages/runtime/build/stage-reuse.js +12 -0
  127. package/dist/packages/runtime/build/stage-session.d.ts +81 -0
  128. package/dist/packages/runtime/build/stage-session.js +308 -0
  129. package/dist/packages/runtime/build/state-io.js +10 -11
  130. package/dist/packages/runtime/build/state-view.js +1 -1
  131. package/dist/packages/runtime/build/state.d.ts +1 -1
  132. package/dist/packages/runtime/build/state.js +1 -1
  133. package/dist/packages/runtime/build/summary-coverage-index.d.ts +21 -0
  134. package/dist/packages/runtime/build/summary-coverage-index.js +189 -0
  135. package/dist/packages/runtime/build/traces.js +3 -3
  136. package/dist/packages/runtime/build/validate-context-graph.d.ts +1 -1
  137. package/dist/packages/runtime/build/validate-context-graph.js +5 -5
  138. package/dist/packages/runtime/build/validate.d.ts +1 -1
  139. package/dist/packages/runtime/build/validate.js +1 -1
  140. package/dist/packages/runtime/client.d.ts +3 -3
  141. package/dist/packages/runtime/client.js +8 -13
  142. package/dist/packages/runtime/context-checks.js +13 -0
  143. package/dist/packages/runtime/context-graph-scaffold.js +2 -1
  144. package/dist/packages/runtime/context-graph-semantic-graph.d.ts +9 -0
  145. package/dist/packages/runtime/context-graph-semantic-graph.js +416 -0
  146. package/dist/packages/runtime/execution/lib/schema.d.ts +34 -31
  147. package/dist/packages/runtime/index.d.ts +2 -2
  148. package/dist/packages/runtime/index.js +1 -1
  149. package/dist/packages/runtime/native-run-handlers.d.ts +38 -0
  150. package/dist/packages/runtime/native-run-handlers.js +52 -33
  151. package/dist/packages/runtime/plan-artifact-contract.js +1 -1
  152. package/dist/packages/runtime/project-source-state.d.ts +4 -4
  153. package/dist/packages/runtime/project-source-state.js +5 -2
  154. package/dist/packages/runtime/project-store.d.ts +5 -0
  155. package/dist/packages/runtime/project-store.js +30 -3
  156. package/dist/packages/runtime/requested-artifacts.js +1 -1
  157. package/dist/packages/runtime/run-observability.js +9 -4
  158. package/dist/packages/runtime/runtime-action-proposals.js +3 -3
  159. package/dist/packages/runtime/runtime-build-plans.js +47 -3
  160. package/dist/packages/runtime/runtime-build-runs.js +9 -16
  161. package/dist/packages/runtime/runtime-caches.d.ts +26 -0
  162. package/dist/packages/runtime/runtime-caches.js +47 -0
  163. package/dist/packages/runtime/runtime-jobs.js +6 -6
  164. package/dist/packages/runtime/runtime-project-mutations.js +1 -0
  165. package/dist/packages/runtime/runtime-project-reads.d.ts +4 -1
  166. package/dist/packages/runtime/runtime-project-reads.js +229 -36
  167. package/dist/packages/runtime/runtime-proposal-helpers.js +6 -6
  168. package/dist/packages/runtime/runtime-resource-builders.d.ts +4 -2
  169. package/dist/packages/runtime/runtime-resource-builders.js +16 -14
  170. package/dist/packages/runtime/runtime-status.d.ts +14 -0
  171. package/dist/packages/runtime/runtime-status.js +15 -0
  172. package/dist/packages/runtime/runtime-verify-runs.js +6 -5
  173. package/dist/packages/runtime/runtime.d.ts +439 -22
  174. package/dist/packages/runtime/runtime.js +16 -2
  175. package/dist/packages/runtime/schemas/actions.d.ts +24 -0
  176. package/dist/packages/runtime/schemas/agents.d.ts +28 -0
  177. package/dist/packages/runtime/schemas/agents.js +33 -0
  178. package/dist/packages/runtime/schemas/build-plans.d.ts +181 -8
  179. package/dist/packages/runtime/schemas/build-plans.js +36 -2
  180. package/dist/packages/runtime/schemas/context-graphs.d.ts +1522 -0
  181. package/dist/packages/runtime/schemas/context-graphs.js +110 -0
  182. package/dist/packages/runtime/schemas/files.d.ts +7 -347
  183. package/dist/packages/runtime/schemas/files.js +1 -24
  184. package/dist/packages/runtime/schemas/index.d.ts +1 -0
  185. package/dist/packages/runtime/schemas/index.js +1 -0
  186. package/dist/packages/runtime/schemas/jobs.js +4 -0
  187. package/dist/packages/runtime/schemas/projects.d.ts +48 -21
  188. package/dist/packages/runtime/schemas/projects.js +34 -10
  189. package/dist/packages/runtime/schemas/runs.d.ts +1009 -240
  190. package/dist/packages/runtime/schemas/runs.js +17 -0
  191. package/dist/packages/runtime/service/openapi.js +1 -0
  192. package/dist/packages/runtime/service/operations.d.ts +1666 -145
  193. package/dist/packages/runtime/service/operations.js +147 -17
  194. package/dist/packages/runtime/service/routes.d.ts +11 -3
  195. package/dist/packages/runtime/service/routes.js +11 -3
  196. package/dist/packages/runtime/service/server-app-boot.js +2 -2
  197. package/dist/packages/runtime/service/server-helpers.d.ts +11 -0
  198. package/dist/packages/runtime/service/server-helpers.js +19 -0
  199. package/dist/packages/runtime/service/server-routes-action-proposals.js +4 -2
  200. package/dist/packages/runtime/service/server-routes-agents.js +19 -85
  201. package/dist/packages/runtime/service/server-routes-build-plans.js +14 -11
  202. package/dist/packages/runtime/service/server-routes-project-context.js +102 -7
  203. package/dist/packages/runtime/service/server-routes-project-jobs.js +19 -12
  204. package/dist/packages/runtime/service/server-routes-project-runs.js +5 -2
  205. package/dist/packages/runtime/service/server-routes-projects.js +6 -2
  206. package/dist/packages/runtime/service/server-routes-runs.js +11 -4
  207. package/dist/packages/runtime/verify/lib/schema.js +12 -0
  208. package/dist/packages/runtime/verify/test-file-guard.d.ts +2 -0
  209. package/dist/packages/runtime/verify/test-file-guard.js +29 -0
  210. package/dist/packages/runtime/verify/verify-execution.d.ts +7 -0
  211. package/dist/packages/runtime/verify/verify-execution.js +109 -35
  212. package/dist/packages/runtime/verify/verify-paths.d.ts +1 -0
  213. package/dist/packages/runtime/verify/verify-paths.js +4 -0
  214. package/dist/packages/runtime/verify/verify-specs.js +49 -39
  215. package/dist/packages/runtime/wire-schemas.d.ts +1 -1
  216. package/dist/packages/runtime/wire-schemas.js +1 -1
  217. package/package.json +2 -8
  218. package/public-repo/CONTRIBUTING.md +10 -3
  219. package/public-repo/README.md +122 -226
  220. package/public-repo/build-plans/interf-default/README.md +15 -12
  221. package/public-repo/build-plans/interf-default/build/stages/entrypoint/SKILL.md +74 -0
  222. package/public-repo/build-plans/interf-default/build/stages/knowledge/SKILL.md +95 -0
  223. package/public-repo/build-plans/interf-default/build/stages/summarize/SKILL.md +38 -5
  224. package/public-repo/build-plans/interf-default/build-plan.json +27 -23
  225. package/public-repo/build-plans/interf-default/build-plan.schema.json +24 -20
  226. package/public-repo/build-plans/interf-default/use/query/SKILL.md +8 -7
  227. package/public-repo/openapi/local-service.openapi.json +11637 -4213
  228. package/public-repo/skills/interf/SKILL.md +174 -134
  229. package/dist/packages/runtime/build/runtime-paths.d.ts +0 -8
  230. package/dist/packages/runtime/build/runtime-paths.js +0 -26
  231. package/dist/packages/runtime/build/state-paths.d.ts +0 -7
  232. package/dist/packages/runtime/build/state-paths.js +0 -22
  233. package/public-repo/build-plans/interf-default/build/stages/shape/SKILL.md +0 -34
  234. package/public-repo/build-plans/interf-default/build/stages/structure/SKILL.md +0 -28
@@ -7,9 +7,11 @@ import { readJsonFileUnchecked, readJsonFileWithSchema } from "../../contracts/u
7
7
  import { isMarkdownFile } from "../../contracts/utils/file-types.js";
8
8
  import { BuildPlanRuntimeApiSchema, BuildPlanPurposeSchema, BuildPlanStageArtifactReadAccessSchema, BuildPlanStageArtifactWriteAccessSchema, } from "../../runtime/build/lib/schema.js";
9
9
  import { RuntimeContractTypeSchema, InterfIdPattern, } from "../../contracts/lib/schema.js";
10
+ import { CANONICAL_LAYER_DIRS, } from "../../contracts/lib/context-graph-layer.js";
10
11
  import { asProjectDataDir, projectBuildPlansRoot, } from "../../contracts/lib/project-paths.js";
11
12
  import { CONTEXT_INTERFACE_FILE, ContextInterfaceSchema, contextInterfaceExists, contextInterfaceFilePath, listContextInterfaceArtifacts, readContextInterface, BuildPlanInputSpecSchema, writeContextInterface, } from "./context-interface.js";
12
13
  import { BuildPlanAuthoringBriefSchema, } from "../authoring/brief.js";
14
+ import { DEFAULT_BUILD_PLAN_ID } from "../build-plan-resolution.js";
13
15
  import { PACKAGE_ROOT } from "./lib/package-root.js";
14
16
  const LocalBuildPlanStageDefinitionSchema = z.object({
15
17
  id: z.string().regex(InterfIdPattern),
@@ -73,16 +75,20 @@ export function builtinBuildPlanPackagePath(buildPlanId) {
73
75
  export function buildPlanDefinitionPath(projectDataDir, id) {
74
76
  return join(buildPlanRootPath(projectDataDir), id);
75
77
  }
78
+ // The single, readable list of which package-relative docs `collectStarterDocs`
79
+ // and `buildPlanPackageCopyPaths` treat as portable starter docs: the package
80
+ // README plus the three authoring-doc trees. A relative path qualifies when it
81
+ // equals or lives under one of these entries.
82
+ const SUPPORTED_STARTER_DOC_PATHS = [
83
+ "README.md",
84
+ "improve/",
85
+ "use/query/",
86
+ "build/stages/",
87
+ ];
76
88
  function isSupportedBuildPlanStarterDocPath(relativePath) {
77
- if (relativePath === "README.md")
78
- return true;
79
- if (relativePath.startsWith("improve/"))
80
- return true;
81
- if (relativePath.startsWith("use/query/"))
82
- return true;
83
- if (relativePath.startsWith("build/stages/"))
84
- return true;
85
- return false;
89
+ return SUPPORTED_STARTER_DOC_PATHS.some((supported) => supported.endsWith("/")
90
+ ? relativePath.startsWith(supported)
91
+ : relativePath === supported);
86
92
  }
87
93
  function collectStarterDocs(dirPath) {
88
94
  return listFilesRecursive(dirPath, isMarkdownFile)
@@ -92,7 +98,11 @@ function collectStarterDocs(dirPath) {
92
98
  relativePath,
93
99
  content: readFileSync(join(dirPath, relativePath), "utf8"),
94
100
  }))
95
- .sort((a, b) => a.relativePath.localeCompare(b.relativePath));
101
+ // Codepoint order, not locale order: starter-doc ordering must be
102
+ // deterministic across machines (full vs small ICU, LANG/LC_ALL). With
103
+ // localeCompare, "README.md" sorts before lowercase paths in some locales
104
+ // and after in others, which made this collection environment-dependent.
105
+ .sort((a, b) => (a.relativePath < b.relativePath ? -1 : a.relativePath > b.relativePath ? 1 : 0));
96
106
  }
97
107
  function buildPlanPackageCopyPaths(dirPath) {
98
108
  return [
@@ -188,7 +198,7 @@ export function resolveBuildPlanPackageSourcePath(projectDataDir, buildPlanId) {
188
198
  return null;
189
199
  }
190
200
  export function seedLocalDefaultBuildPlan(options) {
191
- const buildPlanId = "interf-default";
201
+ const buildPlanId = DEFAULT_BUILD_PLAN_ID;
192
202
  const targetPath = buildPlanDefinitionPath(options.projectDataDir, buildPlanId);
193
203
  if (existsSync(join(targetPath, "build-plan.json"))) {
194
204
  return { buildPlanId, targetPath, alreadyExisted: true };
@@ -201,7 +211,7 @@ export function seedLocalDefaultBuildPlan(options) {
201
211
  patchBuildPlanPackageMetadata(targetPath, {
202
212
  id: buildPlanId,
203
213
  label: "Built-in Interf Build Plan",
204
- hint: "Built-in file-processing Build Plan: summarize source-grounded evidence, structure cross-file connections, and shape the Context Graph around the saved Checks.",
214
+ hint: "Built-in file-processing Build Plan: summarize source-grounded evidence, build task-aware knowledge, and shape the Context Graph around the Project intent and coverage metrics.",
205
215
  });
206
216
  return { buildPlanId, targetPath, alreadyExisted: false };
207
217
  }
@@ -392,7 +402,10 @@ export function validateBuildPlanPackage(dirPath) {
392
402
  const longer = a.length <= b.length ? b : a;
393
403
  return shorter.every((part, index) => longer[index] === part);
394
404
  };
395
- const requiredLayerPaths = new Set(["summaries", "knowledge", "artifacts"]);
405
+ // Canonical required layer dirs come from the central layer model. Package
406
+ // validation is a strict-declaration context (it mirrors authoring), so it
407
+ // matches the exact lowercase dir name a Build Plan must declare.
408
+ const requiredLayerPaths = new Set(CANONICAL_LAYER_DIRS);
396
409
  const isRequiredLayerArtifact = (artifact) => artifact.kind === "directory" && requiredLayerPaths.has(normalizedPathKey(artifact.path));
397
410
  const allowsRequiredLayerContainment = (artifact, existingArtifact) => {
398
411
  const artifactPath = normalizedPathKey(artifact.path);
@@ -405,58 +418,58 @@ export function validateBuildPlanPackage(dirPath) {
405
418
  };
406
419
  for (const artifact of schemaArtifacts) {
407
420
  if (seenArtifactIds.has(artifact.id)) {
408
- errors.push(`build-plan.schema.json repeats Artifact id "${artifact.id}".`);
421
+ errors.push(`build-plan.schema.json repeats requested output id "${artifact.id}".`);
409
422
  }
410
423
  seenArtifactIds.add(artifact.id);
411
424
  const pathKey = normalizedPathKey(artifact.path);
412
425
  const existingPathOwner = seenArtifactsByPath.get(pathKey);
413
426
  if (existingPathOwner) {
414
- errors.push(`build-plan.schema.json repeats Artifact path "${artifact.path}".`);
427
+ errors.push(`build-plan.schema.json repeats requested output path "${artifact.path}".`);
415
428
  }
416
429
  for (const existingArtifact of seenArtifactsByPath.values()) {
417
430
  if (normalizedPathKey(existingArtifact.path) === pathKey)
418
431
  continue;
419
432
  if (hasOverlappingPath(artifact.path, existingArtifact.path) &&
420
433
  !allowsRequiredLayerContainment(artifact, existingArtifact)) {
421
- errors.push(`build-plan.schema.json Artifacts "${artifact.id}" and "${existingArtifact.id}" overlap on path "${artifact.path}" / "${existingArtifact.path}".`);
434
+ errors.push(`build-plan.schema.json requested outputs "${artifact.id}" and "${existingArtifact.id}" overlap on path "${artifact.path}" / "${existingArtifact.path}".`);
422
435
  }
423
436
  }
424
437
  seenArtifactsByPath.set(pathKey, artifact);
425
438
  for (const owner of artifact.owned_by) {
426
439
  if (!stageIds.has(owner)) {
427
- errors.push(`build-plan.schema.json references unknown stage "${owner}" for Artifact path "${artifact.path}".`);
440
+ errors.push(`build-plan.schema.json references unknown stage "${owner}" for requested output path "${artifact.path}".`);
428
441
  continue;
429
442
  }
430
443
  const ownerStage = stages.find((stage) => stage.id === owner);
431
444
  if (ownerStage && !ownerStage.writes.includes(artifact.id)) {
432
- errors.push(`build-plan.schema.json Artifact "${artifact.id}" is owned by stage "${owner}" but that stage does not write it in build-plan.json.`);
445
+ errors.push(`build-plan.schema.json requested output "${artifact.id}" is owned by stage "${owner}" but that stage does not write it in build-plan.json.`);
433
446
  }
434
447
  }
435
448
  }
436
- for (const requiredPath of ["summaries", "knowledge", "artifacts"]) {
449
+ for (const requiredPath of CANONICAL_LAYER_DIRS) {
437
450
  const artifact = seenArtifactsByPath.get(requiredPath);
438
451
  if (!artifact) {
439
- errors.push(`File-based Build Plan packages must declare a writable Artifact at "${requiredPath}".`);
452
+ errors.push(`File-based Build Plan packages must declare a writable requested output at "${requiredPath}".`);
440
453
  continue;
441
454
  }
442
455
  if (artifact.owned_by.length === 0) {
443
- errors.push(`File-based Build Plan Artifact "${artifact.id}" at "${requiredPath}" must be owned by at least one stage.`);
456
+ errors.push(`File-based Build Plan requested output "${artifact.id}" at "${requiredPath}" must be owned by at least one stage.`);
444
457
  }
445
458
  }
446
459
  for (const stage of stages) {
447
460
  for (const artifactId of stage.reads) {
448
461
  if (!artifactById.has(artifactId)) {
449
- errors.push(`Stage "${stage.id}" reads unknown Artifact "${artifactId}".`);
462
+ errors.push(`Stage "${stage.id}" reads unknown requested output "${artifactId}".`);
450
463
  }
451
464
  }
452
465
  for (const artifactId of stage.writes) {
453
466
  const artifact = artifactById.get(artifactId);
454
467
  if (!artifact) {
455
- errors.push(`Stage "${stage.id}" writes unknown Artifact "${artifactId}".`);
468
+ errors.push(`Stage "${stage.id}" writes unknown requested output "${artifactId}".`);
456
469
  continue;
457
470
  }
458
471
  if (!artifact.owned_by.includes(stage.id)) {
459
- errors.push(`Stage "${stage.id}" writes Artifact "${artifactId}" but that Artifact is not owned by the stage in build-plan.schema.json.`);
472
+ errors.push(`Stage "${stage.id}" writes requested output "${artifactId}" but that output is not owned by the stage in build-plan.schema.json.`);
460
473
  }
461
474
  }
462
475
  }
@@ -477,21 +490,33 @@ export function validateBuildPlanPackage(dirPath) {
477
490
  counts,
478
491
  };
479
492
  }
480
- function describeContextInterfaceValidationIssues(dirPath) {
493
+ /**
494
+ * Explain WHY a `build-plan.schema.json` failed to read. Exported so the
495
+ * regression test can assert the corrected branch directly: a valid schema must
496
+ * return `[]` (no issues), an invalid one must return the concrete Zod issues.
497
+ * The previous inverted `if (parsed.success)` reported a VALID schema as
498
+ * "missing or invalid."
499
+ */
500
+ export function describeContextInterfaceValidationIssues(dirPath) {
481
501
  const buildPlanSchemaPath = contextInterfaceFilePath(dirPath);
482
502
  if (!existsSync(buildPlanSchemaPath)) {
483
503
  return ["build-plan.schema.json is missing."];
484
504
  }
485
- const raw = readJsonFileUnchecked(buildPlanSchemaPath, "Build Plan requested Artifact contract");
505
+ const raw = readJsonFileUnchecked(buildPlanSchemaPath, "Build Plan requested output contract");
486
506
  if (raw === null) {
487
507
  return ["build-plan.schema.json is invalid JSON."];
488
508
  }
489
509
  const parsed = ContextInterfaceSchema.safeParse(raw);
490
- if (parsed.success) {
491
- return ["build-plan.schema.json is missing or invalid."];
510
+ if (!parsed.success) {
511
+ // Invalid schema: surface the concrete Zod issues. Matches the sibling
512
+ // `readContextInterface` (context-interface.ts:174), which treats
513
+ // `!parsed.success` as the failure case. The previous `if (parsed.success)`
514
+ // was inverted and reported a VALID schema as "missing or invalid."
515
+ return parsed.error.issues.map((issue) => {
516
+ const path = issue.path.length > 0 ? issue.path.join(".") : "<root>";
517
+ return `build-plan.schema.json ${path}: ${issue.message}`;
518
+ });
492
519
  }
493
- return parsed.error.issues.map((issue) => {
494
- const path = issue.path.length > 0 ? issue.path.join(".") : "<root>";
495
- return `build-plan.schema.json ${path}: ${issue.message}`;
496
- });
520
+ // A valid schema has no issues to describe.
521
+ return [];
497
522
  }
@@ -1,5 +1,6 @@
1
1
  export * as schema from "./lib/schema.js";
2
2
  export * as projectSchema from "./lib/project-schema.js";
3
- export type { AgentRecord, AgentsRegistry, Artifact, ArtifactId, ArtifactShape, ArtifactStatus, CanonicalRole, Check, CheckKind, BuildCheckRow, BuildEvidenceIssueState, BuildEvidenceMetric, BuildEvidenceRef, BuildEvidenceResource, ContextCheck, ContextCheckDeclaration, ContextCheckProjection, ContextCheckStatus, GateStatus, Locator, LocatorKind, BuildPlanId, ProjectId, CheckResult, ReadinessGate, ReadinessStatus, Readiness, ReadyVerdict, RoleMap, RuntimeContractType, RuntimeExecutorInfo, RuntimeStage, RuntimeTargetType, Source, SourceFile, SourceFiles, SourceKind, SourceState, StageEvidence, StageEvidenceCount, StageEvidenceEdge, StageEvidenceItem, StageEvidenceOutputRef, StageEvidenceSourceRef, StageInput, StageInputs, StandardEvidenceFrontmatterKey, TestCaseExpect, TestTargetType, VerifyTargetResult, } from "./lib/schema.js";
4
- export type { ContextGraph, ContextGraphLocator, DriftKind, ProjectName, ProjectSourceState, Trace, Traces, } from "./lib/project-schema.js";
5
- export { CANONICAL_ROLES, CHECK_PARAM_CONTRACTS, CHECK_KINDS, ContextCheckDeclarationSchema, ContextCheckProjectionSchema, ContextCheckSchema, ContextCheckStatusSchema, BuildCheckRowSchema, BuildEvidenceIssueStateSchema, BuildEvidenceMetricSchema, BuildEvidenceRefSchema, BuildEvidenceResourceSchema, EvidenceCountKeySchema, StageEvidenceCountSchema, StageEvidenceEdgeSchema, StageEvidenceItemSchema, StageEvidenceOutputRefSchema, StageEvidenceSchema, StageEvidenceSourceRefSchema, STANDARD_EVIDENCE_FRONTMATTER_KEYS, } from "./lib/schema.js";
3
+ export type { AgentRecord, AgentsRegistry, Artifact, ArtifactId, ArtifactShape, ArtifactStatus, CanonicalRole, Check, CheckKind, BuildCheckRow, BuildEvidenceIssueState, BuildEvidenceMetric, BuildEvidenceRef, BuildEvidenceResource, ContextCheck, ContextCheckDeclaration, ContextCheckProjection, ContextCheckStatus, GateStatus, Locator, LocatorKind, BuildPlanId, ProjectId, ProjectIntent, CheckResult, ReadinessGate, ReadinessStatus, Readiness, ReadyVerdict, RoleMap, RuntimeContractType, RuntimeExecutorInfo, RuntimeStage, RuntimeTargetType, Source, SourceBinding, SourceFile, SourceFiles, SourceKind, SourceState, StageEvidence, StageEvidenceCount, StageEvidenceEdge, StageEvidenceItem, StageEvidenceOutputRef, StageEvidenceSourceRef, StageInput, StageInputs, StandardEvidenceFrontmatterKey, TestCaseExpect, TestTargetType, VerifyTargetResult, } from "./lib/schema.js";
4
+ export type { ContextGraph, ContextGraphArtifactHandoff, ContextGraphHandoff, ContextGraphLocator, ContextGraphPortableHandoff, DriftKind, ProjectName, ProjectSourceState, Trace, Traces, } from "./lib/project-schema.js";
5
+ export { CANONICAL_ROLES, CHECK_PARAM_CONTRACTS, CHECK_KINDS, ContextCheckDeclarationSchema, ContextCheckProjectionSchema, ContextCheckSchema, ContextCheckStatusSchema, ProjectIntentSchema, SourceBindingSchema, BuildCheckRowSchema, BuildEvidenceIssueStateSchema, BuildEvidenceMetricSchema, BuildEvidenceRefSchema, BuildEvidenceResourceSchema, EvidenceCountKeySchema, StageEvidenceCountSchema, StageEvidenceEdgeSchema, StageEvidenceItemSchema, StageEvidenceOutputRefSchema, StageEvidenceSchema, StageEvidenceSourceRefSchema, STANDARD_EVIDENCE_FRONTMATTER_KEYS, } from "./lib/schema.js";
6
+ export { ContextGraphArtifactHandoffSchema, ContextGraphHandoffSchema, ContextGraphPortableHandoffSchema, } from "./lib/project-schema.js";
@@ -1,3 +1,4 @@
1
1
  export * as schema from "./lib/schema.js";
2
2
  export * as projectSchema from "./lib/project-schema.js";
3
- export { CANONICAL_ROLES, CHECK_PARAM_CONTRACTS, CHECK_KINDS, ContextCheckDeclarationSchema, ContextCheckProjectionSchema, ContextCheckSchema, ContextCheckStatusSchema, BuildCheckRowSchema, BuildEvidenceIssueStateSchema, BuildEvidenceMetricSchema, BuildEvidenceRefSchema, BuildEvidenceResourceSchema, EvidenceCountKeySchema, StageEvidenceCountSchema, StageEvidenceEdgeSchema, StageEvidenceItemSchema, StageEvidenceOutputRefSchema, StageEvidenceSchema, StageEvidenceSourceRefSchema, STANDARD_EVIDENCE_FRONTMATTER_KEYS, } from "./lib/schema.js";
3
+ export { CANONICAL_ROLES, CHECK_PARAM_CONTRACTS, CHECK_KINDS, ContextCheckDeclarationSchema, ContextCheckProjectionSchema, ContextCheckSchema, ContextCheckStatusSchema, ProjectIntentSchema, SourceBindingSchema, BuildCheckRowSchema, BuildEvidenceIssueStateSchema, BuildEvidenceMetricSchema, BuildEvidenceRefSchema, BuildEvidenceResourceSchema, EvidenceCountKeySchema, StageEvidenceCountSchema, StageEvidenceEdgeSchema, StageEvidenceItemSchema, StageEvidenceOutputRefSchema, StageEvidenceSchema, StageEvidenceSourceRefSchema, STANDARD_EVIDENCE_FRONTMATTER_KEYS, } from "./lib/schema.js";
4
+ export { ContextGraphArtifactHandoffSchema, ContextGraphHandoffSchema, ContextGraphPortableHandoffSchema, } from "./lib/project-schema.js";
@@ -0,0 +1,161 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * The Context Graph layer model — defined ONCE here and imported by every
4
+ * consumer (Build Plan authoring validation, package validation, StageManifest
5
+ * role inference, the graph-relative link scanner, and the deterministic check
6
+ * defaults). This is the single source of truth for "what are the canonical
7
+ * top-level layers of a Context Graph, and how does a path classify into one."
8
+ *
9
+ * The architecture is FIXED top-level layers / FLEXIBLE interior:
10
+ * - `summaries/` — the grounding/coverage layer (one folder per Source file).
11
+ * - `knowledge/` — the knowledge graph layer; its interior is free
12
+ * (`knowledge/claims/`, `knowledge/entities/`, `knowledge/timelines/`, …).
13
+ * - `artifacts/` — task handoffs / supplemental entrypoints.
14
+ * - `home.md` — the primary entrypoint spine file (exact, no children).
15
+ *
16
+ * Two faces, one definition:
17
+ * - The canonical lowercase set (`CANONICAL_LAYER_DIRS`, `CanonicalLayerSchema`,
18
+ * `isCanonicalLayerDeclaration`) is what a Build Plan may DECLARE. Authoring
19
+ * is strict: a layer must be declared in exact lowercase (`knowledge/`, not
20
+ * `Knowledge/`).
21
+ * - The tolerant classifier (`layerForPath`, `roleForLayerPath`) is DEFENSIVE
22
+ * POLICING of already-built (possibly hand-edited or bad) outputs. It is
23
+ * case-insensitive so a graph that shipped `Knowledge/` notes is still
24
+ * routed into the knowledge layer and still policed by the orphan / web
25
+ * gates — casing can never be used to dodge a gate. Classification only ever
26
+ * widens membership; it never narrows it.
27
+ */
28
+ /**
29
+ * The three canonical top-level DIRECTORY layers, in lockstep lowercase. These
30
+ * are the only top-level folders a Build Plan may declare for core derived
31
+ * content. `home.md` is the spine FILE and is handled separately (it is not a
32
+ * directory layer).
33
+ */
34
+ export declare const CANONICAL_LAYER_DIRS: readonly ["summaries", "knowledge", "artifacts"];
35
+ export type CanonicalLayerDir = (typeof CANONICAL_LAYER_DIRS)[number];
36
+ /**
37
+ * Set form of the canonical directory layers, for membership tests at call
38
+ * sites (authoring guard, package validation) that previously hand-rolled their
39
+ * own `new Set(["summaries", "knowledge", "artifacts"])`.
40
+ */
41
+ export declare const CANONICAL_LAYER_DIR_SET: ReadonlySet<CanonicalLayerDir>;
42
+ /**
43
+ * The canonical layer "label" a path resolves to. Mirrors the directory layers
44
+ * plus `home` (the spine) and `other` (anything outside the canonical
45
+ * skeleton). This is the layer identity used for role inference and per-layer
46
+ * rollups.
47
+ */
48
+ export declare const CanonicalLayerSchema: z.ZodEnum<{
49
+ other: "other";
50
+ knowledge: "knowledge";
51
+ summaries: "summaries";
52
+ artifacts: "artifacts";
53
+ home: "home";
54
+ }>;
55
+ export type CanonicalLayer = z.infer<typeof CanonicalLayerSchema>;
56
+ /**
57
+ * The exact spine file name. Canonical only as the top-level file `home.md`
58
+ * with no children.
59
+ */
60
+ export declare const HOME_SPINE_FILE = "home.md";
61
+ /**
62
+ * Normalize a path to forward slashes with no leading `./` and no trailing
63
+ * slashes. Shared so every consumer normalizes identically before classifying
64
+ * — a layer must not depend on `\` vs `/` or a stray trailing separator.
65
+ */
66
+ export declare function normalizeLayerPath(value: string): string;
67
+ /**
68
+ * The FIRST path segment of a normalized path (the top-level folder, or the
69
+ * file name when the path is a bare top-level file). Empty string for an empty
70
+ * path. Layer membership keys off this segment only, so the entire interior of
71
+ * `knowledge/` is unconstrained.
72
+ */
73
+ export declare function topLevelSegment(value: string): string;
74
+ /**
75
+ * Is `segment` an EXACT canonical layer declaration a Build Plan may use? Used
76
+ * by authoring validation. Strict: `knowledge` passes, `Knowledge` does NOT.
77
+ * The mixed-case form is rejected at authoring so a declaration can never pass
78
+ * authoring and then break the lowercase-path-based package validators and
79
+ * runtime scanners downstream.
80
+ */
81
+ export declare function isCanonicalLayerDeclaration(segment: string): boolean;
82
+ /**
83
+ * Is this normalized top-level path a legal canonical DECLARATION? Accepts the
84
+ * exact lowercase layer dirs (and their interiors, since only the first segment
85
+ * is checked) and the exact spine file `home.md`. Strict on case. A non-path
86
+ * artifact (no folder) is the caller's concern; this only judges path shape.
87
+ */
88
+ export declare function isCanonicalDeclarationPath(rawPath: string): boolean;
89
+ /**
90
+ * Classify any path into its canonical layer. Case-INSENSITIVE on the top-level
91
+ * segment: a custom plan (or a hand-edited output) writing notes into
92
+ * `Knowledge/` (or `Summaries/`/`Artifacts/`) still classifies into the right
93
+ * layer, so the orphan + web + connectivity gates can't be dodged by path
94
+ * casing. Canonical layers are lowercase; this only WIDENS membership, never
95
+ * narrows it, and is the single classifier every consumer (note role, stage
96
+ * role, disconnected/web checks, metrics) routes through.
97
+ *
98
+ * Returns `home` for the spine file, a directory layer for `summaries/`,
99
+ * `knowledge/`, `artifacts/`, and `other` for anything else.
100
+ */
101
+ export declare function layerForPath(rawPath: string): CanonicalLayer;
102
+ /**
103
+ * The directory layer (or `null`) a check should scan for a given artifact
104
+ * target path. `home` and `other` have no scannable directory layer, so they
105
+ * return `null`; the three directory layers return their lowercase dir name.
106
+ * Used by the deterministic checks to derive their scan root from the artifact
107
+ * they are attached to instead of hardcoding a layer dir.
108
+ */
109
+ export declare function layerDirForPath(rawPath: string): CanonicalLayerDir | null;
110
+ /**
111
+ * The fine-grained kind of a note INSIDE the knowledge layer, by the structural
112
+ * convention every consumer agreed on: a `knowledge/claims/` folder (or a
113
+ * `claim-` leaf) is a claim, `knowledge/entities/` (or `entity-`) an entity,
114
+ * `knowledge/indexes/` (or `index-`) an index. `null` means "knowledge note
115
+ * with no recognized sub-kind". This is the single definition both the
116
+ * StageManifest note-kind labels and the semantic-graph node kinds delegate to,
117
+ * so the `knowledge/claims/` / `claim-` rules can never drift between them.
118
+ *
119
+ * Only the STRUCTURAL rules live here (folder prefix + leaf-token prefix). The
120
+ * looser "leaf merely contains `index`" heuristic is intentionally NOT here; it
121
+ * is a semantic-graph display nicety, and folding it in would silently
122
+ * reclassify manifest note kinds. Case-insensitive, like the rest of this
123
+ * module, so casing can never dodge the classification.
124
+ */
125
+ export type KnowledgeNoteKind = "claim" | "entity" | "index" | null;
126
+ export declare function knowledgeNoteKind(rawPath: string): KnowledgeNoteKind;
127
+ /**
128
+ * The ResourceRef role a canonical layer maps to. `summaries → summary`,
129
+ * `knowledge → knowledge`, `artifacts`/`home → entrypoint`, `other → other`.
130
+ * Kept here (next to the layer model) so manifest role inference and the layer
131
+ * classifier never drift apart. The string union is the same as
132
+ * `ResourceRoleSchema`'s layer-bearing members; it is intentionally a plain
133
+ * union so this module has no import cycle back into the big schema.
134
+ */
135
+ export type ContextGraphLayerRole = "summary" | "knowledge" | "entrypoint" | "other";
136
+ export declare function roleForLayer(layer: CanonicalLayer): ContextGraphLayerRole;
137
+ /**
138
+ * Role for a produced-note path (or any graph-relative path), via the tolerant
139
+ * classifier. This is the single mapping `roleForPath` callers route through.
140
+ */
141
+ export declare function roleForLayerPath(rawPath: string): ContextGraphLayerRole;
142
+ /**
143
+ * Role for a Build Plan artifact's WRITE path. A directory artifact declared as
144
+ * a bare layer token (`knowledge`, `summaries`, `artifacts`) carries no
145
+ * trailing segment; it is still classified as that layer because classification
146
+ * keys off the (case-insensitive) first segment. The `home`/`home.md` spine is
147
+ * the entrypoint. This makes a custom plan that writes artifact id "claims" to
148
+ * path `knowledge/` (or the bare token `knowledge`) still classify as the
149
+ * knowledge layer, with no reliance on stringly ids.
150
+ */
151
+ export declare function roleForWritePath(rawPath: string): ContextGraphLayerRole;
152
+ /**
153
+ * Matches a graph-relative reference to a canonical layer note inside free text
154
+ * (note bodies): `summaries/…`, `knowledge/…`, `artifacts/…` (optionally ending
155
+ * `.md`), or the exact `home.md`, when bounded by whitespace/brackets/quotes.
156
+ * Derived from `CANONICAL_LAYER_DIRS` so the scanner and the layer model can
157
+ * never list different folders. A fresh RegExp is returned each call because the
158
+ * pattern is stateful (`/g`) and reusing one instance across `matchAll` loops
159
+ * would carry `lastIndex` between callers.
160
+ */
161
+ export declare function graphRelativePathPattern(): RegExp;
@@ -0,0 +1,216 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * The Context Graph layer model — defined ONCE here and imported by every
4
+ * consumer (Build Plan authoring validation, package validation, StageManifest
5
+ * role inference, the graph-relative link scanner, and the deterministic check
6
+ * defaults). This is the single source of truth for "what are the canonical
7
+ * top-level layers of a Context Graph, and how does a path classify into one."
8
+ *
9
+ * The architecture is FIXED top-level layers / FLEXIBLE interior:
10
+ * - `summaries/` — the grounding/coverage layer (one folder per Source file).
11
+ * - `knowledge/` — the knowledge graph layer; its interior is free
12
+ * (`knowledge/claims/`, `knowledge/entities/`, `knowledge/timelines/`, …).
13
+ * - `artifacts/` — task handoffs / supplemental entrypoints.
14
+ * - `home.md` — the primary entrypoint spine file (exact, no children).
15
+ *
16
+ * Two faces, one definition:
17
+ * - The canonical lowercase set (`CANONICAL_LAYER_DIRS`, `CanonicalLayerSchema`,
18
+ * `isCanonicalLayerDeclaration`) is what a Build Plan may DECLARE. Authoring
19
+ * is strict: a layer must be declared in exact lowercase (`knowledge/`, not
20
+ * `Knowledge/`).
21
+ * - The tolerant classifier (`layerForPath`, `roleForLayerPath`) is DEFENSIVE
22
+ * POLICING of already-built (possibly hand-edited or bad) outputs. It is
23
+ * case-insensitive so a graph that shipped `Knowledge/` notes is still
24
+ * routed into the knowledge layer and still policed by the orphan / web
25
+ * gates — casing can never be used to dodge a gate. Classification only ever
26
+ * widens membership; it never narrows it.
27
+ */
28
+ // ───────────────────────────────────────────────────────────────────────────
29
+ // Canonical directory layers
30
+ // ───────────────────────────────────────────────────────────────────────────
31
+ /**
32
+ * The three canonical top-level DIRECTORY layers, in lockstep lowercase. These
33
+ * are the only top-level folders a Build Plan may declare for core derived
34
+ * content. `home.md` is the spine FILE and is handled separately (it is not a
35
+ * directory layer).
36
+ */
37
+ export const CANONICAL_LAYER_DIRS = ["summaries", "knowledge", "artifacts"];
38
+ /**
39
+ * Set form of the canonical directory layers, for membership tests at call
40
+ * sites (authoring guard, package validation) that previously hand-rolled their
41
+ * own `new Set(["summaries", "knowledge", "artifacts"])`.
42
+ */
43
+ export const CANONICAL_LAYER_DIR_SET = new Set(CANONICAL_LAYER_DIRS);
44
+ /**
45
+ * The canonical layer "label" a path resolves to. Mirrors the directory layers
46
+ * plus `home` (the spine) and `other` (anything outside the canonical
47
+ * skeleton). This is the layer identity used for role inference and per-layer
48
+ * rollups.
49
+ */
50
+ export const CanonicalLayerSchema = z.enum([
51
+ "summaries",
52
+ "knowledge",
53
+ "artifacts",
54
+ "home",
55
+ "other",
56
+ ]);
57
+ /**
58
+ * The exact spine file name. Canonical only as the top-level file `home.md`
59
+ * with no children.
60
+ */
61
+ export const HOME_SPINE_FILE = "home.md";
62
+ // ───────────────────────────────────────────────────────────────────────────
63
+ // Path normalization
64
+ // ───────────────────────────────────────────────────────────────────────────
65
+ /**
66
+ * Normalize a path to forward slashes with no leading `./` and no trailing
67
+ * slashes. Shared so every consumer normalizes identically before classifying
68
+ * — a layer must not depend on `\` vs `/` or a stray trailing separator.
69
+ */
70
+ export function normalizeLayerPath(value) {
71
+ return value
72
+ .replaceAll("\\", "/")
73
+ .replace(/^\.\/+/, "")
74
+ .replace(/\/+$/g, "");
75
+ }
76
+ /**
77
+ * The FIRST path segment of a normalized path (the top-level folder, or the
78
+ * file name when the path is a bare top-level file). Empty string for an empty
79
+ * path. Layer membership keys off this segment only, so the entire interior of
80
+ * `knowledge/` is unconstrained.
81
+ */
82
+ export function topLevelSegment(value) {
83
+ const normalized = normalizeLayerPath(value);
84
+ if (normalized.length === 0)
85
+ return "";
86
+ return normalized.split("/")[0] ?? "";
87
+ }
88
+ // ───────────────────────────────────────────────────────────────────────────
89
+ // Authoring face — strict, exact-lowercase declarations
90
+ // ───────────────────────────────────────────────────────────────────────────
91
+ /**
92
+ * Is `segment` an EXACT canonical layer declaration a Build Plan may use? Used
93
+ * by authoring validation. Strict: `knowledge` passes, `Knowledge` does NOT.
94
+ * The mixed-case form is rejected at authoring so a declaration can never pass
95
+ * authoring and then break the lowercase-path-based package validators and
96
+ * runtime scanners downstream.
97
+ */
98
+ export function isCanonicalLayerDeclaration(segment) {
99
+ return CANONICAL_LAYER_DIRS.includes(segment);
100
+ }
101
+ /**
102
+ * Is this normalized top-level path a legal canonical DECLARATION? Accepts the
103
+ * exact lowercase layer dirs (and their interiors, since only the first segment
104
+ * is checked) and the exact spine file `home.md`. Strict on case. A non-path
105
+ * artifact (no folder) is the caller's concern; this only judges path shape.
106
+ */
107
+ export function isCanonicalDeclarationPath(rawPath) {
108
+ const normalized = normalizeLayerPath(rawPath);
109
+ if (normalized.length === 0)
110
+ return false;
111
+ const segment = topLevelSegment(normalized);
112
+ // `home.md` is canonical only as the exact top-level spine file (no children).
113
+ if (segment === HOME_SPINE_FILE && normalized === segment)
114
+ return true;
115
+ return isCanonicalLayerDeclaration(segment);
116
+ }
117
+ // ───────────────────────────────────────────────────────────────────────────
118
+ // Policing face — tolerant, case-insensitive classification
119
+ // ───────────────────────────────────────────────────────────────────────────
120
+ /**
121
+ * Classify any path into its canonical layer. Case-INSENSITIVE on the top-level
122
+ * segment: a custom plan (or a hand-edited output) writing notes into
123
+ * `Knowledge/` (or `Summaries/`/`Artifacts/`) still classifies into the right
124
+ * layer, so the orphan + web + connectivity gates can't be dodged by path
125
+ * casing. Canonical layers are lowercase; this only WIDENS membership, never
126
+ * narrows it, and is the single classifier every consumer (note role, stage
127
+ * role, disconnected/web checks, metrics) routes through.
128
+ *
129
+ * Returns `home` for the spine file, a directory layer for `summaries/`,
130
+ * `knowledge/`, `artifacts/`, and `other` for anything else.
131
+ */
132
+ export function layerForPath(rawPath) {
133
+ const lower = normalizeLayerPath(rawPath).toLowerCase();
134
+ if (lower.length === 0)
135
+ return "other";
136
+ if (lower === HOME_SPINE_FILE)
137
+ return "home";
138
+ const segment = lower.split("/")[0] ?? "";
139
+ if (CANONICAL_LAYER_DIRS.includes(segment)) {
140
+ return segment;
141
+ }
142
+ return "other";
143
+ }
144
+ /**
145
+ * The directory layer (or `null`) a check should scan for a given artifact
146
+ * target path. `home` and `other` have no scannable directory layer, so they
147
+ * return `null`; the three directory layers return their lowercase dir name.
148
+ * Used by the deterministic checks to derive their scan root from the artifact
149
+ * they are attached to instead of hardcoding a layer dir.
150
+ */
151
+ export function layerDirForPath(rawPath) {
152
+ const layer = layerForPath(rawPath);
153
+ return layer === "home" || layer === "other" ? null : layer;
154
+ }
155
+ export function knowledgeNoteKind(rawPath) {
156
+ const normalized = normalizeLayerPath(rawPath).toLowerCase();
157
+ const leaf = normalized.split("/").pop() ?? "";
158
+ if (normalized.startsWith("knowledge/claims/") || leaf.startsWith("claim-"))
159
+ return "claim";
160
+ if (normalized.startsWith("knowledge/entities/") || leaf.startsWith("entity-"))
161
+ return "entity";
162
+ if (normalized.startsWith("knowledge/indexes/") || leaf.startsWith("index-"))
163
+ return "index";
164
+ return null;
165
+ }
166
+ export function roleForLayer(layer) {
167
+ switch (layer) {
168
+ case "summaries":
169
+ return "summary";
170
+ case "knowledge":
171
+ return "knowledge";
172
+ case "artifacts":
173
+ case "home":
174
+ return "entrypoint";
175
+ case "other":
176
+ return "other";
177
+ }
178
+ }
179
+ /**
180
+ * Role for a produced-note path (or any graph-relative path), via the tolerant
181
+ * classifier. This is the single mapping `roleForPath` callers route through.
182
+ */
183
+ export function roleForLayerPath(rawPath) {
184
+ return roleForLayer(layerForPath(rawPath));
185
+ }
186
+ /**
187
+ * Role for a Build Plan artifact's WRITE path. A directory artifact declared as
188
+ * a bare layer token (`knowledge`, `summaries`, `artifacts`) carries no
189
+ * trailing segment; it is still classified as that layer because classification
190
+ * keys off the (case-insensitive) first segment. The `home`/`home.md` spine is
191
+ * the entrypoint. This makes a custom plan that writes artifact id "claims" to
192
+ * path `knowledge/` (or the bare token `knowledge`) still classify as the
193
+ * knowledge layer, with no reliance on stringly ids.
194
+ */
195
+ export function roleForWritePath(rawPath) {
196
+ const path = normalizeLayerPath(rawPath);
197
+ if (path === "home" || path === HOME_SPINE_FILE)
198
+ return "entrypoint";
199
+ return roleForLayerPath(path);
200
+ }
201
+ // ───────────────────────────────────────────────────────────────────────────
202
+ // Graph-relative link scanner
203
+ // ───────────────────────────────────────────────────────────────────────────
204
+ /**
205
+ * Matches a graph-relative reference to a canonical layer note inside free text
206
+ * (note bodies): `summaries/…`, `knowledge/…`, `artifacts/…` (optionally ending
207
+ * `.md`), or the exact `home.md`, when bounded by whitespace/brackets/quotes.
208
+ * Derived from `CANONICAL_LAYER_DIRS` so the scanner and the layer model can
209
+ * never list different folders. A fresh RegExp is returned each call because the
210
+ * pattern is stateful (`/g`) and reusing one instance across `matchAll` loops
211
+ * would carry `lastIndex` between callers.
212
+ */
213
+ export function graphRelativePathPattern() {
214
+ const dirs = CANONICAL_LAYER_DIRS.join("|");
215
+ return new RegExp(`(?:^|[\\s"'([{<])((?:${dirs})\\/[A-Za-z0-9._~!$&*+,;=:@%/-]+(?:\\.md)?|${HOME_SPINE_FILE.replace(".", "\\.")})(?=$|[\\s"'\`)\\]}>.,;:])`, "g");
216
+ }
@@ -103,6 +103,13 @@ export declare function projectRunContextGraphPath(projectDataDir: ProjectDataDi
103
103
  export declare function projectServiceRoot(projectDataDir: ProjectDataDir): string;
104
104
  /** `<project-data>/.service/jobs/`. */
105
105
  export declare function projectServiceJobsRoot(projectDataDir: ProjectDataDir): string;
106
+ /**
107
+ * `<project-data>/.service/jobs/shells/` — preserved, inspectable execution
108
+ * shells for graph-less agent jobs (Build Plan draft/improve, benchmark-question
109
+ * draft). Each `LocalJobRun.result.shell_path` points at one frozen shell here,
110
+ * the graph-less analogue of a Context Graph's stage execution shells.
111
+ */
112
+ export declare function projectServiceJobShellsRoot(projectDataDir: ProjectDataDir): string;
106
113
  /** `<project-data>/.service/action-proposals/`. */
107
114
  export declare function projectServiceActionProposalsRoot(projectDataDir: ProjectDataDir): string;
108
115
  /** `<project-data>/.service/action-plans/`. */
@@ -140,6 +140,15 @@ export function projectServiceRoot(projectDataDir) {
140
140
  export function projectServiceJobsRoot(projectDataDir) {
141
141
  return join(projectServiceRoot(projectDataDir), SERVICE_JOBS_DIR);
142
142
  }
143
+ /**
144
+ * `<project-data>/.service/jobs/shells/` — preserved, inspectable execution
145
+ * shells for graph-less agent jobs (Build Plan draft/improve, benchmark-question
146
+ * draft). Each `LocalJobRun.result.shell_path` points at one frozen shell here,
147
+ * the graph-less analogue of a Context Graph's stage execution shells.
148
+ */
149
+ export function projectServiceJobShellsRoot(projectDataDir) {
150
+ return join(projectServiceJobsRoot(projectDataDir), "shells");
151
+ }
143
152
  /** `<project-data>/.service/action-proposals/`. */
144
153
  export function projectServiceActionProposalsRoot(projectDataDir) {
145
154
  return join(projectServiceRoot(projectDataDir), SERVICE_ACTION_PROPOSALS_DIR);