@interf/compiler 0.16.0 → 0.21.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 (203) hide show
  1. package/LICENSE.md +1 -0
  2. package/README.md +90 -73
  3. package/TRADEMARKS.md +4 -4
  4. package/dist/cli/commands/mcp.d.ts +0 -34
  5. package/dist/cli/commands/mcp.js +246 -45
  6. package/dist/cli/commands/method.js +261 -15
  7. package/dist/cli/commands/prep.js +116 -15
  8. package/dist/cli/commands/runs.js +103 -9
  9. package/dist/cli/commands/status.js +4 -2
  10. package/dist/cli/commands/test.d.ts +10 -0
  11. package/dist/cli/commands/{verify.js → test.js} +24 -17
  12. package/dist/cli/commands/web.js +82 -8
  13. package/dist/cli/commands/wizard.js +158 -51
  14. package/dist/cli/index.d.ts +2 -2
  15. package/dist/cli/index.js +3 -3
  16. package/dist/compiler-ui/404.html +1 -1
  17. package/dist/compiler-ui/__next.__PAGE__.txt +5 -5
  18. package/dist/compiler-ui/__next._full.txt +13 -12
  19. package/dist/compiler-ui/__next._head.txt +3 -3
  20. package/dist/compiler-ui/__next._index.txt +5 -4
  21. package/dist/compiler-ui/__next._tree.txt +4 -3
  22. package/dist/compiler-ui/_next/static/chunks/01646j7yi.w5a.css +1 -0
  23. package/dist/compiler-ui/_next/static/chunks/{0n51hrfoufc7g.js → 02f_.8.ebn556.js} +1 -1
  24. package/dist/compiler-ui/_next/static/chunks/02r7siaw-_p5w.js +1 -0
  25. package/dist/compiler-ui/_next/static/chunks/{08m7vf5asqlsm.js → 04d0ly-7xb~-j.js} +10 -10
  26. package/dist/compiler-ui/_next/static/chunks/0fhs9psnxqd8s.js +1 -0
  27. package/dist/compiler-ui/_next/static/chunks/0mssmhpbifj15.css +2 -0
  28. package/dist/compiler-ui/_next/static/chunks/0nypu~ddwxari.js +116 -0
  29. package/dist/compiler-ui/_next/static/chunks/0p3s8iyhgcww2.js +31 -0
  30. package/dist/compiler-ui/_next/static/chunks/0tjf-vu_rz8s0.css +1 -0
  31. package/dist/compiler-ui/_next/static/chunks/0u6p3fpbbfgtl.js +1 -0
  32. package/dist/compiler-ui/_next/static/chunks/0wpx5..8dnh0w.js +1 -0
  33. package/dist/compiler-ui/_next/static/chunks/0y0uj160p0ts~.js +1 -0
  34. package/dist/compiler-ui/_next/static/chunks/10t8l~_oenf.c.js +1 -0
  35. package/dist/compiler-ui/_next/static/chunks/13gz9e7z~imx1.js +5 -0
  36. package/dist/compiler-ui/_next/static/chunks/156xed-b6czaw.js +1 -0
  37. package/dist/compiler-ui/_next/static/chunks/{turbopack-0.uq1k8c0j4s..js → turbopack-02-3e_c-yz~5g.js} +1 -1
  38. package/dist/compiler-ui/_next/static/chunks/{turbopack-10e~t1yzi4svj.js → turbopack-0apv8vb-nczuy.js} +1 -1
  39. package/dist/compiler-ui/_not-found/__next._full.txt +10 -9
  40. package/dist/compiler-ui/_not-found/__next._head.txt +3 -3
  41. package/dist/compiler-ui/_not-found/__next._index.txt +5 -4
  42. package/dist/compiler-ui/_not-found/__next._not-found.__PAGE__.txt +2 -2
  43. package/dist/compiler-ui/_not-found/__next._not-found.txt +3 -3
  44. package/dist/compiler-ui/_not-found/__next._tree.txt +3 -2
  45. package/dist/compiler-ui/_not-found.html +1 -1
  46. package/dist/compiler-ui/_not-found.txt +10 -9
  47. package/dist/compiler-ui/index.html +1 -1
  48. package/dist/compiler-ui/index.txt +13 -12
  49. package/dist/packages/contracts/index.d.ts +2 -2
  50. package/dist/packages/contracts/index.js +1 -1
  51. package/dist/packages/contracts/lib/schema.d.ts +275 -72
  52. package/dist/packages/contracts/lib/schema.js +244 -83
  53. package/dist/packages/engine/action-definitions.d.ts +174 -13
  54. package/dist/packages/engine/action-definitions.js +125 -122
  55. package/dist/packages/engine/action-planner.js +4 -11
  56. package/dist/packages/engine/agents/lib/shells.d.ts +15 -5
  57. package/dist/packages/engine/agents/lib/shells.js +134 -123
  58. package/dist/packages/engine/agents/role-executors.js +1 -1
  59. package/dist/packages/engine/cloud-seams.d.ts +115 -0
  60. package/dist/packages/engine/cloud-seams.js +84 -0
  61. package/dist/packages/engine/compile/artifact-counts.d.ts +1 -1
  62. package/dist/packages/engine/compile/artifact-counts.js +3 -3
  63. package/dist/packages/engine/compile/artifact-status.d.ts +41 -0
  64. package/dist/packages/engine/compile/artifact-status.js +166 -0
  65. package/dist/packages/engine/compile/billing-events.d.ts +89 -0
  66. package/dist/packages/engine/compile/billing-events.js +74 -0
  67. package/dist/packages/engine/compile/check-evaluator.d.ts +66 -0
  68. package/dist/packages/engine/compile/check-evaluator.js +298 -0
  69. package/dist/packages/engine/compile/compiled-paths.js +6 -6
  70. package/dist/packages/engine/compile/compiled-schema.d.ts +7 -17
  71. package/dist/packages/engine/compile/compiled-schema.js +55 -70
  72. package/dist/packages/engine/compile/compiled-stage-plan.d.ts +1 -0
  73. package/dist/packages/engine/compile/compiled-stage-plan.js +32 -15
  74. package/dist/packages/engine/compile/compiled-stage-runner.js +1 -1
  75. package/dist/packages/engine/compile/index.d.ts +0 -1
  76. package/dist/packages/engine/compile/index.js +0 -1
  77. package/dist/packages/engine/compile/lib/schema.d.ts +111 -92
  78. package/dist/packages/engine/compile/lib/schema.js +35 -39
  79. package/dist/packages/engine/compile/method-primitives.d.ts +2 -2
  80. package/dist/packages/engine/compile/method-primitives.js +1 -1
  81. package/dist/packages/engine/compile/reset.js +4 -4
  82. package/dist/packages/engine/compile/runtime-contracts.js +2 -1
  83. package/dist/packages/engine/compile/runtime-prompt.js +3 -2
  84. package/dist/packages/engine/compile/runtime-reconcile.js +35 -35
  85. package/dist/packages/engine/compile/runtime-runs.js +0 -1
  86. package/dist/packages/engine/compile/runtime-types.d.ts +7 -8
  87. package/dist/packages/engine/compile/runtime.d.ts +1 -2
  88. package/dist/packages/engine/compile/runtime.js +0 -1
  89. package/dist/packages/engine/compile/state-health.js +6 -6
  90. package/dist/packages/engine/compile/state-view.js +7 -6
  91. package/dist/packages/engine/compile/validate-compiled.js +61 -30
  92. package/dist/packages/engine/compile/validate.js +26 -24
  93. package/dist/packages/engine/connection-config.js +1 -1
  94. package/dist/packages/engine/execution/lib/schema.d.ts +89 -33
  95. package/dist/packages/engine/execution/lib/schema.js +13 -5
  96. package/dist/packages/engine/index.d.ts +2 -2
  97. package/dist/packages/engine/index.js +1 -1
  98. package/dist/packages/engine/instance-paths.d.ts +15 -9
  99. package/dist/packages/engine/instance-paths.js +15 -9
  100. package/dist/packages/engine/lib/schema.d.ts +1316 -351
  101. package/dist/packages/engine/lib/schema.js +99 -36
  102. package/dist/packages/engine/native-run-handlers.js +25 -15
  103. package/dist/packages/engine/preparation-store.d.ts +9 -7
  104. package/dist/packages/engine/preparation-store.js +20 -0
  105. package/dist/packages/engine/requested-artifacts.d.ts +5 -0
  106. package/dist/packages/engine/requested-artifacts.js +36 -0
  107. package/dist/packages/engine/routes.d.ts +7 -1
  108. package/dist/packages/engine/routes.js +7 -1
  109. package/dist/packages/engine/run-observability.js +4 -4
  110. package/dist/packages/engine/runtime-event-applier.js +7 -0
  111. package/dist/packages/engine/runtime-proposal-helpers.d.ts +2 -2
  112. package/dist/packages/engine/runtime-proposal-helpers.js +6 -8
  113. package/dist/packages/engine/runtime-resource-builders.d.ts +11 -6
  114. package/dist/packages/engine/runtime-resource-builders.js +18 -6
  115. package/dist/packages/engine/runtime.d.ts +70 -8
  116. package/dist/packages/engine/runtime.js +304 -49
  117. package/dist/packages/engine/server.d.ts +25 -0
  118. package/dist/packages/engine/server.js +161 -50
  119. package/dist/packages/engine/verify/index.d.ts +10 -10
  120. package/dist/packages/engine/verify/index.js +8 -8
  121. package/dist/packages/engine/verify/readiness-check-run.d.ts +27 -4
  122. package/dist/packages/engine/verify/readiness-check-run.js +92 -24
  123. package/dist/packages/engine/verify/{test-execution.d.ts → verify-execution.d.ts} +2 -2
  124. package/dist/packages/engine/verify/{test-execution.js → verify-execution.js} +3 -3
  125. package/dist/packages/engine/verify/{test-paths.d.ts → verify-paths.d.ts} +1 -1
  126. package/dist/packages/engine/verify/{test-sandbox.d.ts → verify-sandbox.d.ts} +1 -1
  127. package/dist/packages/engine/verify/{test-specs.d.ts → verify-specs.d.ts} +1 -1
  128. package/dist/packages/engine/verify/{test-specs.js → verify-specs.js} +1 -1
  129. package/dist/packages/engine/verify/{test-targets.d.ts → verify-targets.d.ts} +1 -1
  130. package/dist/packages/engine/verify/{test.d.ts → verify.d.ts} +4 -4
  131. package/dist/packages/engine/verify/{test.js → verify.js} +3 -3
  132. package/dist/packages/engine/wire-schemas.d.ts +549 -0
  133. package/dist/packages/engine/wire-schemas.js +59 -0
  134. package/dist/packages/methods/authoring/method-authoring.d.ts +5 -1
  135. package/dist/packages/methods/authoring/method-authoring.js +68 -18
  136. package/dist/packages/methods/authoring/method-edit-session.js +5 -5
  137. package/dist/packages/methods/authoring/method-improvement.js +1 -1
  138. package/dist/packages/methods/package/builtin-compiled-method.d.ts +12 -12
  139. package/dist/packages/methods/package/builtin-compiled-method.js +26 -23
  140. package/dist/packages/methods/package/context-interface.d.ts +39 -26
  141. package/dist/packages/methods/package/context-interface.js +48 -39
  142. package/dist/packages/methods/package/interf-method-package.js +28 -47
  143. package/dist/packages/methods/package/local-methods.d.ts +4 -4
  144. package/dist/packages/methods/package/local-methods.js +53 -66
  145. package/dist/packages/methods/package/method-definitions.d.ts +4 -6
  146. package/dist/packages/methods/package/method-definitions.js +1 -5
  147. package/dist/packages/methods/package/method-helpers.d.ts +0 -2
  148. package/dist/packages/methods/package/method-helpers.js +0 -4
  149. package/dist/packages/project/interf-detect.js +6 -6
  150. package/dist/packages/project/interf-scaffold.js +12 -12
  151. package/dist/packages/project/lib/schema.d.ts +193 -0
  152. package/dist/packages/project/lib/schema.js +46 -1
  153. package/dist/packages/project/source-config.js +6 -1
  154. package/dist/packages/project/source-folders.js +1 -1
  155. package/package.json +12 -23
  156. package/public-repo/CONTRIBUTING.md +47 -0
  157. package/public-repo/LICENSE.md +1 -0
  158. package/public-repo/README.md +325 -0
  159. package/public-repo/SECURITY.md +67 -0
  160. package/public-repo/TRADEMARKS.md +8 -0
  161. package/{builtin-methods → public-repo/methods}/interf-default/README.md +10 -7
  162. package/{builtin-methods → public-repo/methods}/interf-default/compile/stages/shape/SKILL.md +4 -8
  163. package/{builtin-methods → public-repo/methods}/interf-default/method.json +8 -69
  164. package/public-repo/methods/interf-default/method.schema.json +75 -0
  165. package/public-repo/methods/interf-default/use/query/SKILL.md +23 -0
  166. package/public-repo/plugins/README.md +9 -0
  167. package/public-repo/plugins/interf/.claude-plugin/plugin.json +21 -0
  168. package/public-repo/plugins/interf/.mcp.json +12 -0
  169. package/public-repo/plugins/interf/README.md +29 -0
  170. package/public-repo/plugins/interf/skills/interf/SKILL.md +477 -0
  171. package/public-repo/skills/interf/SKILL.md +477 -0
  172. package/agent-skills/interf-actions/SKILL.md +0 -185
  173. package/agent-skills/interf-actions/references/cli.md +0 -243
  174. package/builtin-methods/interf-default/method.schema.json +0 -73
  175. package/builtin-methods/interf-default/use/query/SKILL.md +0 -28
  176. package/dist/cli/commands/verify.d.ts +0 -8
  177. package/dist/compiler-ui/_next/static/chunks/06yhdspx~ca5-.js +0 -5
  178. package/dist/compiler-ui/_next/static/chunks/06z~l3kwb891e.js +0 -1
  179. package/dist/compiler-ui/_next/static/chunks/08g7lvje.te.u.js +0 -1
  180. package/dist/compiler-ui/_next/static/chunks/0_i-3_5l9t2qe.js +0 -1
  181. package/dist/compiler-ui/_next/static/chunks/0b-ywny_j0g~0.js +0 -1
  182. package/dist/compiler-ui/_next/static/chunks/0b52v41o1gixx.js +0 -1
  183. package/dist/compiler-ui/_next/static/chunks/0gpzgsv0w.q~m.js +0 -31
  184. package/dist/compiler-ui/_next/static/chunks/0ilwfezfvu6~-.js +0 -1
  185. package/dist/compiler-ui/_next/static/chunks/0jipmpez3_ehh.js +0 -89
  186. package/dist/compiler-ui/_next/static/chunks/0xxmf45eskdt~.css +0 -1
  187. package/dist/compiler-ui/_next/static/chunks/13awzu4tooflw.css +0 -3
  188. package/dist/compiler-ui/_next/static/chunks/14wtz~vq25~qq.js +0 -1
  189. package/dist/packages/engine/compile/runtime-acceptance.d.ts +0 -9
  190. package/dist/packages/engine/compile/runtime-acceptance.js +0 -265
  191. /package/dist/compiler-ui/_next/static/{a3UiUF0DiMEbfWy_0gihg → tYHMLL9oKds1yDoNYgkPV}/_buildManifest.js +0 -0
  192. /package/dist/compiler-ui/_next/static/{a3UiUF0DiMEbfWy_0gihg → tYHMLL9oKds1yDoNYgkPV}/_clientMiddlewareManifest.js +0 -0
  193. /package/dist/compiler-ui/_next/static/{a3UiUF0DiMEbfWy_0gihg → tYHMLL9oKds1yDoNYgkPV}/_ssgManifest.js +0 -0
  194. /package/dist/packages/engine/verify/{test-paths.js → verify-paths.js} +0 -0
  195. /package/dist/packages/engine/verify/{test-profile-presets.d.ts → verify-profile-presets.d.ts} +0 -0
  196. /package/dist/packages/engine/verify/{test-profile-presets.js → verify-profile-presets.js} +0 -0
  197. /package/dist/packages/engine/verify/{test-sandbox.js → verify-sandbox.js} +0 -0
  198. /package/dist/packages/engine/verify/{test-targets.js → verify-targets.js} +0 -0
  199. /package/dist/packages/engine/verify/{test-types.d.ts → verify-types.d.ts} +0 -0
  200. /package/dist/packages/engine/verify/{test-types.js → verify-types.js} +0 -0
  201. /package/{builtin-methods → public-repo/methods}/interf-default/compile/stages/structure/SKILL.md +0 -0
  202. /package/{builtin-methods → public-repo/methods}/interf-default/compile/stages/summarize/SKILL.md +0 -0
  203. /package/{builtin-methods → public-repo/methods}/interf-default/improve/SKILL.md +0 -0
