@interf/compiler 0.5.0 → 0.6.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 (113) hide show
  1. package/README.md +126 -188
  2. package/builtin-workflows/interf/README.md +22 -10
  3. package/builtin-workflows/interf/compile/stages/shape/SKILL.md +6 -3
  4. package/builtin-workflows/interf/compile/stages/structure/SKILL.md +3 -0
  5. package/builtin-workflows/interf/compile/stages/summarize/SKILL.md +18 -2
  6. package/builtin-workflows/interf/improve/SKILL.md +2 -2
  7. package/builtin-workflows/interf/workflow.json +18 -4
  8. package/builtin-workflows/interf/{compiled.schema.json → workflow.schema.json} +9 -2
  9. package/dist/commands/check-draft.js +3 -3
  10. package/dist/commands/compile-controller.js +9 -16
  11. package/dist/commands/compile.d.ts +19 -1
  12. package/dist/commands/compile.js +98 -28
  13. package/dist/commands/create-workflow-wizard.d.ts +20 -2
  14. package/dist/commands/create-workflow-wizard.js +163 -27
  15. package/dist/commands/create.d.ts +1 -1
  16. package/dist/commands/create.js +67 -60
  17. package/dist/commands/dataset-selection.d.ts +6 -0
  18. package/dist/commands/dataset-selection.js +11 -0
  19. package/dist/commands/default.js +3 -3
  20. package/dist/commands/doctor.js +8 -8
  21. package/dist/commands/executor-flow.d.ts +1 -1
  22. package/dist/commands/executor-flow.js +5 -2
  23. package/dist/commands/init.d.ts +5 -0
  24. package/dist/commands/init.js +56 -48
  25. package/dist/commands/list.js +6 -3
  26. package/dist/commands/reset.js +1 -1
  27. package/dist/commands/source-config-wizard.d.ts +2 -2
  28. package/dist/commands/source-config-wizard.js +50 -17
  29. package/dist/commands/test-flow.js +5 -16
  30. package/dist/commands/test.d.ts +0 -6
  31. package/dist/commands/test.js +9 -17
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.js +1 -1
  34. package/dist/lib/agent-args.d.ts +1 -0
  35. package/dist/lib/agent-args.js +10 -0
  36. package/dist/lib/agent-execution.js +2 -1
  37. package/dist/lib/agent-preflight.js +2 -1
  38. package/dist/lib/agent-shells.d.ts +26 -1
  39. package/dist/lib/agent-shells.js +214 -40
  40. package/dist/lib/agents.d.ts +1 -1
  41. package/dist/lib/agents.js +1 -1
  42. package/dist/lib/builtin-compiled-workflow.d.ts +38 -0
  43. package/dist/lib/builtin-compiled-workflow.js +94 -0
  44. package/dist/lib/compiled-compile.d.ts +0 -4
  45. package/dist/lib/compiled-compile.js +11 -30
  46. package/dist/lib/compiled-paths.d.ts +1 -2
  47. package/dist/lib/compiled-paths.js +8 -13
  48. package/dist/lib/compiled-raw.d.ts +2 -2
  49. package/dist/lib/compiled-reset.d.ts +1 -0
  50. package/dist/lib/compiled-reset.js +42 -14
  51. package/dist/lib/compiled-schema.d.ts +11 -7
  52. package/dist/lib/compiled-schema.js +47 -16
  53. package/dist/lib/discovery.d.ts +1 -1
  54. package/dist/lib/discovery.js +2 -2
  55. package/dist/lib/executors.d.ts +1 -1
  56. package/dist/lib/executors.js +2 -2
  57. package/dist/lib/interf-detect.d.ts +0 -1
  58. package/dist/lib/interf-detect.js +7 -18
  59. package/dist/lib/interf-scaffold.js +4 -11
  60. package/dist/lib/interf-workflow-package.d.ts +8 -3
  61. package/dist/lib/interf-workflow-package.js +128 -62
  62. package/dist/lib/interf.d.ts +1 -1
  63. package/dist/lib/interf.js +1 -1
  64. package/dist/lib/local-workflows.d.ts +4 -3
  65. package/dist/lib/local-workflows.js +127 -104
  66. package/dist/lib/project-paths.d.ts +2 -4
  67. package/dist/lib/project-paths.js +13 -10
  68. package/dist/lib/runtime-acceptance.js +15 -3
  69. package/dist/lib/runtime-contracts.js +3 -2
  70. package/dist/lib/runtime-paths.d.ts +1 -0
  71. package/dist/lib/runtime-paths.js +4 -1
  72. package/dist/lib/runtime-prompt.js +4 -4
  73. package/dist/lib/runtime-reconcile.js +90 -64
  74. package/dist/lib/runtime-runs.js +29 -102
  75. package/dist/lib/runtime.d.ts +1 -1
  76. package/dist/lib/runtime.js +1 -1
  77. package/dist/lib/schema.d.ts +104 -54
  78. package/dist/lib/schema.js +32 -116
  79. package/dist/lib/source-config.js +21 -22
  80. package/dist/lib/state-health.js +4 -2
  81. package/dist/lib/state-io.js +2 -110
  82. package/dist/lib/state-view.js +8 -8
  83. package/dist/lib/state.d.ts +1 -0
  84. package/dist/lib/state.js +7 -0
  85. package/dist/lib/test-execution.js +2 -2
  86. package/dist/lib/test-paths.js +12 -3
  87. package/dist/lib/test-sandbox.js +4 -17
  88. package/dist/lib/test-specs.js +1 -1
  89. package/dist/lib/validate-compiled.js +13 -8
  90. package/dist/lib/validate.d.ts +5 -1
  91. package/dist/lib/validate.js +30 -22
  92. package/dist/lib/workflow-authoring.d.ts +26 -0
  93. package/dist/lib/workflow-authoring.js +119 -0
  94. package/dist/lib/workflow-definitions.d.ts +14 -3
  95. package/dist/lib/workflow-definitions.js +21 -17
  96. package/dist/lib/workflow-edit-session.d.ts +16 -0
  97. package/dist/lib/workflow-edit-session.js +57 -0
  98. package/dist/lib/workflow-edit-utils.d.ts +10 -0
  99. package/dist/lib/workflow-edit-utils.js +39 -0
  100. package/dist/lib/workflow-improvement.js +30 -217
  101. package/dist/lib/workflow-primitives.d.ts +2 -0
  102. package/dist/lib/workflow-primitives.js +5 -0
  103. package/dist/lib/workflow-stage-policy.d.ts +5 -0
  104. package/dist/lib/workflow-stage-policy.js +31 -0
  105. package/package.json +7 -8
  106. package/dist/lib/compiled-layout.d.ts +0 -2
  107. package/dist/lib/compiled-layout.js +0 -60
  108. package/dist/lib/obsidian.d.ts +0 -1
  109. package/dist/lib/obsidian.js +0 -15
  110. package/dist/lib/summarize-plan.d.ts +0 -17
  111. package/dist/lib/summarize-plan.js +0 -124
  112. package/dist/lib/workflow-abi.d.ts +0 -129
  113. package/dist/lib/workflow-abi.js +0 -156
