@interf/compiler 0.4.1 → 0.5.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 (162) hide show
  1. package/README.md +70 -66
  2. package/builtin-workflows/interf/README.md +6 -6
  3. package/builtin-workflows/interf/compile/stages/shape/SKILL.md +7 -7
  4. package/builtin-workflows/interf/compile/stages/structure/SKILL.md +2 -2
  5. package/builtin-workflows/interf/compile/stages/summarize/SKILL.md +1 -1
  6. package/builtin-workflows/interf/{workspace.schema.json → compiled.schema.json} +5 -5
  7. package/builtin-workflows/interf/improve/SKILL.md +3 -3
  8. package/builtin-workflows/interf/use/query/SKILL.md +2 -2
  9. package/builtin-workflows/interf/workflow.json +42 -31
  10. package/dist/commands/check-draft.d.ts +19 -0
  11. package/dist/commands/check-draft.js +110 -0
  12. package/dist/commands/compile-controller.d.ts +4 -4
  13. package/dist/commands/compile-controller.js +117 -81
  14. package/dist/commands/compile.d.ts +5 -5
  15. package/dist/commands/compile.js +61 -62
  16. package/dist/commands/compiled-flow.d.ts +23 -0
  17. package/dist/commands/compiled-flow.js +112 -0
  18. package/dist/commands/create-workflow-wizard.d.ts +3 -3
  19. package/dist/commands/create-workflow-wizard.js +11 -11
  20. package/dist/commands/create.d.ts +2 -2
  21. package/dist/commands/create.js +50 -57
  22. package/dist/commands/default.js +2 -2
  23. package/dist/commands/executor-flow.d.ts +20 -1
  24. package/dist/commands/executor-flow.js +67 -7
  25. package/dist/commands/init.js +242 -289
  26. package/dist/commands/list.js +14 -10
  27. package/dist/commands/reset.js +6 -6
  28. package/dist/commands/source-config-wizard.d.ts +12 -8
  29. package/dist/commands/source-config-wizard.js +356 -119
  30. package/dist/commands/status.js +49 -26
  31. package/dist/commands/test-flow.d.ts +23 -10
  32. package/dist/commands/test-flow.js +274 -65
  33. package/dist/commands/test.d.ts +7 -1
  34. package/dist/commands/test.js +264 -65
  35. package/dist/commands/verify.js +23 -14
  36. package/dist/index.d.ts +7 -7
  37. package/dist/index.js +4 -4
  38. package/dist/lib/agent-args.js +2 -1
  39. package/dist/lib/agent-constants.js +1 -1
  40. package/dist/lib/agent-render.js +4 -4
  41. package/dist/lib/agent-shells.d.ts +8 -8
  42. package/dist/lib/agent-shells.js +231 -142
  43. package/dist/lib/{workflow-abi.d.ts → builtin-compiled-workflow.d.ts} +37 -46
  44. package/dist/lib/builtin-compiled-workflow.js +153 -0
  45. package/dist/lib/compiled-compile.d.ts +52 -0
  46. package/dist/lib/compiled-compile.js +274 -0
  47. package/dist/lib/compiled-home.d.ts +5 -0
  48. package/dist/lib/compiled-home.js +32 -0
  49. package/dist/lib/compiled-paths.d.ts +39 -0
  50. package/dist/lib/compiled-paths.js +103 -0
  51. package/dist/lib/{workspace-raw.d.ts → compiled-raw.d.ts} +9 -8
  52. package/dist/lib/{workspace-raw.js → compiled-raw.js} +16 -14
  53. package/dist/lib/compiled-reset.d.ts +1 -0
  54. package/dist/lib/compiled-reset.js +44 -0
  55. package/dist/lib/compiled-schema.d.ts +27 -0
  56. package/dist/lib/compiled-schema.js +110 -0
  57. package/dist/lib/config.d.ts +0 -1
  58. package/dist/lib/config.js +0 -1
  59. package/dist/lib/discovery.d.ts +1 -1
  60. package/dist/lib/discovery.js +3 -3
  61. package/dist/lib/interf-bootstrap.d.ts +1 -1
  62. package/dist/lib/interf-bootstrap.js +4 -4
  63. package/dist/lib/interf-detect.d.ts +9 -10
  64. package/dist/lib/interf-detect.js +70 -59
  65. package/dist/lib/interf-scaffold.d.ts +2 -2
  66. package/dist/lib/interf-scaffold.js +90 -57
  67. package/dist/lib/interf-workflow-package.d.ts +3 -3
  68. package/dist/lib/interf-workflow-package.js +30 -30
  69. package/dist/lib/interf.d.ts +5 -5
  70. package/dist/lib/interf.js +4 -4
  71. package/dist/lib/local-workflows.d.ts +4 -4
  72. package/dist/lib/local-workflows.js +35 -70
  73. package/dist/lib/obsidian.d.ts +1 -1
  74. package/dist/lib/parse.js +92 -1
  75. package/dist/lib/project-paths.d.ts +11 -0
  76. package/dist/lib/project-paths.js +32 -0
  77. package/dist/lib/runtime-acceptance.d.ts +7 -1
  78. package/dist/lib/runtime-acceptance.js +194 -59
  79. package/dist/lib/runtime-contracts.d.ts +2 -4
  80. package/dist/lib/runtime-contracts.js +17 -161
  81. package/dist/lib/runtime-inventory.d.ts +7 -0
  82. package/dist/lib/runtime-inventory.js +29 -0
  83. package/dist/lib/runtime-paths.js +5 -5
  84. package/dist/lib/runtime-prompt.js +7 -6
  85. package/dist/lib/runtime-reconcile.d.ts +2 -3
  86. package/dist/lib/runtime-reconcile.js +94 -184
  87. package/dist/lib/runtime-runs.js +25 -119
  88. package/dist/lib/runtime-types.d.ts +10 -19
  89. package/dist/lib/runtime.d.ts +2 -2
  90. package/dist/lib/runtime.js +1 -1
  91. package/dist/lib/schema.d.ts +169 -153
  92. package/dist/lib/schema.js +116 -164
  93. package/dist/lib/source-config.d.ts +24 -20
  94. package/dist/lib/source-config.js +159 -122
  95. package/dist/lib/state-artifacts.d.ts +5 -5
  96. package/dist/lib/state-artifacts.js +8 -8
  97. package/dist/lib/state-health.d.ts +4 -4
  98. package/dist/lib/state-health.js +110 -126
  99. package/dist/lib/state-io.d.ts +8 -8
  100. package/dist/lib/state-io.js +21 -102
  101. package/dist/lib/state-paths.js +5 -5
  102. package/dist/lib/state-view.d.ts +4 -4
  103. package/dist/lib/state-view.js +52 -55
  104. package/dist/lib/state.d.ts +5 -5
  105. package/dist/lib/state.js +4 -4
  106. package/dist/lib/summarize-plan.d.ts +3 -2
  107. package/dist/lib/summarize-plan.js +19 -21
  108. package/dist/lib/test-execution.js +9 -9
  109. package/dist/lib/test-matrices.d.ts +3 -3
  110. package/dist/lib/test-matrices.js +6 -6
  111. package/dist/lib/test-paths.d.ts +4 -4
  112. package/dist/lib/test-paths.js +26 -11
  113. package/dist/lib/test-sandbox.d.ts +1 -1
  114. package/dist/lib/test-sandbox.js +32 -38
  115. package/dist/lib/test-specs.js +1 -1
  116. package/dist/lib/test-targets.d.ts +2 -2
  117. package/dist/lib/test-targets.js +11 -11
  118. package/dist/lib/test-types.d.ts +1 -1
  119. package/dist/lib/test.d.ts +1 -1
  120. package/dist/lib/test.js +1 -1
  121. package/dist/lib/util.d.ts +2 -0
  122. package/dist/lib/util.js +14 -1
  123. package/dist/lib/validate-compiled.d.ts +27 -0
  124. package/dist/lib/validate-compiled.js +238 -0
  125. package/dist/lib/validate-helpers.d.ts +0 -8
  126. package/dist/lib/validate-helpers.js +0 -30
  127. package/dist/lib/validate.d.ts +6 -4
  128. package/dist/lib/validate.js +76 -27
  129. package/dist/lib/workflow-definitions.d.ts +12 -11
  130. package/dist/lib/workflow-definitions.js +45 -55
  131. package/dist/lib/workflow-helpers.d.ts +2 -3
  132. package/dist/lib/workflow-helpers.js +9 -13
  133. package/dist/lib/workflow-improvement.d.ts +3 -3
  134. package/dist/lib/workflow-improvement.js +48 -48
  135. package/dist/lib/workflow-primitives.d.ts +2 -0
  136. package/dist/lib/workflow-primitives.js +5 -0
  137. package/dist/lib/workflow-review-paths.d.ts +3 -3
  138. package/dist/lib/workflow-review-paths.js +11 -11
  139. package/dist/lib/workflow-stage-runner.d.ts +1 -1
  140. package/dist/lib/workflow-stage-runner.js +8 -8
  141. package/dist/lib/workflows.d.ts +9 -9
  142. package/dist/lib/workflows.js +15 -17
  143. package/package.json +13 -12
  144. package/dist/commands/workspace-flow.d.ts +0 -23
  145. package/dist/commands/workspace-flow.js +0 -109
  146. package/dist/lib/registry.d.ts +0 -16
  147. package/dist/lib/registry.js +0 -65
  148. package/dist/lib/validate-workspace.d.ts +0 -121
  149. package/dist/lib/validate-workspace.js +0 -407
  150. package/dist/lib/workflow-abi.js +0 -181
  151. package/dist/lib/workspace-compile.d.ts +0 -54
  152. package/dist/lib/workspace-compile.js +0 -476
  153. package/dist/lib/workspace-home.d.ts +0 -5
  154. package/dist/lib/workspace-home.js +0 -32
  155. package/dist/lib/workspace-layout.d.ts +0 -2
  156. package/dist/lib/workspace-layout.js +0 -60
  157. package/dist/lib/workspace-paths.d.ts +0 -41
  158. package/dist/lib/workspace-paths.js +0 -107
  159. package/dist/lib/workspace-reset.d.ts +0 -1
  160. package/dist/lib/workspace-reset.js +0 -43
  161. package/dist/lib/workspace-schema.d.ts +0 -17
  162. package/dist/lib/workspace-schema.js +0 -74