@@ -8,7 +8,7 @@ import { MethodListingCache, MtimeListingCache, ReadinessCache, RunListingCache,
8
8
  import { applyEventToCompileRun, applyEventToLocalJob, } from "./runtime-event-applier.js";
9
9
  import { buildMethodResource, buildPreparationResource, createRunId, logsForRuntimeRun, logsForStageRun, proofForStage, readinessStateToPreparationReadiness, readinessSummaryForStatus, readinessTargetResult, stageArtifactRefs, } from "./runtime-resource-builders.js";
10
10
  import { ACTION_PLANNER_CLARIFICATION_MESSAGE, actionAssistantMessage, actionCommandPreview, actionTypeFromValues, actionValueMethodTaskPrompt, configuredAgentName, createActionProposalId, detachMethodFromPreparation, detectedExecutorOptions, directServiceEndpointForAction, hasCompiledTestTarget, methodAuthoringHintFromPrompt, methodAuthoringPromptFallback, methodIdForProposal, methodLabelFromId, numberValue, requireSelectedMethod, sanitizeActionProposalPlan, stringValue, testModeFromValues, } from "./runtime-proposal-helpers.js";
11
- import { ReadinessStateSchema, } from "../contracts/lib/schema.js";
11
+ import { ReadinessSchema, } from "../contracts/lib/schema.js";
12
12
  import { discoverSourceFiles, } from "./compile/discovery.js";
13
13
  import { resetCompiledGeneratedState, } from "./compile/reset.js";
14
14
  import { ensurePortableContextScaffold, readInterfConfig, } from "../project/interf.js";
@@ -16,7 +16,8 @@ import { findSourcePreparationConfig, fingerprintReadinessChecks, listSourcePrep
16
16
  import { listSourceFolderChoices, } from "../project/source-folders.js";
17
17
  import { asPreparationDataDir, preparationPortableContextPath, userMethodsRoot, preparationConfigPath, preparationMethodPackagePath, preparationMethodsRoot, } from "../contracts/lib/preparation-paths.js";
18
18
  import { getCompiledMethod, listCompiledMethodChoices, } from "../methods/package/method-definitions.js";
19
- import { contextInterfaceArtifactPath, } from "../methods/package/context-interface.js";
19
+ import { aggregateArtifactVerdict, computeArtifactStatuses, } from "./compile/artifact-status.js";
20
+ import { JsonlBillingEventSink, buildCompilationEventsForRun, defaultBillingEventLogPath, } from "./compile/billing-events.js";
20
21
  import { methodDefinitionPath, resolveMethodPackageSourcePath, } from "../methods/package/local-methods.js";
21
22
  import { seedLocalMethodPackageFromBase, } from "../methods/package/interf-method-package.js";
22
23
  import { PACKAGE_ROOT } from "../methods/package/lib/package-root.js";
@@ -24,15 +25,38 @@ import { resolveAgent, detectAgents, supportsAutomatedRuns, } from "./agents/lib
24
25
  import { loadUserConfig, saveUserConfig, } from "./agents/lib/user-config.js";
25
26
  import { loadAgentsRegistry, registerCustomAgent, unregisterCustomAgent, patchRoleMap, setActiveAgent, } from "./agents/registry.js";
26
27
  import { readSavedReadinessCheckRun, } from "./verify/readiness-check-run.js";
27
- import { createCompiledTestTarget, } from "./verify/test-targets.js";
28
+ import { createCompiledTestTarget, } from "./verify/verify-targets.js";
28
29
  import { ActionProposalApprovalRequestSchema, ActionProposalCreateRequestSchema, ActionProposalPlanSchema, ActionProposalResourceSchema, ActionProposalTypeSchema, CompileRunCreateRequestSchema, CompileRunResourceSchema, LocalExecutorStatusSchema, LocalExecutorSelectRequestSchema, LocalServiceHealthSchema, LocalRunHandlerResultSchema, LocalJobEventAppendRequestSchema, LocalJobRunCreateRequestSchema, LocalJobRunResourceSchema, SourceFileResourceSchema, WorkspaceFileResourceSchema, PortableContextResourceSchema, PreparationSetupCreateRequestSchema, PreparationSetupResultSchema, MethodChangeCreateRequestSchema, MethodChangeResultSchema, PreparationChangeCreateRequestSchema, PreparationChangeResultSchema, ReadinessCheckDraftCreateRequestSchema, ReadinessCheckDraftResultSchema, ResetRequestSchema, ResetResultSchema, ServiceRegistryWorkspaceSchema, VerifyRunCreateRequestSchema, VerifyRunResourceSchema, MethodAuthoringCreateRequestSchema, MethodAuthoringResultSchema, } from "./lib/schema.js";
29
30
  import { buildLocalServiceUrl, } from "./routes.js";
30
31
  import { MethodAuthoringActionValuesSchema, PreparationSetupActionValuesSchema, } from "./action-values.js";
31
32
  import { compileRunToObservability, jobRunToObservability, verifyRunToObservability, uniqueArtifacts, } from "./run-observability.js";
33
+ import { artifactRequirementsFromRequestedArtifacts } from "./requested-artifacts.js";
32
34
  /** TTL for `POST /v1/compile-runs` idempotency-key dedupe entries. */
33
35
  const IDEMPOTENCY_TTL_MS = 60 * 60 * 1000;
34
36
  /** Idempotency cache size at which to schedule an opportunistic prune. */
35
37
  const IDEMPOTENCY_PRUNE_THRESHOLD = 64;
38
+ const INTERRUPTED_COMPILE_RUN_MESSAGE = "Compile run interrupted because the Interf engine stopped before the run reached a terminal state.";
39
+ const INTERRUPTED_JOB_RUN_MESSAGE = "Job interrupted because the Interf engine stopped before the job reached a terminal state.";
40
+ function isTerminalCompileRunStatus(status) {
41
+ return status === "succeeded" || status === "failed" || status === "cancelled";
42
+ }
43
+ function isTerminalJobStatus(status) {
44
+ return status === "succeeded" || status === "failed" || status === "cancelled";
45
+ }
46
+ function countsFromCompiledState(state) {
47
+ const counts = {};
48
+ for (const stage of Object.values(state.stages ?? {})) {
49
+ for (const [key, value] of Object.entries(stage.counts ?? {})) {
50
+ if (Number.isFinite(value))
51
+ counts[key] = Math.max(counts[key] ?? 0, value);
52
+ }
53
+ for (const [key, value] of Object.entries(stage.artifact_counts ?? {})) {
54
+ if (Number.isFinite(value))
55
+ counts[key] = Math.max(counts[key] ?? 0, value);
56
+ }
57
+ }
58
+ return counts;
59
+ }
36
60
  export class LocalServiceRuntime {
37
61
  host;
38
62
  port;
@@ -62,6 +86,11 @@ export class LocalServiceRuntime {
62
86
  * when a compile run is launched and cleared once the run reaches a
63
87
  * terminal state. Each entry remembers where the persisted record lives
64
88
  * so cancel can mark it without re-resolving the Preparation.
89
+ *
90
+ * TODO(cloud): when the cloud variant lands, wire `RunLeaseStore`
91
+ * (see `cloud-seams.ts` B4.2) through this map. Multi-replica engines
92
+ * need a shared lease store so a replica can take over a run whose
93
+ * owning replica died.
65
94
  */
66
95
  activeCompileRuns = new Map();
67
96
  /**
@@ -71,6 +100,11 @@ export class LocalServiceRuntime {
71
100
  * tenants on the same engine (CSO finding: a malicious preparation could
72
101
  * otherwise hijack another preparation's run id by reusing its key).
73
102
  * Entries expire after `IDEMPOTENCY_TTL_MS`.
103
+ *
104
+ * TODO(cloud): when the cloud variant lands, wire `IdempotencyStore`
105
+ * (see `cloud-seams.ts` B4.1) through this map. Multi-replica engines
106
+ * need a shared store so retries that land on a different replica
107
+ * still hit the same dedupe entry.
74
108
  */
75
109
  idempotencyKeyCache = new Map();
76
110
  /**
@@ -85,6 +119,22 @@ export class LocalServiceRuntime {
85
119
  readinessCache = new ReadinessCache();
86
120
  sourceFilesCache = new MtimeListingCache();
87
121
  methodListingCache = new MethodListingCache();
122
+ /**
123
+ * 0.17 — sink for per-Artifact billing events. Set once at construction.
124
+ * Lazy-defaults to a per-run JSONL writer when no override was injected.
125
+ */
126
+ billingEventSink;
127
+ /**
128
+ * 0.17 — cloud-variant injection points. The local engine accepts
129
+ * these on the options surface but does not consume them yet — the
130
+ * in-process Maps remain authoritative for idempotency and
131
+ * run-lease state. The cloud variant fork will swap to these stores;
132
+ * the option-fields are documented here so the cloud build has a
133
+ * stable target. See `cloud-seams.ts` for the contracts.
134
+ */
135
+ cloudIdempotencyStore;
136
+ cloudRunLeaseStore;
137
+ cloudTokenValidator = null;
88
138
  constructor(options) {
89
139
  this.host = options.host;
90
140
  this.port = options.port;
@@ -93,6 +143,9 @@ export class LocalServiceRuntime {
93
143
  this.handlers = options.handlers ?? {};
94
144
  this.authToken = options.authToken ?? null;
95
145
  this.rootPath = resolve(options.rootPath);
146
+ this.billingEventSink = options.billingEventSink ?? null;
147
+ this.cloudIdempotencyStore = options.idempotencyStore ?? null;
148
+ this.cloudRunLeaseStore = options.runLeaseStore ?? null;
96
149
  // Auto-register the initial preparation so single-preparation callers
97
150
  // (existing tests, the current `interf web` command) work without
98
151
  // additional bootstrapping. The constructor seed is the only role
@@ -103,6 +156,18 @@ export class LocalServiceRuntime {
103
156
  setBoundPort(port) {
104
157
  this.port = port;
105
158
  }
159
+ /**
160
+ * 0.17 — token validator setter for cloud variants. Stored on the
161
+ * runtime so `isAuthorizedMutation` in `server.ts` can opt into the
162
+ * async per-account check when present. Local default: never set —
163
+ * the static bearer-token check runs.
164
+ */
165
+ setTokenValidator(validator) {
166
+ this.cloudTokenValidator = validator;
167
+ }
168
+ getTokenValidator() {
169
+ return this.cloudTokenValidator;
170
+ }
106
171
  /** Set a hook that fires whenever the registered preparations change. */
107
172
  setOnRegistryChanged(handler) {
108
173
  this.onRegistryChanged = handler;
@@ -126,6 +191,8 @@ export class LocalServiceRuntime {
126
191
  lastActivity: now,
127
192
  };
128
193
  this.preparationContexts.set(resolved, context);
194
+ this.finalizeInterruptedCompileRuns(resolved);
195
+ this.finalizeInterruptedJobRuns(resolved);
129
196
  this.onRegistryChanged?.();
130
197
  return context;
131
198
  }
@@ -258,7 +325,11 @@ export class LocalServiceRuntime {
258
325
  const compileRuns = this.listCompileRunsForPreparation(prepDataDir, preparation.name);
259
326
  const verifyRuns = this.listVerifyRunsForPreparation(prepDataDir, preparation.name);
260
327
  const readiness = this.computePreparationReadiness(prepDataDir, preparation);
261
- return buildPreparationResource(prepDataDir, preparation, readiness, compileRuns[0]?.run_id ?? null, verifyRuns[0]?.run_id ?? null);
328
+ return buildPreparationResource(prepDataDir, preparation, readiness, compileRuns[0]?.run_id ?? null, verifyRuns[0]?.run_id ?? null,
329
+ // 0.17 — surface per-Artifact status from the latest compile
330
+ // run so the UI can render artifact rows on the Preparation
331
+ // page without a separate fetch.
332
+ compileRuns[0]?.artifacts ?? []);
262
333
  });
263
334
  }
264
335
  getPreparation(prepDataDir, preparationName) {
@@ -291,6 +362,13 @@ export class LocalServiceRuntime {
291
362
  const compileRun = this.listCompileRunsForPreparation(prepDataDir, preparation.name)[0] ?? null;
292
363
  const verifyRun = this.listVerifyRunsForPreparation(prepDataDir, preparation.name)[0] ?? null;
293
364
  const readinessRun = this.readLatestReadinessRun(prepDataDir, preparation.name);
365
+ const artifactStatuses = compileRun?.artifacts ?? [];
366
+ const hasArtifactContract = artifactStatuses.length > 0;
367
+ const artifactVerdict = aggregateArtifactVerdict(artifactStatuses);
368
+ const artifactFailures = artifactStatuses.filter((artifact) => artifact.status !== "ready");
369
+ const artifactProofs = artifactStatuses.flatMap((artifact) => artifact.proofs ?? []);
370
+ const requiredArtifactProofs = artifactProofs.filter((proof) => proof.required !== false);
371
+ const passedArtifactProofs = requiredArtifactProofs.filter((proof) => proof.passed);
294
372
  const configuredChecks = preparation.checks.length;
295
373
  const currentFingerprint = configuredChecks > 0 ? fingerprintReadinessChecks(preparation.checks) : null;
296
374
  const readinessRunFingerprint = readinessRun?.checks_fingerprint ?? null;
@@ -356,13 +434,25 @@ export class LocalServiceRuntime {
356
434
  artifact_path: contextReady ? compiledPath : null,
357
435
  },
358
436
  compileCheck,
437
+ {
438
+ gate: "artifact-checks",
439
+ ok: !hasArtifactContract || artifactVerdict === "ready",
440
+ status: !hasArtifactContract
441
+ ? "not-configured"
442
+ : artifactVerdict === "ready" ? "ready" : "not-ready",
443
+ summary: !hasArtifactContract
444
+ ? "No Artifacts are declared by the selected Build Plan."
445
+ : artifactVerdict === "ready"
446
+ ? `${artifactStatuses.length} Artifact${artifactStatuses.length === 1 ? "" : "s"} ready; ${passedArtifactProofs.length}/${requiredArtifactProofs.length} required Artifact check${requiredArtifactProofs.length === 1 ? "" : "s"} passed.`
447
+ : `${artifactFailures.length} Artifact${artifactFailures.length === 1 ? "" : "s"} not ready.`,
448
+ },
359
449
  {
360
450
  gate: "readiness-checks",
361
- ok: configuredChecks > 0,
451
+ ok: true,
362
452
  status: configuredChecks > 0 ? "built" : "not-configured",
363
453
  summary: configuredChecks > 0
364
454
  ? `${configuredChecks} readiness check${configuredChecks === 1 ? "" : "s"} configured.`
365
- : "No readiness checks are configured.",
455
+ : "No optional readiness checks are configured.",
366
456
  },
367
457
  {
368
458
  gate: "checks-current",
@@ -382,16 +472,18 @@ export class LocalServiceRuntime {
382
472
  return "failed";
383
473
  if (!compileRun || !contextReady)
384
474
  return "not-built";
385
- if (configuredChecks === 0)
386
- return "not-configured";
475
+ if (hasArtifactContract && artifactVerdict !== "ready")
476
+ return "not-ready";
387
477
  if (checksStale)
388
478
  return "stale";
479
+ if (configuredChecks === 0)
480
+ return hasArtifactContract ? "ready" : "built";
389
481
  if (!contextResult)
390
482
  return "built";
391
483
  return contextResult.total > 0 && contextResult.passed === contextResult.total ? "ready" : "not-ready";
392
484
  })();
393
485
  const ready = status === "ready";
394
- return ReadinessStateSchema.parse({
486
+ return ReadinessSchema.parse({
395
487
  kind: "interf-readiness-state",
396
488
  version: 1,
397
489
  generated_at: generatedAt,
@@ -484,7 +576,7 @@ export class LocalServiceRuntime {
484
576
  // method roots. Key the cache off mtimes for the three roots; if
485
577
  // any of them changes (a new local Method, an edit to the user
486
578
  // library, etc.) the cache misses and we re-resolve.
487
- const builtinRoot = join(PACKAGE_ROOT, "builtin-methods");
579
+ const builtinRoot = join(PACKAGE_ROOT, "public-repo", "methods");
488
580
  const localRoot = preparationMethodsRoot(asPreparationDataDir(prepDataDir));
489
581
  const userRoot = userMethodsRoot();
490
582
  return this.methodListingCache.get(prepDataDir, [builtinRoot, localRoot, userRoot], () => {
@@ -499,13 +591,12 @@ export class LocalServiceRuntime {
499
591
  path: resolveMethodPackageSourcePath(prepDataDir, method.id) ?? method.id,
500
592
  label: method.label,
501
593
  hint: method.hint,
594
+ purpose: method.purpose,
595
+ inputs: method.inputs,
502
596
  source_kind: method.scope === "builtin" ? "builtin" : "local",
503
597
  built_in: method.scope === "builtin",
504
598
  active_for_preparations: activeForPreparations,
505
- output_paths: (method.contextInterface?.zones ?? [])
506
- .filter((zone) => zone.role === "output")
507
- .map((zone) => contextInterfaceArtifactPath(zone))
508
- .sort(),
599
+ artifacts: method.contextInterface?.artifacts ?? [],
509
600
  stages: method.stages.map((stage) => ({
510
601
  id: stage.id,
511
602
  label: stage.label,
@@ -515,7 +606,6 @@ export class LocalServiceRuntime {
515
606
  role: stage.role && stage.role.trim().length > 0 ? stage.role : "general",
516
607
  reads: stage.reads,
517
608
  writes: stage.writes,
518
- ...(stage.acceptance ? { acceptance: stage.acceptance } : {}),
519
609
  })),
520
610
  });
521
611
  });
@@ -707,7 +797,10 @@ export class LocalServiceRuntime {
707
797
  }
708
798
  listRunObservability(prepDataDir) {
709
799
  return [
710
- ...this.listCompileRuns(prepDataDir).map((resource) => compileRunToObservability(resource.run)),
800
+ ...this.listCompileRuns(prepDataDir).map((resource) => compileRunToObservability({
801
+ ...resource.run,
802
+ readiness: this.getReadiness(prepDataDir, resource.run.preparation),
803
+ })),
711
804
  ...this.listVerifyRuns(prepDataDir).map(verifyRunToObservability),
712
805
  ...this.listJobs(prepDataDir).map(jobRunToObservability),
713
806
  ].sort((left, right) => {
@@ -720,7 +813,7 @@ export class LocalServiceRuntime {
720
813
  return this.listRunObservability(prepDataDir).find((run) => run.run_id === runId) ?? null;
721
814
  }
722
815
  /**
723
- * Method-scoped runs: every method-authoring or method-improvement job
816
+ * Method Activity runs: every method-authoring or method-improvement job
724
817
  * whose `method` matches `methodId`. Surfaced through
725
818
  * `GET /v1/methods/<id>/runs` so Method Detail can show the full audit
726
819
  * trail of authoring + improvement work for a Method.
@@ -919,11 +1012,16 @@ export class LocalServiceRuntime {
919
1012
  applyPreparationSetup(prepDataDir, requestValue) {
920
1013
  const request = PreparationSetupCreateRequestSchema.parse(requestValue);
921
1014
  const preparationConfig = request.preparation;
922
- const methodId = methodIdForSourcePreparationConfig(preparationConfig) ?? DEFAULT_METHOD_ID;
923
- const normalizedPreparationConfig = {
924
- ...preparationConfig,
925
- method: methodId,
926
- };
1015
+ const methodId = methodIdForSourcePreparationConfig(preparationConfig);
1016
+ if (request.setup_mode === "select-method" && !methodId) {
1017
+ throw new Error("Build Plan is required when selecting a Build Plan for a Preparation.");
1018
+ }
1019
+ const normalizedPreparationConfig = methodId
1020
+ ? { ...preparationConfig, method: methodId }
1021
+ : (() => {
1022
+ const { method: _method, ...withoutMethod } = preparationConfig;
1023
+ return withoutMethod;
1024
+ })();
927
1025
  const sourceFolderPath = resolveSourcePreparationPath(prepDataDir, normalizedPreparationConfig);
928
1026
  if (!existsSync(sourceFolderPath) || !statSync(sourceFolderPath).isDirectory()) {
929
1027
  throw new Error(`Source folder "${preparationConfig.path}" is not available.`);
@@ -942,14 +1040,16 @@ export class LocalServiceRuntime {
942
1040
  version: 1,
943
1041
  operation,
944
1042
  preparation: normalizedPreparationConfig.name,
945
- method: methodId,
1043
+ method: methodId ?? null,
946
1044
  source_folder_path: sourceFolderPath,
947
1045
  config_path: preparationConfigPath(asPreparationDataDir(prepDataDir)),
948
1046
  portable_context_path: preparationPortableContextPath(asPreparationDataDir(prepDataDir), normalizedPreparationConfig.name),
949
1047
  changed: true,
950
1048
  message: operation === "select-method"
951
- ? `Preparation ${normalizedPreparationConfig.name} now uses Method ${methodId}.`
952
- : `Preparation ${normalizedPreparationConfig.name} is saved.`,
1049
+ ? `Preparation ${normalizedPreparationConfig.name} now uses Build Plan ${methodId}.`
1050
+ : methodId
1051
+ ? `Preparation ${normalizedPreparationConfig.name} is saved with Build Plan ${methodId}.`
1052
+ : `Preparation ${normalizedPreparationConfig.name} is saved. Draft or select a Build Plan before compiling.`,
953
1053
  });
954
1054
  }
955
1055
  applyPreparationChange(prepDataDir, requestValue) {
@@ -1004,7 +1104,17 @@ export class LocalServiceRuntime {
1004
1104
  });
1005
1105
  }
1006
1106
  async createMethodAuthoringRun(prepDataDir, requestValue, jobType = "method-authoring") {
1007
- const request = MethodAuthoringCreateRequestSchema.parse(requestValue);
1107
+ const parsedRequest = MethodAuthoringCreateRequestSchema.parse(requestValue);
1108
+ const savedPreparation = parsedRequest.preparation
1109
+ ? findSourcePreparationConfig(loadSourceFolderConfig(prepDataDir), parsedRequest.preparation)
1110
+ : null;
1111
+ const request = {
1112
+ ...parsedRequest,
1113
+ requested_artifacts: parsedRequest.requested_artifacts.length > 0
1114
+ ? parsedRequest.requested_artifacts
1115
+ : savedPreparation?.requested_artifacts ?? [],
1116
+ source_profile: parsedRequest.source_profile ?? savedPreparation?.source_profile ?? null,
1117
+ };
1008
1118
  const isImprovement = jobType === "method-improvement";
1009
1119
  const job = this.createJobRun(prepDataDir, {
1010
1120
  job_type: jobType,
@@ -1021,6 +1131,7 @@ export class LocalServiceRuntime {
1021
1131
  preparation: request.preparation ?? null,
1022
1132
  source_folder_path: request.source_folder_path,
1023
1133
  checks: request.checks.length,
1134
+ requested_artifacts: request.requested_artifacts.length,
1024
1135
  },
1025
1136
  },
1026
1137
  {
@@ -1030,6 +1141,8 @@ export class LocalServiceRuntime {
1030
1141
  method_id: request.method_id,
1031
1142
  label: request.label,
1032
1143
  task_prompt: request.task_prompt,
1144
+ requested_artifacts: request.requested_artifacts.length,
1145
+ artifact_requirements: request.artifact_requirements.length,
1033
1146
  },
1034
1147
  },
1035
1148
  {
@@ -1049,6 +1162,7 @@ export class LocalServiceRuntime {
1049
1162
  preparation: request.preparation ?? null,
1050
1163
  source_folder_path: request.source_folder_path,
1051
1164
  checks: request.checks.length,
1165
+ requested_artifacts: request.requested_artifacts.length,
1052
1166
  },
1053
1167
  });
1054
1168
  this.appendJobRunEvent(prepDataDir, job.run_id, {
@@ -1058,6 +1172,7 @@ export class LocalServiceRuntime {
1058
1172
  output: {
1059
1173
  source_folder_path: request.source_folder_path,
1060
1174
  checks: request.checks.length,
1175
+ requested_artifacts: request.requested_artifacts.length,
1061
1176
  },
1062
1177
  });
1063
1178
  this.appendJobRunEvent(prepDataDir, job.run_id, {
@@ -1068,6 +1183,8 @@ export class LocalServiceRuntime {
1068
1183
  method_id: request.method_id,
1069
1184
  label: request.label,
1070
1185
  task_prompt: request.task_prompt,
1186
+ requested_artifacts: request.requested_artifacts.length,
1187
+ artifact_requirements: request.artifact_requirements.length,
1071
1188
  },
1072
1189
  });
1073
1190
  void this.runMethodAuthoringInBackground(prepDataDir, request, job.run_id);
@@ -1108,7 +1225,7 @@ export class LocalServiceRuntime {
1108
1225
  const compiledPath = preparationPortableContextPath(asPreparationDataDir(prepDataDir), preparationName);
1109
1226
  return byCreatedAtDesc(listJsonFiles(compileRunsRoot(compiledPath))
1110
1227
  .map(readCompileRunAt)
1111
- .filter((run) => run !== null));
1228
+ .filter((run) => run !== null)).map((run) => this.hydrateCompileRunFromRuntime(prepDataDir, compiledPath, run));
1112
1229
  }, (run) => run.run_id);
1113
1230
  }
1114
1231
  getCompileRun(prepDataDir, runId) {
@@ -1187,7 +1304,6 @@ export class LocalServiceRuntime {
1187
1304
  stage_total: stageTotal,
1188
1305
  reads: stage.reads,
1189
1306
  writes: stage.writes,
1190
- ...(stage.acceptance ? { acceptance: stage.acceptance } : {}),
1191
1307
  },
1192
1308
  artifacts: [],
1193
1309
  };
@@ -1226,7 +1342,7 @@ export class LocalServiceRuntime {
1226
1342
  }
1227
1343
  /**
1228
1344
  * Cancel an in-flight compile run. Marks the persisted record as
1229
- * `cancelled`, emits a `run.failed` event to capture the cancellation in
1345
+ * `cancelled`, emits a `run.cancelled` event to capture the cancellation in
1230
1346
  * the run timeline, and clears the active handle so retries may start a
1231
1347
  * fresh run. If the run already finished, returns
1232
1348
  * `{ cancelled: false, reason: "already finished" }` and persists nothing.
@@ -1246,23 +1362,14 @@ export class LocalServiceRuntime {
1246
1362
  handle.cancelled = true;
1247
1363
  handle.cancelledAt = cancelledAt;
1248
1364
  const current = this.readCompileRun(handle.compiledPath, runId);
1249
- if (current && current.status !== "succeeded" && current.status !== "failed" && current.status !== "cancelled") {
1250
- const cancelledRun = {
1251
- ...current,
1252
- status: "cancelled",
1253
- finished_at: current.finished_at ?? cancelledAt,
1254
- events: [
1255
- ...current.events,
1256
- {
1257
- type: "run.failed",
1258
- event_id: createRunEventId("event"),
1259
- run_id: runId,
1260
- timestamp: cancelledAt,
1261
- error: "Compile run cancelled by request.",
1262
- },
1263
- ],
1264
- };
1265
- this.writeCompileRun(handle.prepDataDir, handle.compiledPath, cancelledRun);
1365
+ if (current && !isTerminalCompileRunStatus(current.status)) {
1366
+ this.writeCompileRun(handle.prepDataDir, handle.compiledPath, applyEventToCompileRun(current, {
1367
+ type: "run.cancelled",
1368
+ event_id: createRunEventId("event"),
1369
+ run_id: runId,
1370
+ timestamp: cancelledAt,
1371
+ reason: "Compile run cancelled by request.",
1372
+ }));
1266
1373
  }
1267
1374
  return { cancelled: true };
1268
1375
  }
@@ -1433,6 +1540,9 @@ export class LocalServiceRuntime {
1433
1540
  });
1434
1541
  }
1435
1542
  await this.recordCompileRunEvent(prepDataDir, context.compiledPath, context.runId, this.readinessUpdatedEvent(context.runId, context.preparationConfig.name, this.computePreparationReadiness(prepDataDir, context.preparationConfig)));
1543
+ // 0.17 — emit per-Artifact billing events (stub form: JSONL).
1544
+ // Production sink (Metronome HTTP) wires in 0.18+.
1545
+ this.emitBillingEventsForRun(prepDataDir, context);
1436
1546
  }
1437
1547
  catch (error) {
1438
1548
  await this.recordCompileRunEvent(prepDataDir, context.compiledPath, context.runId, {
@@ -1591,18 +1701,35 @@ export class LocalServiceRuntime {
1591
1701
  },
1592
1702
  });
1593
1703
  if (result.status === "updated" || result.status === "no-change") {
1704
+ let selectedPreparation = null;
1705
+ if (request.preparation) {
1706
+ const selected = this.resolvePreparationConfig(prepDataDir, request.preparation, {
1707
+ method: request.method_id,
1708
+ });
1709
+ upsertSourcePreparationConfig(prepDataDir, selected);
1710
+ this.readinessCache.invalidatePreparation(prepDataDir, selected.name);
1711
+ this.compileRunCache.invalidatePreparation(prepDataDir, selected.name);
1712
+ this.verifyRunCache.invalidatePreparation(prepDataDir, selected.name);
1713
+ this.methodListingCache.invalidate(prepDataDir);
1714
+ selectedPreparation = selected.name;
1715
+ }
1594
1716
  this.appendJobRunEvent(prepDataDir, runId, {
1595
1717
  type: "step.completed",
1596
1718
  step_id: "validate-package",
1597
- message: result.summary,
1719
+ message: selectedPreparation
1720
+ ? `${result.summary} Selected Build Plan ${request.method_id} for Preparation ${selectedPreparation}.`
1721
+ : result.summary,
1598
1722
  output: {
1599
1723
  status: result.status,
1600
1724
  validation: result.validation ?? null,
1725
+ selected_preparation: selectedPreparation,
1601
1726
  },
1602
1727
  });
1603
1728
  this.appendJobRunEvent(prepDataDir, runId, {
1604
1729
  type: "job.completed",
1605
- message: result.summary,
1730
+ message: selectedPreparation
1731
+ ? `${result.summary} Selected Build Plan ${request.method_id} for Preparation ${selectedPreparation}.`
1732
+ : result.summary,
1606
1733
  });
1607
1734
  }
1608
1735
  else {
@@ -1658,7 +1785,7 @@ export class LocalServiceRuntime {
1658
1785
  return ActionProposalPlanSchema.parse({
1659
1786
  action_type: "clarification",
1660
1787
  ...(request.preparation ? { preparation: request.preparation } : {}),
1661
- assistant_message: "No local action planner is configured for this Interf Workspace.",
1788
+ assistant_message: "No local action planner is configured for this Interf instance.",
1662
1789
  });
1663
1790
  }
1664
1791
  const preparations = listSourcePreparationConfigs(loadSourceFolderConfig(prepDataDir));
@@ -1843,6 +1970,9 @@ export class LocalServiceRuntime {
1843
1970
  hint,
1844
1971
  task_prompt: taskPrompt,
1845
1972
  checks: preparationConfig?.checks ?? [],
1973
+ requested_artifacts: preparationConfig?.requested_artifacts ?? [],
1974
+ source_profile: preparationConfig?.source_profile ?? null,
1975
+ artifact_requirements: artifactRequirementsFromRequestedArtifacts(preparationConfig?.requested_artifacts ?? []),
1846
1976
  };
1847
1977
  const commandPreview = (usePlannerText ? plan.command_preview : undefined) ??
1848
1978
  actionCommandPreview(actionType, preparationConfig?.name ?? null, methodId, proposalValues);
@@ -1960,6 +2090,9 @@ export class LocalServiceRuntime {
1960
2090
  hint,
1961
2091
  task_prompt: taskPrompt,
1962
2092
  checks: preparationConfig.checks ?? [],
2093
+ requested_artifacts: preparationConfig.requested_artifacts ?? [],
2094
+ source_profile: preparationConfig.source_profile ?? null,
2095
+ artifact_requirements: artifactRequirementsFromRequestedArtifacts(preparationConfig.requested_artifacts ?? []),
1963
2096
  };
1964
2097
  })();
1965
2098
  const title = (() => {
@@ -2066,6 +2199,114 @@ export class LocalServiceRuntime {
2066
2199
  readCompileRun(compiledPath, runId) {
2067
2200
  return readCompileRunAt(compileRunPath(compiledPath, runId));
2068
2201
  }
2202
+ hydrateCompileRunFromRuntime(prepDataDir, compiledPath, run) {
2203
+ if (!isTerminalCompileRunStatus(run.status))
2204
+ return run;
2205
+ this.refreshCompileRunFromRuntime(prepDataDir, compiledPath, run.run_id);
2206
+ return this.readCompileRun(compiledPath, run.run_id) ?? run;
2207
+ }
2208
+ finalizeInterruptedCompileRuns(prepDataDir) {
2209
+ let preparations;
2210
+ try {
2211
+ preparations = listSourcePreparationConfigs(loadSourceFolderConfig(prepDataDir));
2212
+ }
2213
+ catch {
2214
+ return;
2215
+ }
2216
+ for (const preparation of preparations) {
2217
+ const compiledPath = preparationPortableContextPath(asPreparationDataDir(prepDataDir), preparation.name);
2218
+ for (const run of listJsonFiles(compileRunsRoot(compiledPath))
2219
+ .map(readCompileRunAt)
2220
+ .filter((entry) => entry !== null)) {
2221
+ if (isTerminalCompileRunStatus(run.status) || this.activeCompileRuns.has(run.run_id))
2222
+ continue;
2223
+ const timestamp = createRunEventTimestamp();
2224
+ const interruptedRun = {
2225
+ ...run,
2226
+ stages: run.stages.map((stage) => {
2227
+ if (stage.status !== "running")
2228
+ return stage;
2229
+ return {
2230
+ ...stage,
2231
+ status: "failed",
2232
+ finished_at: stage.finished_at ?? timestamp,
2233
+ summary: stage.summary ?? INTERRUPTED_COMPILE_RUN_MESSAGE,
2234
+ failure: stage.failure ?? INTERRUPTED_COMPILE_RUN_MESSAGE,
2235
+ };
2236
+ }),
2237
+ };
2238
+ this.writeCompileRun(prepDataDir, compiledPath, applyEventToCompileRun(interruptedRun, {
2239
+ type: "run.cancelled",
2240
+ event_id: createRunEventId("event"),
2241
+ run_id: run.run_id,
2242
+ timestamp,
2243
+ reason: INTERRUPTED_COMPILE_RUN_MESSAGE,
2244
+ }));
2245
+ }
2246
+ }
2247
+ }
2248
+ finalizeInterruptedJobRuns(prepDataDir) {
2249
+ for (const run of this.listJobs(prepDataDir)) {
2250
+ if (isTerminalJobStatus(run.status))
2251
+ continue;
2252
+ const timestamp = createRunEventTimestamp();
2253
+ const interruptedRun = LocalJobRunResourceSchema.parse({
2254
+ ...run,
2255
+ steps: run.steps.map((step) => {
2256
+ if (step.status !== "running")
2257
+ return step;
2258
+ return {
2259
+ ...step,
2260
+ status: "failed",
2261
+ finished_at: step.finished_at ?? timestamp,
2262
+ summary: step.summary ?? INTERRUPTED_JOB_RUN_MESSAGE,
2263
+ };
2264
+ }),
2265
+ });
2266
+ this.writeJobRun(prepDataDir, applyEventToLocalJob(interruptedRun, {
2267
+ type: "job.failed",
2268
+ event_id: createRunEventId("event"),
2269
+ run_id: run.run_id,
2270
+ timestamp,
2271
+ message: INTERRUPTED_JOB_RUN_MESSAGE,
2272
+ }));
2273
+ }
2274
+ }
2275
+ /**
2276
+ * 0.17 — emit per-Artifact billing events when a compile run reaches
2277
+ * a terminal state. STUB FORM: writes a JSONL file alongside the run
2278
+ * record by default. Production sink (Metronome HTTP) wires in 0.18+.
2279
+ * The JSONL output is observability/dev fixture, NOT production
2280
+ * billing data.
2281
+ */
2282
+ emitBillingEventsForRun(prepDataDir, context) {
2283
+ try {
2284
+ const run = this.readCompileRun(context.compiledPath, context.runId);
2285
+ if (!run || run.artifacts.length === 0)
2286
+ return;
2287
+ const sink = this.billingEventSink ?? new JsonlBillingEventSink(defaultBillingEventLogPath({
2288
+ preparationDataDir: prepDataDir,
2289
+ preparationName: run.preparation,
2290
+ runId: context.runId,
2291
+ }));
2292
+ const events = buildCompilationEventsForRun({
2293
+ runId: context.runId,
2294
+ preparation: run.preparation,
2295
+ methodId: run.method,
2296
+ accountId: null, // 0.17 — loopback only; cloud variant fills via tokenValidator (B4.3).
2297
+ artifacts: run.artifacts,
2298
+ startedAt: run.started_at ?? null,
2299
+ finishedAt: run.finished_at ?? null,
2300
+ });
2301
+ for (const event of events) {
2302
+ sink.emit(event);
2303
+ }
2304
+ }
2305
+ catch {
2306
+ // Billing is observability-only in 0.17; never let stub failures
2307
+ // block a successful compile.
2308
+ }
2309
+ }
2069
2310
  writeCompileRun(prepDataDir, compiledPath, run) {
2070
2311
  mkdirSync(compileRunsRoot(compiledPath), { recursive: true });
2071
2312
  writeJsonFile(compileRunPath(compiledPath, run.run_id), CompileRunSchema.parse(run));
@@ -2156,6 +2397,20 @@ export class LocalServiceRuntime {
2156
2397
  }),
2157
2398
  };
2158
2399
  next.latest_proof = [...next.stages].reverse().find((stage) => Boolean(stage.latest_proof))?.latest_proof ?? next.latest_proof;
2400
+ // Recompute per-Artifact status whenever the compile run is
2401
+ // refreshed from runtime state.
2402
+ try {
2403
+ const method = getCompiledMethod(current.method, { prepDataDir });
2404
+ next.artifacts = computeArtifactStatuses({
2405
+ method,
2406
+ compiledPath,
2407
+ stageRuns: next.stages,
2408
+ counts: countsFromCompiledState(state),
2409
+ });
2410
+ }
2411
+ catch {
2412
+ next.artifacts = current.artifacts ?? [];
2413
+ }
2159
2414
  this.writeCompileRun(prepDataDir, compiledPath, next);
2160
2415
  }
2161
2416
  async emitRuntimeDerivedEvents(prepDataDir, compiledPath, runId) {