@@ -7,7 +7,7 @@ import { loadState } from "./state-io.js";
7
7
  import { validateCompiled, validateCompiledStage, validateCompiledWorkflow, } from "./validate.js";
8
8
  import { readCompiledSchemaFile } from "./compiled-schema.js";
9
9
  import { workflowPackagePathForCompiled } from "./compiled-paths.js";
10
- import { getActiveCompiledWorkflow, resolveCompiledWorkflowFromConfig } from "./workflow-definitions.js";
10
+ import { getActiveCompiledWorkflow, resolveRequiredCompiledWorkflowFromConfig } from "./workflow-definitions.js";
11
11
  import { listFilesRecursive } from "./filesystem.js";
12
12
  function countZoneArtifacts(compiledPath, zonePath, kind) {
13
13
  const absolutePath = join(compiledPath, zonePath);
@@ -26,7 +26,9 @@ export function computeCompiledHealth(dirPath) {
26
26
  const state = loadState(dirPath);
27
27
  const validation = validateCompiled(dirPath);
28
28
  const activeRun = loadRuntimeRun(dirPath);
29
- const workflowId = config ? resolveCompiledWorkflowFromConfig(config) : null;
29
+ const workflowId = config
30
+ ? resolveRequiredCompiledWorkflowFromConfig(config, `.interf/interf.json for ${dirPath}`)
31
+ : null;
30
32
  let workflow = null;
31
33
  if (workflowId) {
32
34
  try {
@@ -1,17 +1,10 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync, } from "node:fs";
2
2
  import { warnInterf } from "./logger.js";
3
3
  import { readJsonFileUnchecked, readJsonFileWithSchema } from "./parse.js";
4
- import { CompiledHealthSchema, CompiledInventorySchema, CompiledRawSnapshotSchema, CompiledStateSchema, CompiledViewSpecSchema, } from "./schema.js";
5
- import { compiledInventoryEntryCount, compiledInventoryTotal, emptyCompiledInventory, } from "./runtime-inventory.js";
4
+ import { CompiledHealthSchema, CompiledRawSnapshotSchema, CompiledStateSchema, CompiledViewSpecSchema, } from "./schema.js";
5
+ import { emptyCompiledInventory, } from "./runtime-inventory.js";
6
6
  import { healthPath, rawSnapshotPath, statePath, viewSpecPath } from "./state-paths.js";
7
7
  import { compiledRuntimeInventoryPath, compiledRuntimeRoot, } from "./compiled-paths.js";
8
- const NORMALIZABLE_RUNTIME_TIMESTAMP_FIELDS = [
9
- "last_add",
10
- "last_summarize",
11
- "last_structure",
12
- "last_shape",
13
- "last_compile",
14
- ];
15
8
  export function loadState(dirPath) {
16
9
  const path = statePath(dirPath);
17
10
  if (!existsSync(path))
@@ -21,18 +14,8 @@ export function loadState(dirPath) {
21
14
  return null;
22
15
  const parsed = CompiledStateSchema.safeParse(raw);
23
16
  if (parsed.success) {
24
- const healed = healParsedRuntimeState(dirPath, parsed.data);
25
- if (healed !== null) {
26
- saveState(dirPath, healed);
27
- return healed;
28
- }
29
17
  return parsed.data;
30
18
  }
31
- const normalized = normalizeRuntimeState(raw);
32
- if (normalized !== null) {
33
- saveState(dirPath, normalized);
34
- return normalized;
35
- }
36
19
  warnInterf(`Warning: failed to validate runtime state at ${path}: ${parsed.error.issues.map((issue) => issue.message).join("; ")}`);
37
20
  return null;
38
21
  }
@@ -81,97 +64,6 @@ export function initCompiledState() {
81
64
  error_count: 0,
82
65
  };
83
66
  }
84
- function normalizeRuntimeState(raw) {
85
- if (!raw || typeof raw !== "object" || Array.isArray(raw))
86
- return null;
87
- const normalized = { ...raw };
88
- let changed = false;
89
- for (const field of NORMALIZABLE_RUNTIME_TIMESTAMP_FIELDS) {
90
- const timestamp = normalizeRuntimeTimestampField(normalized[field]);
91
- if (timestamp !== undefined && timestamp !== normalized[field]) {
92
- normalized[field] = timestamp;
93
- changed = true;
94
- }
95
- }
96
- if (!changed)
97
- return null;
98
- const parsed = CompiledStateSchema.safeParse(normalized);
99
- return parsed.success ? parsed.data : null;
100
- }
101
- function healParsedRuntimeState(dirPath, state) {
102
- const hasLegacyStageFields = (typeof state.last_summarize === "string" && state.last_summarize.length > 0) ||
103
- (typeof state.last_structure === "string" && state.last_structure.length > 0) ||
104
- (typeof state.last_shape === "string" && state.last_shape.length > 0);
105
- const hasStageMap = Boolean(state.stages) &&
106
- typeof state.stages === "object" &&
107
- Object.keys(state.stages ?? {}).length > 0;
108
- if (state.version >= 2 && (hasStageMap || !hasLegacyStageFields))
109
- return null;
110
- const next = {
111
- ...initCompiledState(),
112
- ...state,
113
- stages: { ...(state.stages ?? {}) },
114
- };
115
- if (typeof state.last_summarize === "string" && state.last_summarize.length > 0) {
116
- next.stages.summarize = {
117
- contract_type: "compiled-file-evidence",
118
- status: "succeeded",
119
- finished_at: state.last_summarize,
120
- counts: {
121
- summarized: state.summarized ?? 0,
122
- expected_summary_total: state.summarized ?? 0,
123
- },
124
- zone_counts: {
125
- summaries: state.summarized ?? 0,
126
- },
127
- };
128
- }
129
- if (typeof state.last_structure === "string" && state.last_structure.length > 0) {
130
- next.stages.structure = {
131
- contract_type: "compiled-knowledge-structure",
132
- status: "succeeded",
133
- finished_at: state.last_structure,
134
- counts: {
135
- summary_total: state.structured ?? state.summarized ?? 0,
136
- },
137
- };
138
- }
139
- if (typeof state.last_shape === "string" && state.last_shape.length > 0) {
140
- next.stages.shape = {
141
- contract_type: "compiled-query-shape",
142
- status: "succeeded",
143
- finished_at: state.last_shape,
144
- counts: {
145
- summary_total: state.shaped ?? state.summarized ?? 0,
146
- },
147
- };
148
- }
149
- const inventoryPath = compiledRuntimeInventoryPath(dirPath);
150
- if (existsSync(inventoryPath)) {
151
- const inventory = readJsonFileWithSchema(inventoryPath, "compiled inventory", CompiledInventorySchema);
152
- const inventoryTotal = compiledInventoryTotal(inventory);
153
- if (inventory &&
154
- inventoryTotal === (state.summarized ?? 0) &&
155
- compiledInventoryEntryCount(inventory) === (state.summarized ?? 0)) {
156
- next.inventory_complete = true;
157
- }
158
- }
159
- return next;
160
- }
161
- function normalizeRuntimeTimestampField(value) {
162
- if (typeof value === "string" || value === null)
163
- return value;
164
- if (!value || typeof value !== "object" || Array.isArray(value))
165
- return undefined;
166
- const record = value;
167
- for (const key of ["generated_at", "finished_at", "updated_at", "started_at", "timestamp"]) {
168
- const candidate = record[key];
169
- if (typeof candidate === "string" && candidate.length > 0) {
170
- return candidate;
171
- }
172
- }
173
- return undefined;
174
- }
175
67
  function loadViewSpec(filePath, schema) {
176
68
  try {
177
69
  const raw = JSON.parse(readFileSync(filePath, "utf8"));
@@ -6,7 +6,7 @@ import { loadCompiledViewSpec, saveCompiledViewSpec, } from "./state-io.js";
6
6
  import { rawSnapshotPath } from "./state-paths.js";
7
7
  import { compiledRuntimeRoot, workflowPackagePathForCompiled } from "./compiled-paths.js";
8
8
  import { readCompiledSchemaFile } from "./compiled-schema.js";
9
- import { getActiveCompiledWorkflow, resolveCompiledWorkflowFromConfig } from "./workflow-definitions.js";
9
+ import { getActiveCompiledWorkflow, resolveRequiredCompiledWorkflowFromConfig } from "./workflow-definitions.js";
10
10
  export function ensureCompiledViewSpec(dirPath) {
11
11
  const existing = loadCompiledViewSpec(dirPath);
12
12
  const now = new Date().toISOString();
@@ -70,13 +70,13 @@ export function normalizeCompiledViewSpec(existing, defaults) {
70
70
  }
71
71
  function buildDefaultCompiledViewSpec(dirPath, compiledName, generatedAt) {
72
72
  const config = readInterfConfig(dirPath);
73
- const workflowId = resolveCompiledWorkflowFromConfig(config);
73
+ const workflowId = resolveRequiredCompiledWorkflowFromConfig(config, `.interf/interf.json for ${dirPath}`);
74
74
  const workflow = getActiveCompiledWorkflow(dirPath, workflowId);
75
75
  const schema = readCompiledSchemaFile(workflowPackagePathForCompiled(dirPath));
76
- const zoneDocumentPaths = (schema?.zones ?? [])
77
- .filter((zone) => zone.kind !== "runtime")
76
+ const outputDocumentPaths = (schema?.zones ?? [])
77
+ .filter((zone) => zone.role === "output" && zone.kind !== "runtime")
78
78
  .map((zone) => zone.path);
79
- const defaultNote = (schema?.zones ?? []).find((zone) => zone.kind === "file")?.path ?? null;
79
+ const defaultNote = (schema?.zones ?? []).find((zone) => zone.role === "output" && zone.kind === "file")?.path ?? null;
80
80
  return {
81
81
  kind: "compiled-view-spec",
82
82
  version: 2,
@@ -85,7 +85,7 @@ function buildDefaultCompiledViewSpec(dirPath, compiledName, generatedAt) {
85
85
  health_source: ".interf/runtime/health.json",
86
86
  state_source: ".interf/runtime/state.json",
87
87
  default_note: defaultNote,
88
- graph_scope: zoneDocumentPaths,
88
+ graph_scope: outputDocumentPaths,
89
89
  cards: [
90
90
  { id: "source-total", label: "Source Files", metric: "source_total", format: "number" },
91
91
  { id: "stage-total", label: "Stages", metric: "stage_total", format: "number" },
@@ -94,8 +94,8 @@ function buildDefaultCompiledViewSpec(dirPath, compiledName, generatedAt) {
94
94
  sections: [
95
95
  { id: "status", type: "status", title: "Compile Status", path: ".interf/runtime/health.json" },
96
96
  { id: "cards", type: "cards", title: "Key Metrics", path: ".interf/runtime/health.json" },
97
- ...(zoneDocumentPaths.length > 0
98
- ? [{ id: "outputs", type: "documents", title: "Compiled Outputs", paths: zoneDocumentPaths }]
97
+ ...(outputDocumentPaths.length > 0
98
+ ? [{ id: "outputs", type: "documents", title: "Output Zones", paths: outputDocumentPaths }]
99
99
  : []),
100
100
  { id: "workflow", type: "documents", title: "Workflow Docs", paths: ["workflow/README.md", ...workflow.stages.map((stage) => `workflow/compile/stages/${stage.skillDir}`)] },
101
101
  ],
@@ -4,3 +4,4 @@ export { loadState, saveState, loadCompiledHealth, saveCompiledHealth, loadCompi
4
4
  export { refreshCompiledArtifacts, } from "./state-artifacts.js";
5
5
  export { computeCompiledHealth, resolveCompiledStatus, resolveCompiledStage, } from "./state-health.js";
6
6
  export { ensureCompiledViewSpec, ensureCompiledRawSnapshot, normalizeCompiledViewSpec, } from "./state-view.js";
7
+ export declare function initializeCompiledRuntimeState(dirPath: string): void;
package/dist/lib/state.js CHANGED
@@ -3,3 +3,10 @@ export { loadState, saveState, loadCompiledHealth, saveCompiledHealth, loadCompi
3
3
  export { refreshCompiledArtifacts, } from "./state-artifacts.js";
4
4
  export { computeCompiledHealth, resolveCompiledStatus, resolveCompiledStage, } from "./state-health.js";
5
5
  export { ensureCompiledViewSpec, ensureCompiledRawSnapshot, normalizeCompiledViewSpec, } from "./state-view.js";
6
+ import { saveEmptyCompiledInventory, initCompiledState, saveState } from "./state-io.js";
7
+ import { refreshCompiledArtifacts } from "./state-artifacts.js";
8
+ export function initializeCompiledRuntimeState(dirPath) {
9
+ saveState(dirPath, initCompiledState());
10
+ saveEmptyCompiledInventory(dirPath);
11
+ refreshCompiledArtifacts(dirPath, { ensureViewSpec: true });
12
+ }
@@ -203,7 +203,7 @@ function buildTestQueryPrompt(target, testCase, answerPath, tracePath) {
203
203
  "Answer the truth-check question the same way you would answer a real user inside this compiled dataset.",
204
204
  "Prefer the workflow-declared compiled outputs before raw fallback.",
205
205
  "This sandbox is self-contained: the copied compiled dataset has its own sanitized `raw/` fallback via `.interf/interf.json` `source.path`.",
206
- "The project control plane is intentionally absent from this sandbox. Work only from this sandboxed compiled dataset and its embedded raw files.",
206
+ "The original project root is intentionally absent from this sandbox. Work only from this sandboxed compiled dataset and its embedded raw files.",
207
207
  ]
208
208
  : [
209
209
  "You are running an Interf baseline test inside an isolated raw test shell.",
@@ -211,7 +211,7 @@ function buildTestQueryPrompt(target, testCase, answerPath, tracePath) {
211
211
  "Use the local native `interf-query` skill available in this shell.",
212
212
  "There is no compiled dataset in this sandbox.",
213
213
  "Answer only from `raw/` inside this shell.",
214
- "The project control plane is intentionally absent from this sandbox.",
214
+ "The original project root is intentionally absent from this sandbox.",
215
215
  ];
216
216
  return [
217
217
  ...header,
@@ -1,5 +1,5 @@
1
1
  import { existsSync } from "node:fs";
2
- import { join } from "node:path";
2
+ import { basename, dirname, join } from "node:path";
3
3
  import { targetTestRunsRootForCompiled, targetTestSandboxesRootForCompiled, compiledInterfConfigPath, } from "./compiled-paths.js";
4
4
  const TEST_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,79}$/;
5
5
  export const TEST_SPEC_EXTENSIONS = new Set([".json"]);
@@ -9,11 +9,17 @@ export function testSpecRootPath(sourcePath) {
9
9
  export function testSpecTypePath(sourcePath, type) {
10
10
  return join(testSpecRootPath(sourcePath), type);
11
11
  }
12
+ function isDatasetTestsRootPath(artifactRootPath) {
13
+ return basename(dirname(artifactRootPath)) === "tests";
14
+ }
12
15
  export function targetTestRunsPath(compiledPath, type) {
13
16
  if (existsSync(compiledInterfConfigPath(compiledPath))) {
14
17
  return join(targetTestRunsRootForCompiled(compiledPath), type);
15
18
  }
16
- return join(compiledPath, "tests", type === "raw" ? "file-as-is" : "compiled", "runs");
19
+ if (isDatasetTestsRootPath(compiledPath)) {
20
+ return join(compiledPath, type === "raw" ? "file-as-is" : "compiled", "runs");
21
+ }
22
+ throw new Error(`Unsupported test artifact root: ${compiledPath}. Expected a compiled dataset or interf/tests/<dataset> root.`);
17
23
  }
18
24
  export function targetTestRunGitignorePath(compiledPath, type) {
19
25
  return join(targetTestRunsPath(compiledPath, type), ".gitignore");
@@ -22,7 +28,10 @@ export function targetTestSandboxesPath(compiledPath, type) {
22
28
  if (existsSync(compiledInterfConfigPath(compiledPath))) {
23
29
  return join(targetTestSandboxesRootForCompiled(compiledPath), type);
24
30
  }
25
- return join(compiledPath, "tests", type === "raw" ? "file-as-is" : "compiled", "sandboxes");
31
+ if (isDatasetTestsRootPath(compiledPath)) {
32
+ return join(compiledPath, type === "raw" ? "file-as-is" : "compiled", "sandboxes");
33
+ }
34
+ throw new Error(`Unsupported test artifact root: ${compiledPath}. Expected a compiled dataset or interf/tests/<dataset> root.`);
26
35
  }
27
36
  export function targetTestSandboxGitignorePath(compiledPath, type) {
28
37
  return join(targetTestSandboxesPath(compiledPath, type), ".gitignore");
@@ -2,10 +2,10 @@ import { cpSync, existsSync, mkdirSync, mkdtempSync, renameSync, rmSync, } from
2
2
  import { tmpdir } from "node:os";
3
3
  import { dirname, join, relative, sep } from "node:path";
4
4
  import { refreshCompiledBootstrapGuidance } from "./interf-bootstrap.js";
5
- import { readInterfConfig, resolveSourceControlPath, resolveCompiledRawPath } from "./interf.js";
5
+ import { readInterfConfig, resolveCompiledRawPath } from "./interf.js";
6
6
  import { projectRawTestQueryShell } from "./agent-shells.js";
7
7
  import { compiledCompiledPathForDataset } from "./project-paths.js";
8
- import { findSourceDatasetConfig, loadSourceFolderConfig, resolveSourceDatasetPath, saveCompiledInterfConfig, } from "./source-config.js";
8
+ import { saveCompiledInterfConfig } from "./source-config.js";
9
9
  import { refreshCompiledArtifacts } from "./state.js";
10
10
  import { projectRawSnapshot } from "./compiled-raw.js";
11
11
  import { testRootForCompiled, stageExecutionShellsRoot, targetTestSandboxesRootForCompiled, workflowImprovementLoopsRoot, compiledQueryAcceptanceRoot, compiledRuntimeLogsRoot, } from "./compiled-paths.js";
@@ -46,27 +46,14 @@ export function createTestSandbox(target) {
46
46
  throw new Error(`Sandbox compiled is missing interf.json: ${sandboxTargetPath()}`);
47
47
  }
48
48
  if (!existsSync(resolveCompiledRawPath(sandboxTargetPath(), sandboxConfig))) {
49
- const projectPath = resolveSourceControlPath(target.path);
50
- const projectConfig = loadSourceFolderConfig(projectPath);
51
- const datasetConfig = findSourceDatasetConfig(projectConfig, sandboxConfig.name);
52
- if (!datasetConfig) {
53
- throw new Error(`Sandbox compiled is missing a trusted dataset binding for "${sandboxConfig.name}": ${target.path}`);
54
- }
55
- const controlSourcePath = resolveSourceDatasetPath(projectPath, datasetConfig);
56
- projectRawSnapshot({
57
- sourcePath: controlSourcePath,
58
- destinationPath: sandboxRawPath(),
59
- compiledPath: target.path,
60
- mode: "link-or-copy",
61
- prune: false,
62
- preserveTimestamps: false,
63
- });
49
+ throw new Error(`Sandbox compiled is missing its local raw snapshot: ${sandboxTargetPath()}. Rebuild the compiled dataset before testing it.`);
64
50
  }
65
51
  saveCompiledInterfConfig(sandboxTargetPath(), {
66
52
  ...sandboxConfig,
67
53
  checks: [],
68
54
  source: {
69
55
  path: toPortableRelativePath(sandboxTargetPath(), sandboxRawPath()),
56
+ dataset_path: toPortableRelativePath(sandboxTargetPath(), sandboxRawPath()),
70
57
  },
71
58
  });
72
59
  refreshCompiledBootstrapGuidance(sandboxTargetPath());
@@ -21,7 +21,7 @@ function serializeTestSpec(spec) {
21
21
  type: spec.type,
22
22
  name: spec.name,
23
23
  ...(spec.description ? { description: spec.description } : {}),
24
- checks: spec.cases.map((testCase) => ({
24
+ cases: spec.cases.map((testCase) => ({
25
25
  id: testCase.id,
26
26
  question: testCase.question,
27
27
  ...(testCase.file ? { file: testCase.file } : {}),
@@ -1,14 +1,17 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { discoverSourceFiles } from "./discovery.js";
4
- import { compiledZoneAbsolutePath, findCompiledSchemaZone, readCompiledSchemaFile, } from "./compiled-schema.js";
4
+ import { WORKFLOW_SCHEMA_FILE, compiledZoneAbsolutePath, findCompiledSchemaZone, readCompiledSchemaFile, workflowSchemaExists, } from "./compiled-schema.js";
5
5
  import { resolveSourceFolderPath } from "./interf.js";
6
6
  import { compiledInterfConfigPath, workflowPackagePathForCompiled } from "./compiled-paths.js";
7
7
  import { readCompiledConfig } from "./validate.js";
8
8
  import { loadState } from "./state.js";
9
9
  import { validateResolvedStageAcceptance, stageRecordFromState } from "./runtime-acceptance.js";
10
- import { getActiveCompiledWorkflow, resolveCompiledWorkflowFromConfig, } from "./workflow-definitions.js";
10
+ import { getActiveCompiledWorkflow, resolveRequiredCompiledWorkflowFromConfig, } from "./workflow-definitions.js";
11
11
  import { listFilesRecursive } from "./filesystem.js";
12
+ function workflowSchemaPresent(dirPath) {
13
+ return workflowSchemaExists(join(dirPath, ".interf", "workflow"));
14
+ }
12
15
  function countZoneArtifacts(compiledPath, zonePath, kind) {
13
16
  const absolutePath = compiledZoneAbsolutePath(compiledPath, { path: zonePath });
14
17
  if (!existsSync(absolutePath))
@@ -19,7 +22,9 @@ function countZoneArtifacts(compiledPath, zonePath, kind) {
19
22
  }
20
23
  function readWorkflowContext(dirPath) {
21
24
  const config = readCompiledConfig(dirPath);
22
- const workflowId = config.valid ? resolveCompiledWorkflowFromConfig(config.value) : null;
25
+ const workflowId = config.valid
26
+ ? resolveRequiredCompiledWorkflowFromConfig(config.value, `.interf/interf.json for ${dirPath}`)
27
+ : null;
23
28
  let workflow = null;
24
29
  if (workflowId) {
25
30
  try {
@@ -82,7 +87,7 @@ export function validateCompiled(dirPath) {
82
87
  config_type_match: context.config.typeMatch(),
83
88
  workflow_present: existsSync(join(dirPath, ".interf", "workflow", "workflow.json")),
84
89
  workflow_valid: context.workflow !== null,
85
- schema_present: existsSync(join(dirPath, ".interf", "workflow", "compiled.schema.json")),
90
+ schema_present: workflowSchemaPresent(dirPath),
86
91
  schema_valid: context.schema !== null,
87
92
  };
88
93
  }
@@ -94,7 +99,7 @@ export function validateCompiledStage(dirPath, stageId) {
94
99
  config_type_match: context.config.typeMatch(),
95
100
  workflow_present: existsSync(join(dirPath, ".interf", "workflow", "workflow.json")),
96
101
  workflow_valid: context.workflow !== null,
97
- schema_present: existsSync(join(dirPath, ".interf", "workflow", "compiled.schema.json")),
102
+ schema_present: workflowSchemaPresent(dirPath),
98
103
  schema_valid: context.schema !== null,
99
104
  };
100
105
  const errors = [];
@@ -109,9 +114,9 @@ export function validateCompiledStage(dirPath, stageId) {
109
114
  else if (!checks.workflow_valid)
110
115
  errors.push("Could not load the active workflow package.");
111
116
  if (!checks.schema_present)
112
- errors.push("Missing .interf/workflow/compiled.schema.json.");
117
+ errors.push(`Missing .interf/workflow/${WORKFLOW_SCHEMA_FILE}.`);
113
118
  else if (!checks.schema_valid)
114
- errors.push("Could not parse .interf/workflow/compiled.schema.json.");
119
+ errors.push(`Could not parse .interf/workflow/${WORKFLOW_SCHEMA_FILE}.`);
115
120
  const workflowStage = context.workflow?.stages.find((stage) => stage.id === stageId) ?? null;
116
121
  checks.stage_present = workflowStage !== null;
117
122
  if (!checks.stage_present) {
@@ -176,7 +181,7 @@ export function validateCompiledWorkflow(dirPath) {
176
181
  config_type_match: context.config.typeMatch(),
177
182
  workflow_present: existsSync(join(dirPath, ".interf", "workflow", "workflow.json")),
178
183
  workflow_valid: context.workflow !== null,
179
- schema_present: existsSync(join(dirPath, ".interf", "workflow", "compiled.schema.json")),
184
+ schema_present: workflowSchemaPresent(dirPath),
180
185
  schema_valid: context.schema !== null,
181
186
  };
182
187
  if (!context.workflow) {
@@ -5,7 +5,9 @@ export declare function readCompiledConfig(dirPath: string): {
5
5
  value: ReturnType<typeof readInterfConfig>;
6
6
  typeMatch(): boolean;
7
7
  };
8
- export declare function validateSynthFiles(files: string[]): {
8
+ export declare function validateSynthFiles(files: string[], options?: {
9
+ requiredFrontmatterKeys?: string[];
10
+ }): {
9
11
  invalid_frontmatter: number;
10
12
  short_abstracts: number;
11
13
  };
@@ -13,5 +15,7 @@ export declare function countBrokenWikilinks(compiledRoot: string, noteIndexRoot
13
15
  export declare function asNumber(value: unknown): number;
14
16
  export declare function isOutputMarkdownFile(filePath: string): boolean;
15
17
  export declare function safeReadText(filePath: string): string | null;
18
+ export declare function extractSynthAbstract(frontmatter: Record<string, unknown>, body: string): string | null;
19
+ export declare function countSynthAbstractWords(frontmatter: Record<string, unknown>, body: string): number;
16
20
  export { validateCompiled, validateCompiledStage, validateCompiledWorkflow, validateCompiledSummarize, validateCompiledStructure, validateCompiledCompile, } from "./validate-compiled.js";
17
21
  export type { CompiledValidationSummary, CompiledStageValidation, CompiledWorkflowValidation, } from "./validate-compiled.js";
@@ -7,12 +7,6 @@ import { listFilesRecursive } from "./filesystem.js";
7
7
  import { readInterfConfig } from "./interf.js";
8
8
  import { parseJsonFrontmatter } from "./parse.js";
9
9
  import { compiledInterfConfigPath } from "./compiled-paths.js";
10
- const REQUIRED_DIGEST_FIELDS = [
11
- "source_kind",
12
- "evidence_tier",
13
- "truth_mode",
14
- "state",
15
- ];
16
10
  const MIN_ABSTRACT_WORDS = 5;
17
11
  const WIKILINK_PATTERN = /!?\[\[([^[\]]+)\]\]/g;
18
12
  export function readCompiledConfig(dirPath) {
@@ -36,7 +30,7 @@ export function readCompiledConfig(dirPath) {
36
30
  },
37
31
  };
38
32
  }
39
- export function validateSynthFiles(files) {
33
+ export function validateSynthFiles(files, options) {
40
34
  let invalidFrontmatter = 0;
41
35
  let shortAbstracts = 0;
42
36
  for (const filePath of files) {
@@ -44,11 +38,11 @@ export function validateSynthFiles(files) {
44
38
  if (content === null)
45
39
  continue;
46
40
  const parsed = parseJsonFrontmatter(content);
47
- if (!parsed || !hasRequiredSynthFields(parsed.frontmatter)) {
41
+ if (!parsed || !hasRequiredFrontmatterKeys(parsed.frontmatter, options?.requiredFrontmatterKeys)) {
48
42
  invalidFrontmatter += 1;
49
43
  continue;
50
44
  }
51
- const abstractWords = countAbstractWords(parsed.frontmatter, parsed.body);
45
+ const abstractWords = countSynthAbstractWords(parsed.frontmatter, parsed.body);
52
46
  if (abstractWords < MIN_ABSTRACT_WORDS) {
53
47
  shortAbstracts += 1;
54
48
  }
@@ -138,22 +132,36 @@ function dedupeFiles(dirPaths) {
138
132
  }
139
133
  return files;
140
134
  }
141
- function hasRequiredSynthFields(frontmatter) {
142
- const hasSourceReference = "source" in frontmatter || "source_file" in frontmatter || "raw" in frontmatter;
143
- return hasSourceReference && REQUIRED_DIGEST_FIELDS.every((field) => field in frontmatter);
135
+ function hasRequiredFrontmatterKeys(frontmatter, requiredKeys) {
136
+ return (requiredKeys ?? []).every((field) => field in frontmatter);
144
137
  }
145
- function countAbstractWords(frontmatter, body) {
146
- const frontmatterAbstract = typeof frontmatter.abstract === "string" ? frontmatter.abstract : null;
147
- if (frontmatterAbstract && frontmatterAbstract.trim().length > 0) {
148
- return countWords(frontmatterAbstract);
138
+ function extractBodyAbstract(body) {
139
+ const lines = body.split(/\r?\n/);
140
+ const abstractHeadingIndex = lines.findIndex((line) => /^#{1,2}\s+Abstract\s*$/.test(line.trim()));
141
+ if (abstractHeadingIndex < 0)
142
+ return null;
143
+ const abstractLines = [];
144
+ for (let index = abstractHeadingIndex + 1; index < lines.length; index += 1) {
145
+ const line = lines[index] ?? "";
146
+ if (/^#{1,2}\s+\S/.test(line.trim()))
147
+ break;
148
+ abstractLines.push(line);
149
149
  }
150
- const match = body.match(/^## Abstract\s+([\s\S]*?)(?:\n## |\n# |$)/m);
151
- if (!match)
152
- return 0;
153
- const abstractText = match[1];
154
- if (!abstractText)
150
+ const abstractText = abstractLines.join("\n").trim();
151
+ return abstractText.length > 0 ? abstractText : null;
152
+ }
153
+ export function extractSynthAbstract(frontmatter, body) {
154
+ const frontmatterAbstract = typeof frontmatter.abstract === "string" ? frontmatter.abstract.trim() : "";
155
+ if (frontmatterAbstract.length > 0) {
156
+ return frontmatterAbstract;
157
+ }
158
+ return extractBodyAbstract(body);
159
+ }
160
+ export function countSynthAbstractWords(frontmatter, body) {
161
+ const abstract = extractSynthAbstract(frontmatter, body);
162
+ if (!abstract)
155
163
  return 0;
156
- return countWords(abstractText);
164
+ return countWords(abstract);
157
165
  }
158
166
  function countWords(text) {
159
167
  return text
@@ -0,0 +1,26 @@
1
+ import type { WorkflowExecutor } from "./executors.js";
2
+ import { validateWorkflowPackage } from "./local-workflows.js";
3
+ import type { SourceTruthCheck } from "./schema.js";
4
+ import { type WorkflowReporter } from "./workflows.js";
5
+ export interface WorkflowAuthoringRunResult {
6
+ status: "updated" | "no-change" | "invalid" | "executor-failed";
7
+ changed: boolean;
8
+ summary: string;
9
+ validation: ReturnType<typeof validateWorkflowPackage> | null;
10
+ workflowPath: string;
11
+ shellPath: string;
12
+ }
13
+ export declare function runWorkflowAuthoringDraft(options: {
14
+ sourcePath: string;
15
+ datasetPath: string;
16
+ baseWorkflowId: string;
17
+ workflowId: string;
18
+ label: string;
19
+ hint: string;
20
+ taskPrompt: string;
21
+ checks?: SourceTruthCheck[];
22
+ executor: WorkflowExecutor;
23
+ preparePreview?: boolean;
24
+ previewReporter?: WorkflowReporter | null;
25
+ }): Promise<WorkflowAuthoringRunResult>;
26
+ export declare function resolveWorkflowDraftPath(sourcePath: string, workflowId: string): string;