@@ -1,23 +1,32 @@
1
- import { existsSync, readFileSync, } from "node:fs";
1
+ import { existsSync, } from "node:fs";
2
2
  import { join } from "node:path";
3
+ import { listFilesRecursive } from "./filesystem.js";
3
4
  import { readJsonFileWithSchema } from "./parse.js";
5
+ import { compiledZoneAbsolutePath, findCompiledSchemaZone, readCompiledSchemaFile, } from "./compiled-schema.js";
4
6
  import { RuntimeStageContractSchema, } from "./schema.js";
5
- import { warnInterf } from "./logger.js";
6
7
  import { stageContractPath } from "./runtime-paths.js";
7
- function readWorkspaceJsonFile(dirPath, relativePath) {
8
- const targetPath = join(dirPath, relativePath);
9
- if (!existsSync(targetPath))
10
- return null;
11
- try {
12
- const parsed = JSON.parse(readFileSync(targetPath, "utf8"));
13
- return parsed && typeof parsed === "object" && !Array.isArray(parsed)
14
- ? parsed
15
- : null;
16
- }
17
- catch (error) {
18
- warnInterf(`Warning: failed to read workspace JSON at ${targetPath}: ${error instanceof Error ? error.message : String(error)}`);
8
+ import { workflowPackagePathForCompiled } from "./compiled-paths.js";
9
+ import { loadState } from "./state-io.js";
10
+ import { countBrokenWikilinks, isOutputMarkdownFile, safeReadText, validateSynthFiles } from "./validate.js";
11
+ function toFiniteNumber(value) {
12
+ if (typeof value !== "number" || !Number.isFinite(value))
19
13
  return null;
20
- }
14
+ return value;
15
+ }
16
+ function hasAcceptanceRules(acceptance) {
17
+ if (!acceptance)
18
+ return false;
19
+ return (acceptance.artifacts_exist?.length ?? 0) > 0 ||
20
+ (acceptance.stage_truthy?.length ?? 0) > 0 ||
21
+ Object.keys(acceptance.stage_equals_counts ?? {}).length > 0 ||
22
+ Object.keys(acceptance.stage_at_least ?? {}).length > 0 ||
23
+ Object.keys(acceptance.stage_at_least_counts ?? {}).length > 0 ||
24
+ Object.keys(acceptance.zone_counts_at_least ?? {}).length > 0 ||
25
+ Object.keys(acceptance.zone_counts_at_least_counts ?? {}).length > 0 ||
26
+ (acceptance.markdown_frontmatter_valid_zones?.length ?? 0) > 0 ||
27
+ (acceptance.markdown_abstract_valid_zones?.length ?? 0) > 0 ||
28
+ (acceptance.wikilinks_valid_in_zones?.length ?? 0) > 0 ||
29
+ Object.keys(acceptance.artifacts_must_not_contain ?? {}).length > 0;
21
30
  }
22
31
  function readPathValue(record, keyPath) {
23
32
  if (!record)
@@ -31,92 +40,218 @@ function readPathValue(record, keyPath) {
31
40
  return current[segment];
32
41
  }, record);
33
42
  }
34
- function toFiniteNumber(value) {
35
- if (typeof value !== "number" || !Number.isFinite(value))
43
+ function countZoneArtifacts(compiledPath, zonePath, kind) {
44
+ const absolutePath = compiledZoneAbsolutePath(compiledPath, { path: zonePath });
45
+ if (!existsSync(absolutePath))
46
+ return 0;
47
+ if (kind === "file")
48
+ return 1;
49
+ return listFilesRecursive(absolutePath).length;
50
+ }
51
+ function markdownFilesForZone(compiledPath, zonePath, kind) {
52
+ const absolutePath = compiledZoneAbsolutePath(compiledPath, { path: zonePath });
53
+ if (!existsSync(absolutePath))
54
+ return [];
55
+ if (kind === "file") {
56
+ return isOutputMarkdownFile(absolutePath) ? [absolutePath] : [];
57
+ }
58
+ return listFilesRecursive(absolutePath, isOutputMarkdownFile);
59
+ }
60
+ function zoneRoots(compiledPath, acceptanceZones) {
61
+ const schema = readCompiledSchemaFile(workflowPackagePathForCompiled(compiledPath));
62
+ if (!schema)
63
+ return [];
64
+ return acceptanceZones
65
+ .map((zoneId) => findCompiledSchemaZone(schema, zoneId))
66
+ .filter((zone) => zone !== null)
67
+ .map((zone) => compiledZoneAbsolutePath(compiledPath, zone));
68
+ }
69
+ function readArtifactText(compiledPath, relativePath) {
70
+ const artifactPath = join(compiledPath, relativePath);
71
+ if (!existsSync(artifactPath))
36
72
  return null;
37
- return value;
73
+ return safeReadText(artifactPath);
38
74
  }
39
- function hasAcceptanceRules(acceptance) {
40
- if (!acceptance)
41
- return false;
42
- return (acceptance.artifacts_exist?.length ?? 0) > 0 ||
43
- (acceptance.state_truthy?.length ?? 0) > 0 ||
44
- Object.keys(acceptance.state_equals_counts ?? {}).length > 0 ||
45
- Object.keys(acceptance.state_at_least ?? {}).length > 0 ||
46
- Object.keys(acceptance.state_at_least_counts ?? {}).length > 0;
75
+ function resolveStageCounts(contract, stageRecord) {
76
+ const merged = { ...contract.counts };
77
+ const fromStage = stageRecord?.counts;
78
+ if (fromStage && typeof fromStage === "object" && !Array.isArray(fromStage)) {
79
+ for (const [key, value] of Object.entries(fromStage)) {
80
+ const numeric = toFiniteNumber(value);
81
+ if (numeric !== null)
82
+ merged[key] = numeric;
83
+ }
84
+ }
85
+ return merged;
47
86
  }
48
- export function validateStageContractAcceptance(dirPath, contract = null) {
49
- const resolvedContract = contract ?? (() => {
50
- const path = stageContractPath(dirPath);
51
- if (!existsSync(path))
52
- return null;
53
- return readJsonFileWithSchema(path, "stage contract", RuntimeStageContractSchema);
54
- })();
55
- if (!resolvedContract) {
87
+ export function stageRecordFromState(state, stageId) {
88
+ const stages = state?.stages;
89
+ if (!stages || typeof stages !== "object")
90
+ return null;
91
+ const stage = stages[stageId];
92
+ if (!stage || typeof stage !== "object" || Array.isArray(stage))
93
+ return null;
94
+ return stage;
95
+ }
96
+ export function validateResolvedStageAcceptance(dirPath, options) {
97
+ const acceptance = options.acceptance;
98
+ if (!hasAcceptanceRules(acceptance)) {
56
99
  return {
57
100
  ok: true,
58
- summary: "No active stage contract found for acceptance validation.",
101
+ summary: "No additional stage acceptance criteria were declared.",
59
102
  failures: [],
60
103
  };
61
104
  }
62
- const acceptance = resolvedContract.acceptance;
63
- if (!hasAcceptanceRules(acceptance)) {
105
+ const schema = readCompiledSchemaFile(workflowPackagePathForCompiled(dirPath));
106
+ if (!schema) {
64
107
  return {
65
- ok: true,
66
- summary: "No additional stage acceptance criteria were declared.",
67
- failures: [],
108
+ ok: false,
109
+ summary: `Stage acceptance failed for ${options.stageId}: missing compiled schema.`,
110
+ failures: ["Missing workflow/compiled.schema.json."],
68
111
  };
69
112
  }
70
113
  const failures = [];
71
- const state = readWorkspaceJsonFile(dirPath, ".interf/runtime/state.json");
114
+ const state = loadState(dirPath);
115
+ const stageRecord = stageRecordFromState(state, options.stageId);
116
+ const counts = options.counts ?? {};
72
117
  for (const relativePath of acceptance?.artifacts_exist ?? []) {
73
118
  if (!existsSync(join(dirPath, relativePath))) {
74
119
  failures.push(`Missing required artifact: ${relativePath}.`);
75
120
  }
76
121
  }
77
- for (const keyPath of acceptance?.state_truthy ?? []) {
78
- if (!readPathValue(state, keyPath)) {
79
- failures.push(`Expected truthy state value at "${keyPath}".`);
122
+ for (const keyPath of acceptance?.stage_truthy ?? []) {
123
+ if (!readPathValue(stageRecord, keyPath)) {
124
+ failures.push(`Expected truthy stage value at "${keyPath}".`);
80
125
  }
81
126
  }
82
- for (const [keyPath, countKey] of Object.entries(acceptance?.state_equals_counts ?? {})) {
83
- const actual = readPathValue(state, keyPath);
84
- const expected = resolvedContract.counts[countKey];
127
+ for (const [keyPath, countKey] of Object.entries(acceptance?.stage_equals_counts ?? {})) {
128
+ const actual = readPathValue(stageRecord, keyPath);
129
+ const expected = counts[countKey];
85
130
  if (typeof expected !== "number") {
86
- failures.push(`Acceptance references missing contract count "${countKey}" for "${keyPath}".`);
131
+ failures.push(`Acceptance references missing count "${countKey}" for "${keyPath}".`);
87
132
  continue;
88
133
  }
89
134
  if (actual !== expected) {
90
- failures.push(`Expected state "${keyPath}" to equal count "${countKey}" (${expected}), got ${JSON.stringify(actual)}.`);
135
+ failures.push(`Expected stage "${keyPath}" to equal count "${countKey}" (${expected}), got ${JSON.stringify(actual)}.`);
91
136
  }
92
137
  }
93
- for (const [keyPath, minimum] of Object.entries(acceptance?.state_at_least ?? {})) {
94
- const actual = toFiniteNumber(readPathValue(state, keyPath));
138
+ for (const [keyPath, minimum] of Object.entries(acceptance?.stage_at_least ?? {})) {
139
+ const actual = toFiniteNumber(readPathValue(stageRecord, keyPath));
95
140
  if (actual === null || actual < minimum) {
96
- failures.push(`Expected state "${keyPath}" to be at least ${minimum}, got ${actual === null ? "non-numeric" : actual}.`);
141
+ failures.push(`Expected stage "${keyPath}" to be at least ${minimum}, got ${actual === null ? "non-numeric" : actual}.`);
97
142
  }
98
143
  }
99
- for (const [keyPath, countKey] of Object.entries(acceptance?.state_at_least_counts ?? {})) {
100
- const actual = toFiniteNumber(readPathValue(state, keyPath));
101
- const expected = resolvedContract.counts[countKey];
144
+ for (const [keyPath, countKey] of Object.entries(acceptance?.stage_at_least_counts ?? {})) {
145
+ const actual = toFiniteNumber(readPathValue(stageRecord, keyPath));
146
+ const expected = counts[countKey];
102
147
  if (typeof expected !== "number") {
103
- failures.push(`Acceptance references missing contract count "${countKey}" for "${keyPath}".`);
148
+ failures.push(`Acceptance references missing count "${countKey}" for "${keyPath}".`);
104
149
  continue;
105
150
  }
106
151
  if (actual === null || actual < expected) {
107
- failures.push(`Expected state "${keyPath}" to be at least count "${countKey}" (${expected}), got ${actual === null ? "non-numeric" : actual}.`);
152
+ failures.push(`Expected stage "${keyPath}" to be at least count "${countKey}" (${expected}), got ${actual === null ? "non-numeric" : actual}.`);
153
+ }
154
+ }
155
+ for (const [zoneId, minimum] of Object.entries(acceptance?.zone_counts_at_least ?? {})) {
156
+ const zone = findCompiledSchemaZone(schema, zoneId);
157
+ if (!zone) {
158
+ failures.push(`Acceptance references unknown zone "${zoneId}".`);
159
+ continue;
160
+ }
161
+ const actual = countZoneArtifacts(dirPath, zone.path, zone.kind);
162
+ if (actual < minimum) {
163
+ failures.push(`Expected zone "${zoneId}" to contain at least ${minimum} artifact(s), got ${actual}.`);
164
+ }
165
+ }
166
+ for (const [zoneId, countKey] of Object.entries(acceptance?.zone_counts_at_least_counts ?? {})) {
167
+ const zone = findCompiledSchemaZone(schema, zoneId);
168
+ if (!zone) {
169
+ failures.push(`Acceptance references unknown zone "${zoneId}".`);
170
+ continue;
171
+ }
172
+ const expected = counts[countKey];
173
+ if (typeof expected !== "number") {
174
+ failures.push(`Acceptance references missing count "${countKey}" for zone "${zoneId}".`);
175
+ continue;
176
+ }
177
+ const actual = countZoneArtifacts(dirPath, zone.path, zone.kind);
178
+ if (actual < expected) {
179
+ failures.push(`Expected zone "${zoneId}" to contain at least count "${countKey}" (${expected}) artifact(s), got ${actual}.`);
180
+ }
181
+ }
182
+ for (const zoneId of acceptance?.markdown_frontmatter_valid_zones ?? []) {
183
+ const zone = findCompiledSchemaZone(schema, zoneId);
184
+ if (!zone) {
185
+ failures.push(`Acceptance references unknown zone "${zoneId}".`);
186
+ continue;
187
+ }
188
+ const validation = validateSynthFiles(markdownFilesForZone(dirPath, zone.path, zone.kind));
189
+ if (validation.invalid_frontmatter > 0) {
190
+ failures.push(`Zone "${zoneId}" has ${validation.invalid_frontmatter} markdown file(s) with invalid or incomplete frontmatter.`);
191
+ }
192
+ }
193
+ for (const zoneId of acceptance?.markdown_abstract_valid_zones ?? []) {
194
+ const zone = findCompiledSchemaZone(schema, zoneId);
195
+ if (!zone) {
196
+ failures.push(`Acceptance references unknown zone "${zoneId}".`);
197
+ continue;
198
+ }
199
+ const validation = validateSynthFiles(markdownFilesForZone(dirPath, zone.path, zone.kind));
200
+ if (validation.short_abstracts > 0) {
201
+ failures.push(`Zone "${zoneId}" has ${validation.short_abstracts} markdown file(s) with missing or weak abstracts.`);
202
+ }
203
+ }
204
+ if ((acceptance?.wikilinks_valid_in_zones?.length ?? 0) > 0) {
205
+ const roots = zoneRoots(dirPath, acceptance?.wikilinks_valid_in_zones ?? []);
206
+ const brokenLinks = countBrokenWikilinks(dirPath, roots, roots);
207
+ if (brokenLinks > 0) {
208
+ failures.push(`Detected ${brokenLinks} broken wikilink(s) across the declared acceptance zones.`);
209
+ }
210
+ }
211
+ for (const [relativePath, disallowedTexts] of Object.entries(acceptance?.artifacts_must_not_contain ?? {})) {
212
+ const content = readArtifactText(dirPath, relativePath);
213
+ if (content === null) {
214
+ failures.push(`Missing artifact required for content check: ${relativePath}.`);
215
+ continue;
216
+ }
217
+ for (const disallowed of disallowedTexts) {
218
+ if (content.includes(disallowed)) {
219
+ failures.push(`Artifact "${relativePath}" still contains forbidden text: ${JSON.stringify(disallowed)}.`);
220
+ }
108
221
  }
109
222
  }
110
223
  if (failures.length === 0) {
111
224
  return {
112
225
  ok: true,
113
- summary: `Stage acceptance satisfied for ${resolvedContract.stage}.`,
226
+ summary: `Stage acceptance satisfied for ${options.stageId}.`,
114
227
  failures: [],
115
228
  };
116
229
  }
117
230
  return {
118
231
  ok: false,
119
- summary: `Stage acceptance failed for ${resolvedContract.stage}: ${failures.join(" ")}`,
232
+ summary: `Stage acceptance failed for ${options.stageId}: ${failures.join(" ")}`,
120
233
  failures,
121
234
  };
122
235
  }
236
+ export function validateStageContractAcceptance(dirPath, contract = null) {
237
+ const resolvedContract = contract ?? (() => {
238
+ const path = stageContractPath(dirPath);
239
+ if (!existsSync(path))
240
+ return null;
241
+ return readJsonFileWithSchema(path, "stage contract", RuntimeStageContractSchema);
242
+ })();
243
+ if (!resolvedContract) {
244
+ return {
245
+ ok: true,
246
+ summary: "No active stage contract found for acceptance validation.",
247
+ failures: [],
248
+ };
249
+ }
250
+ const state = loadState(dirPath);
251
+ const stageRecord = stageRecordFromState(state, resolvedContract.stage);
252
+ return validateResolvedStageAcceptance(dirPath, {
253
+ stageId: resolvedContract.stage,
254
+ acceptance: resolvedContract.acceptance,
255
+ counts: resolveStageCounts(resolvedContract, stageRecord),
256
+ });
257
+ }
@@ -1,4 +1,2 @@
1
- import { type RuntimeStageContractDraft, type WorkspaceKnowledgeContractOptions, type WorkspaceShapeContractOptions, type WorkspaceSummarizeContractOptions } from "./runtime-types.js";
2
- export declare function buildWorkspaceSummarizeContract(options: WorkspaceSummarizeContractOptions): RuntimeStageContractDraft;
3
- export declare function buildWorkspaceStructureContract(options: WorkspaceKnowledgeContractOptions): RuntimeStageContractDraft;
4
- export declare function buildWorkspaceShapeContract(options: WorkspaceShapeContractOptions): RuntimeStageContractDraft;
1
+ import { type RuntimeStageContractDraft, type RuntimeStageContractOptions } from "./runtime-types.js";
2
+ export declare function buildRuntimeStageContract(options: RuntimeStageContractOptions): RuntimeStageContractDraft;
@@ -1,191 +1,47 @@
1
- import { CHART_APPROXIMATION_NOTES } from "./chart-guidance.js";
2
- export function buildWorkspaceSummarizeContract(options) {
1
+ export function buildRuntimeStageContract(options) {
3
2
  const localSkillDocs = options.localSkillDocs ?? [];
4
3
  const workflowNotes = options.workflowNotes ?? [];
5
4
  return {
6
5
  instructions: options.instructions,
7
6
  counts: {
8
- target_files: options.targetCount,
9
- expected_summary_total: options.expectedSummaryTotal,
7
+ ...(options.counts ?? {}),
10
8
  local_skill_docs: localSkillDocs.length,
11
9
  },
12
10
  ...(options.acceptance ? { acceptance: options.acceptance } : {}),
13
11
  artifacts: {
14
12
  reads: [
15
13
  "interf.json",
16
- options.planPath,
17
- "workflow/workspace.schema.json",
18
- ".interf/runtime/raw-snapshot.json",
14
+ "workflow/compiled.schema.json",
15
+ ...(options.extraReadArtifacts ?? []),
16
+ ...options.stageReadArtifacts,
19
17
  ...localSkillDocs,
20
18
  ],
21
- writes: [
22
- "summaries/",
23
- ".interf/runtime/inventory.json",
24
- ],
25
- verifies: [
26
- "interf verify summarize --json",
27
- ],
28
- },
29
- policies: {
30
- execution_mode: "deterministic",
31
- status_prefixes: ["STATUS:", "DONE:", "BLOCKED:", "ERROR:"],
32
- notes: [
33
- `Process only the files listed in ${options.planPath}.`,
34
- `When ${options.planPath} includes \`targets[].output\`, write each summary to that exact relative path instead of inferring a filename.`,
35
- `Source files live at ${options.sourcePath}.`,
36
- "This is a workspace file-evidence stage only. Do not do broad cross-file synthesis here.",
37
- "Write every summary as markdown under `summaries/` with a `.md` filename, even when the source file has another extension. If the source file already ends in `.md`, keep a single `.md` suffix instead of appending another one.",
38
- "Use JSON frontmatter wrapped in `---` markers. Do not use YAML frontmatter.",
39
- "Every summary must include a non-trivial `abstract` in JSON frontmatter. Do not leave it blank or push the abstract only into body sections.",
40
- "Also include a `## Abstract` section or another clearly labeled abstract block so humans can scan the summary quickly.",
41
- "Use cautious, source-grounded titles. Summary filenames should describe what the file is or records without overstating uncertain conclusions.",
42
- "If links are added, keep them minimal and source-grounded.",
43
- "Do not precreate `.interf/runtime/inventory.json` with placeholder writes. Write the final JSON document directly.",
44
- "When updating `.interf/runtime/inventory.json`, rewrite the full JSON document in one whole-file write instead of patching partial lines.",
45
- "The CLI reconciles `.interf/runtime/state.json` and refreshes `.interf/runtime/health.json` after stage validation. Do not treat those files as stage-owned write targets for this stage.",
46
- "Prefer the current inventory runtime shape: `.interf/runtime/inventory.json` should use `entries` plus `total`, with one object per source file containing at least `source`, `summary`, and a conservative status/state field.",
47
- "Keep scratch extraction commands single-purpose and non-destructive. Do not use `rm`, wildcard cleanup, `;`, or `&&` chains during summarize.",
48
- "Use `workflow/workspace.schema.json` as the deterministic output-shape reference for this workspace.",
49
- "The workspace includes its own `raw/` snapshot. Read source files there when summary evidence depends on direct inspection.",
50
- ...workflowNotes,
51
- ...(localSkillDocs.length > 0
52
- ? [
53
- "Read any local instruction docs listed in the contract before processing source files.",
54
- options.instructions.effective_mode === "override"
55
- ? "Local docs replace the bundled file-evidence workflow for this run, but they still do not bypass per-file evidence capture, required writes, or deterministic runtime reconciliation."
56
- : "Local docs can refine summary structure and file-evidence heuristics, but they do not bypass per-file evidence capture, required proof artifacts, or deterministic runtime reconciliation.",
57
- ]
58
- : []),
59
- ],
60
- evidence_weighting: {
61
- required_fields: ["source_kind", "evidence_tier", "truth_mode", "state"],
62
- preserve_tiers: true,
63
- },
64
- },
65
- };
66
- }
67
- export function buildWorkspaceStructureContract(options) {
68
- const localSkillDocs = options.localSkillDocs ?? [];
69
- const workflowNotes = options.workflowNotes ?? [];
70
- return {
71
- instructions: options.instructions,
72
- counts: {
73
- summary_total: options.summaryCount,
74
- local_skill_docs: localSkillDocs.length,
75
- },
76
- ...(options.acceptance ? { acceptance: options.acceptance } : {}),
77
- artifacts: {
78
- reads: [
79
- "interf.json",
80
- "workflow/workspace.schema.json",
81
- "summaries/",
82
- ".interf/runtime/inventory.json",
83
- ...localSkillDocs,
84
- ],
85
- writes: [
86
- "knowledge/entities/",
87
- "knowledge/claims/",
88
- "knowledge/indexes/",
89
- ],
90
- verifies: [
91
- "interf verify structure --json",
92
- ],
93
- },
94
- policies: {
95
- execution_mode: "deterministic",
96
- status_prefixes: ["STATUS:", "DONE:", "BLOCKED:", "ERROR:"],
97
- notes: [
98
- "This is the cross-file knowledge-structure stage. Canonicalize entities, claims, timelines, and retrieval indexes from the summary set.",
99
- "Use taxonomy and ontology only as means to improve retrieval, grounding, and navigation.",
100
- "Prefer stable entity, claim, and index notes over one giant catch-all document.",
101
- "Do not write `home.md` yet unless the contract explicitly lists it as a write target.",
102
- "Do not copy expected answers from checks into the knowledge layer.",
103
- "Use `workflow/workspace.schema.json` as the deterministic output-shape reference for this workspace.",
104
- "Rewrite knowledge artifacts directly instead of doing destructive cleanup passes.",
105
- ...workflowNotes,
106
- ...(localSkillDocs.length > 0
107
- ? [
108
- "Read any local instruction docs listed in the contract before structuring the knowledge layer.",
109
- options.instructions.effective_mode === "override"
110
- ? "Local docs replace the bundled structure workflow for this run, but they still do not bypass required writes or deterministic runtime reconciliation."
111
- : "Local docs can refine structure heuristics, but they do not bypass required writes or deterministic runtime reconciliation.",
112
- ]
113
- : []),
114
- ],
115
- evidence_weighting: {
116
- required_fields: ["source_kind", "evidence_tier", "truth_mode", "state"],
117
- preserve_tiers: true,
118
- },
119
- working_set: {
120
- target_context_fraction: 0.4,
121
- strategy: "Read summaries in bounded batches, then canonicalize and relate the cross-file knowledge structure.",
122
- },
123
- },
124
- };
125
- }
126
- export function buildWorkspaceShapeContract(options) {
127
- const localSkillDocs = options.localSkillDocs ?? [];
128
- const workflowNotes = options.workflowNotes ?? [];
129
- return {
130
- instructions: options.instructions,
131
- counts: {
132
- summary_total: options.summaryCount,
133
- local_skill_docs: localSkillDocs.length,
134
- },
135
- ...(options.acceptance ? { acceptance: options.acceptance } : {}),
136
- artifacts: {
137
- reads: [
138
- "interf.json",
139
- "workflow/workspace.schema.json",
140
- options.shapeInputPath,
141
- "raw/",
142
- "knowledge/entities/",
143
- "knowledge/claims/",
144
- "knowledge/indexes/",
145
- "summaries/",
146
- ...localSkillDocs,
147
- ],
148
- writes: [
149
- "home.md",
150
- "knowledge/indexes/",
151
- ],
19
+ writes: options.stageWriteArtifacts,
152
20
  verifies: [
153
- "interf verify compile --json",
21
+ `interf verify stage ${options.stageId} --json`,
154
22
  ],
155
23
  },
156
24
  policies: {
157
25
  execution_mode: "deterministic",
158
26
  status_prefixes: ["STATUS:", "DONE:", "BLOCKED:", "ERROR:"],
159
27
  notes: [
160
- `Use ${options.shapeInputPath} as the shaping input for this workspace.`,
161
- "The shaping input provides the workspace focus plus saved truth-check question text only. It does not provide expected answers.",
162
- "Use the focus and saved truth checks to bias routing, navigation, priority notes, and the final workspace home.",
163
- "Replace any scaffold or placeholder content in `home.md`. The final home note must not keep the initial `Not yet compiled.` message.",
164
- "Do not copy expected answers into the workspace or treat the checks as license to hallucinate missing evidence.",
165
- "When a focused question depends on chart-derived or table-derived evidence, prefer the saved summaries and structured notes if they already preserve the needed bounded reads plus provenance.",
166
- "Use `raw/` during shaping only when the compiled layer is missing the needed value, the metric family is ambiguous, or the earlier bounded read is clearly inconsistent.",
167
- "Carry the final bounded reads forward into focused indexes with clear provenance instead of repeatedly recomputing them from `raw/`.",
168
- ...CHART_APPROXIMATION_NOTES,
169
- "Use `workflow/workspace.schema.json` as the deterministic output-shape reference for this workspace.",
170
- "Prefer better retrieval routes, tighter home.md guidance, and focused indexes over speculative synthesis.",
28
+ `This is the "${options.stageLabel}" stage for the compiled dataset "${options.compiledName}".`,
29
+ "The workflow package is the authoritative method layer for this run.",
30
+ "Use `workflow/compiled.schema.json` as the deterministic output-shape reference for this compiled dataset.",
31
+ "Honor the declared read and write artifacts instead of inventing a parallel workflow.",
32
+ "Only create or update files that fall under the declared write targets for this stage.",
33
+ "The CLI owns `.interf/runtime/` ledgers such as run, state, health, and view-spec files. Read them if needed, but do not create, edit, or replace them in this stage unless the contract explicitly marks a runtime artifact as stage-owned.",
34
+ "Keep scratch extraction commands single-purpose and non-destructive. Avoid wildcard cleanup and multi-command chains.",
171
35
  ...workflowNotes,
172
36
  ...(localSkillDocs.length > 0
173
37
  ? [
174
- "Read any local instruction docs listed in the contract before shaping the final workspace.",
38
+ "Read any local instruction docs listed in the contract before you write artifacts or declare the stage complete.",
175
39
  options.instructions.effective_mode === "override"
176
- ? "Local docs replace the bundled shaping workflow for this run, but they still do not bypass required writes or deterministic runtime reconciliation."
177
- : "Local docs can refine shaping behavior, but they do not bypass required writes or deterministic runtime reconciliation.",
40
+ ? "Local stage docs replace the default workflow guidance for this run, but they still do not bypass required writes, declared proof artifacts, or deterministic runtime reconciliation."
41
+ : "Local stage docs can refine the stage behavior, but they do not bypass required writes, declared proof artifacts, or deterministic runtime reconciliation.",
178
42
  ]
179
43
  : []),
180
44
  ],
181
- evidence_weighting: {
182
- required_fields: ["source_kind", "evidence_tier", "truth_mode", "state"],
183
- preserve_tiers: true,
184
- },
185
- working_set: {
186
- target_context_fraction: 0.3,
187
- strategy: "Use the existing knowledge structure plus shaping input to produce the final workspace surface.",
188
- },
189
45
  },
190
46
  };
191
47
  }
@@ -0,0 +1,7 @@
1
+ import { type CompiledInventory, type CompiledInventoryEntry } from "./schema.js";
2
+ export declare function emptyCompiledInventory(): CompiledInventory;
3
+ export declare function compiledInventoryFromEntries(entries: CompiledInventoryEntry[], stage?: string): CompiledInventory;
4
+ export declare function compiledInventoryEntryCount(inventory: CompiledInventory | null): number;
5
+ export declare function compiledInventoryTotal(inventory: CompiledInventory | null): number;
6
+ export declare function compiledInventoryFrontmatterScannedCount(inventory: CompiledInventory | null): number;
7
+ export declare function compiledInventoryAbstractCoverageCount(inventory: CompiledInventory | null): number;
@@ -0,0 +1,29 @@
1
+ export function emptyCompiledInventory() {
2
+ return {
3
+ kind: "compiled-runtime-ledger",
4
+ version: 1,
5
+ entries: [],
6
+ total: 0,
7
+ };
8
+ }
9
+ export function compiledInventoryFromEntries(entries, stage) {
10
+ return {
11
+ kind: "compiled-runtime-ledger",
12
+ version: 1,
13
+ ...(stage ? { stage } : {}),
14
+ entries,
15
+ total: entries.length,
16
+ };
17
+ }
18
+ export function compiledInventoryEntryCount(inventory) {
19
+ return inventory?.entries.length ?? 0;
20
+ }
21
+ export function compiledInventoryTotal(inventory) {
22
+ return inventory?.total ?? 0;
23
+ }
24
+ export function compiledInventoryFrontmatterScannedCount(inventory) {
25
+ return (inventory?.entries ?? []).filter((entry) => entry.metadata?.frontmatter_scanned === true).length;
26
+ }
27
+ export function compiledInventoryAbstractCoverageCount(inventory) {
28
+ return (inventory?.entries ?? []).filter((entry) => entry.metadata?.abstract_read === true || typeof entry.metadata?.abstract === "string").length;
29
+ }
@@ -1,16 +1,16 @@
1
1
  import { join } from "node:path";
2
- import { workspaceRuntimeLogsRoot, workspaceRuntimeRunHistoryPath, workspaceRuntimeRunPath, workspaceRuntimeStageContractPath, } from "./workspace-paths.js";
2
+ import { compiledRuntimeLogsRoot, compiledRuntimeRunHistoryPath, compiledRuntimeRunPath, compiledRuntimeStageContractPath, } from "./compiled-paths.js";
3
3
  export function runPath(dirPath) {
4
- return workspaceRuntimeRunPath(dirPath);
4
+ return compiledRuntimeRunPath(dirPath);
5
5
  }
6
6
  export function runHistoryPath(dirPath) {
7
- return workspaceRuntimeRunHistoryPath(dirPath);
7
+ return compiledRuntimeRunHistoryPath(dirPath);
8
8
  }
9
9
  export function stageContractPath(dirPath) {
10
- return workspaceRuntimeStageContractPath(dirPath);
10
+ return compiledRuntimeStageContractPath(dirPath);
11
11
  }
12
12
  export function logsDirPath(dirPath) {
13
- return workspaceRuntimeLogsRoot(dirPath);
13
+ return compiledRuntimeLogsRoot(dirPath);
14
14
  }
15
15
  export function promptLogPath(dirPath, runId) {
16
16
  return join(logsDirPath(dirPath), `${runId}.prompt.txt`);
@@ -1,4 +1,5 @@
1
1
  export function buildStagePrompt(instructions, contractPath, statusLines) {
2
+ const stageSkillDir = instructions.stage_skill_dir || "stage";
2
3
  const modeNotes = instructions.effective_mode === "override"
3
4
  ? [
4
5
  "Local stage instruction docs are authoritative for this run.",
@@ -7,14 +8,14 @@ export function buildStagePrompt(instructions, contractPath, statusLines) {
7
8
  ]
8
9
  : instructions.effective_mode === "extend"
9
10
  ? [
10
- "Use the current workspace workflow docs as the default workflow for this run.",
11
+ "Use the current compiled workflow docs as the default workflow for this run.",
11
12
  "Then apply any additional local stage instruction docs referenced by the contract as stage-specific extensions.",
12
13
  ]
13
14
  : [
14
- "Use the current workspace workflow docs as the default workflow for this run.",
15
+ "Use the current compiled workflow docs as the default workflow for this run.",
15
16
  ];
16
17
  return [
17
- `Interf built-in stage id: ${instructions.bundled_skill}`,
18
+ `Interf workflow stage docs: workflow/compile/stages/${stageSkillDir}/`,
18
19
  "",
19
20
  "This is an automated Interf stage run, not an open-ended chat session.",
20
21
  "The user has already invoked this stage through the Interf CLI. Execute it now.",
@@ -25,13 +26,13 @@ export function buildStagePrompt(instructions, contractPath, statusLines) {
25
26
  "Honor the contract's counts, artifact paths, and policies instead of inventing another workflow.",
26
27
  "Read only the files named by the contract's `artifacts.reads` list plus any paths they explicitly direct you to open.",
27
28
  "If the contract lists `instructions.local_docs`, open every one of those files before you write artifacts or declare the stage complete.",
28
- "If `AGENTS.md` is listed in the contract, use it only for workspace routing that is directly relevant to this stage.",
29
+ "If `AGENTS.md` is listed in the contract, use it only for compiled routing that is directly relevant to this stage.",
29
30
  "`.interf/runtime/run.json`, `.interf/runtime/state.json`, `.interf/runtime/health.json`, and `.interf/runtime/view-spec.json` are CLI-owned runtime artifacts. You may read them, but do not create, edit, or replace them unless the contract explicitly marks a different proof artifact as stage-owned.",
30
31
  "If a contract-listed output file does not exist yet, create it in one whole-file write. Do not attempt patch-style edits against a missing runtime path.",
31
- "Stay inside the current workspace. Do not try to open SDK repo docs, SDK source files, or other paths outside the workspace unless the stage contract explicitly requires them.",
32
+ "Stay inside the current compiled dataset. Do not try to open SDK repo docs, SDK source files, or other paths outside the compiled dataset unless the stage contract explicitly requires them.",
32
33
  instructions.effective_mode === "override"
33
34
  ? "Do not depend on any repo-root or globally installed skill cache for this run. Local stage instruction docs are the primary workflow layer for this run."
34
- : "Do not depend on any repo-root or globally installed skill cache for this run. Use the current workspace workflow docs and the contract as the default behavior.",
35
+ : "Do not depend on any repo-root or globally installed skill cache for this run. Use the current compiled workflow docs and the contract as the default behavior.",
35
36
  ...modeNotes,
36
37
  "Do not narrate intentions, plans, or reasoning to the user.",
37
38
  "Only emit user-visible updates that begin with STATUS:, DONE:, BLOCKED:, or ERROR:.",
@@ -1,3 +1,2 @@
1
- export declare function reconcileWorkspaceSummarizeRun(dirPath: string, expectedSummaryTotal: number): boolean;
2
- export declare function reconcileWorkspaceStructureRun(dirPath: string): boolean;
3
- export declare function reconcileWorkspaceShapeRun(dirPath: string): boolean;
1
+ import { type WorkflowStageDefinition } from "./workflow-definitions.js";
2
+ export declare function reconcileCompiledStageRun(dirPath: string, stage: Pick<WorkflowStageDefinition, "id" | "contractType" | "reads" | "writes">): boolean;