agentera 3.0.0-dev.5 → 3.0.0-dev.51

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 (598) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +119 -22
  3. package/bundle/CHANGELOG.md +1351 -0
  4. package/bundle/DESIGN.md +229 -0
  5. package/bundle/LICENSE +201 -0
  6. package/bundle/README.md +143 -0
  7. package/bundle/UPGRADE.md +236 -0
  8. package/bundle/extract-corpus-parity.json +21 -0
  9. package/bundle/fixtures/routing/hybrid-corpus.yaml +154 -0
  10. package/bundle/references/adapters/package-publication.json +437 -0
  11. package/bundle/references/adapters/package-registry.yaml +435 -189
  12. package/bundle/references/adapters/package-surface-characterization.md +66 -35
  13. package/bundle/references/adapters/runtime-lifecycle-adapters.yaml +74 -0
  14. package/bundle/references/adapters/runtime-lifecycle-authority.yaml +62 -0
  15. package/bundle/references/adapters/runtime-lifecycle-operation-contract.yaml +84 -0
  16. package/bundle/references/adapters/runtime-retired-resources.yaml +333 -0
  17. package/bundle/references/analysis/evidence-tier-authority.yaml +518 -0
  18. package/bundle/references/analysis/verification-policy.yaml +191 -0
  19. package/bundle/references/artifacts/artifact-registry-interface-model.yaml +20 -10
  20. package/bundle/references/artifacts/glossary-entry-contract.yaml +1254 -0
  21. package/bundle/references/artifacts/state-storage-authority.yaml +3022 -0
  22. package/bundle/references/artifacts/verbosity-budget-authority.yaml +42 -0
  23. package/bundle/references/cli/app-lifecycle-vocabulary.yaml +7 -8
  24. package/bundle/references/cli/capability-instruction-contract.yaml +11 -14
  25. package/bundle/references/cli/hybrid-route-contract.yaml +438 -0
  26. package/bundle/references/cli/routing-model.md +155 -0
  27. package/bundle/references/cli/trigger-schema-enrichment.md +147 -0
  28. package/bundle/references/cli/update-channels.yaml +19 -1
  29. package/bundle/references/cli/vocabulary-index.yaml +20 -17
  30. package/bundle/references/cli/vocabulary.md +326 -361
  31. package/bundle/references/meta/retained-reference-authority.yaml +151 -0
  32. package/bundle/registry.json +13 -13
  33. package/bundle/skills/agentera/SKILL.md +224 -422
  34. package/bundle/skills/agentera/capabilities/audit/schemas/artifacts.yaml +84 -0
  35. package/bundle/skills/agentera/capabilities/{inspektera → audit}/schemas/exit.yaml +2 -2
  36. package/bundle/skills/agentera/capabilities/audit/schemas/triggers.yaml +25 -0
  37. package/bundle/skills/agentera/capabilities/{inspektera → audit}/schemas/validation.yaml +37 -18
  38. package/bundle/skills/agentera/capabilities/build/schemas/artifacts.yaml +102 -0
  39. package/bundle/skills/agentera/capabilities/{realisera → build}/schemas/exit.yaml +6 -5
  40. package/bundle/skills/agentera/capabilities/build/schemas/triggers.yaml +27 -0
  41. package/bundle/skills/agentera/capabilities/build/schemas/validation.yaml +176 -0
  42. package/bundle/skills/agentera/capabilities/{visualisera → design}/schemas/artifacts.yaml +12 -12
  43. package/bundle/skills/agentera/capabilities/{visualisera → design}/schemas/exit.yaml +5 -5
  44. package/bundle/skills/agentera/capabilities/design/schemas/triggers.yaml +16 -0
  45. package/bundle/skills/agentera/capabilities/{visualisera → design}/schemas/validation.yaml +7 -7
  46. package/bundle/skills/agentera/capabilities/discuss/schemas/artifacts.yaml +51 -0
  47. package/bundle/skills/agentera/capabilities/{resonera → discuss}/schemas/exit.yaml +2 -2
  48. package/bundle/skills/agentera/capabilities/discuss/schemas/triggers.yaml +20 -0
  49. package/bundle/skills/agentera/capabilities/discuss/schemas/validation.yaml +144 -0
  50. package/bundle/skills/agentera/capabilities/document/schemas/artifacts.yaml +80 -0
  51. package/bundle/skills/agentera/capabilities/document/schemas/triggers.yaml +16 -0
  52. package/bundle/skills/agentera/capabilities/{dokumentera → document}/schemas/validation.yaml +18 -18
  53. package/bundle/skills/agentera/capabilities/{optimera → optimize}/schemas/artifacts.yaml +18 -18
  54. package/bundle/skills/agentera/capabilities/{optimera → optimize}/schemas/exit.yaml +2 -2
  55. package/bundle/skills/agentera/capabilities/optimize/schemas/triggers.yaml +16 -0
  56. package/bundle/skills/agentera/capabilities/{optimera → optimize}/schemas/validation.yaml +9 -9
  57. package/bundle/skills/agentera/capabilities/orchestrate/schemas/artifacts.yaml +76 -0
  58. package/bundle/skills/agentera/capabilities/{orkestrera → orchestrate}/schemas/exit.yaml +9 -7
  59. package/bundle/skills/agentera/capabilities/orchestrate/schemas/triggers.yaml +23 -0
  60. package/bundle/skills/agentera/capabilities/orchestrate/schemas/validation.yaml +121 -0
  61. package/bundle/skills/agentera/capabilities/plan/schemas/artifacts.yaml +66 -0
  62. package/bundle/skills/agentera/capabilities/{planera → plan}/schemas/exit.yaml +2 -2
  63. package/bundle/skills/agentera/capabilities/plan/schemas/triggers.yaml +23 -0
  64. package/bundle/skills/agentera/capabilities/plan/schemas/validation.yaml +121 -0
  65. package/bundle/skills/agentera/capabilities/profile/schemas/artifacts.yaml +23 -0
  66. package/bundle/skills/agentera/capabilities/{profilera → profile}/schemas/exit.yaml +6 -5
  67. package/bundle/skills/agentera/capabilities/profile/schemas/triggers.yaml +20 -0
  68. package/bundle/skills/agentera/capabilities/profile/schemas/validation.yaml +115 -0
  69. package/bundle/skills/agentera/capabilities/research/schemas/artifacts.yaml +24 -0
  70. package/bundle/skills/agentera/capabilities/research/schemas/triggers.yaml +16 -0
  71. package/bundle/skills/agentera/capabilities/{inspirera → research}/schemas/validation.yaml +2 -2
  72. package/bundle/skills/agentera/capabilities/status/schemas/artifacts.yaml +73 -0
  73. package/bundle/skills/agentera/capabilities/status/schemas/triggers.yaml +22 -0
  74. package/bundle/skills/agentera/capabilities/{hej → status}/schemas/validation.yaml +5 -5
  75. package/bundle/skills/agentera/capabilities/{visionera → vision}/schemas/artifacts.yaml +16 -16
  76. package/bundle/skills/agentera/capabilities/{visionera → vision}/schemas/exit.yaml +7 -6
  77. package/bundle/skills/agentera/capabilities/vision/schemas/triggers.yaml +23 -0
  78. package/bundle/skills/agentera/capabilities/{visionera → vision}/schemas/validation.yaml +18 -20
  79. package/bundle/skills/agentera/capability_schema_contract.yaml +178 -34
  80. package/bundle/skills/agentera/protocol.yaml +31 -31
  81. package/bundle/skills/agentera/route-phrases.yaml +81 -0
  82. package/bundle/skills/agentera/schemas/artifacts/changelog.yaml +5 -4
  83. package/bundle/skills/agentera/schemas/artifacts/decisions.yaml +39 -21
  84. package/bundle/skills/agentera/schemas/artifacts/design.yaml +5 -4
  85. package/bundle/skills/agentera/schemas/artifacts/docs.yaml +15 -19
  86. package/bundle/skills/agentera/schemas/artifacts/experiments.yaml +145 -14
  87. package/bundle/skills/agentera/schemas/artifacts/glossary.yaml +82 -0
  88. package/bundle/skills/agentera/schemas/artifacts/health.yaml +58 -21
  89. package/bundle/skills/agentera/schemas/artifacts/objective.yaml +22 -8
  90. package/bundle/skills/agentera/schemas/artifacts/plan.yaml +458 -36
  91. package/bundle/skills/agentera/schemas/artifacts/progress.yaml +128 -17
  92. package/bundle/skills/agentera/schemas/artifacts/todo.yaml +342 -42
  93. package/bundle/skills/agentera/schemas/artifacts/vision.yaml +3 -3
  94. package/dist/analytics/extractCorpus/cli.js +190 -0
  95. package/dist/analytics/extractCorpus/copilotSessions.js +275 -0
  96. package/dist/analytics/extractCorpus/core.js +392 -0
  97. package/dist/analytics/extractCorpus/corpus.js +167 -0
  98. package/dist/analytics/extractCorpus/coverageAudit.js +260 -0
  99. package/dist/analytics/extractCorpus/cursorSessions.js +425 -0
  100. package/dist/analytics/extractCorpus/evidenceTiers.js +603 -0
  101. package/dist/analytics/extractCorpus/extractCorpusParity.js +105 -0
  102. package/dist/analytics/extractCorpus/filesystemSources.js +118 -0
  103. package/dist/analytics/extractCorpus/index.js +14 -0
  104. package/dist/analytics/extractCorpus/jsonlSessions.js +204 -0
  105. package/dist/analytics/extractCorpus/sqliteCaps.js +43 -0
  106. package/dist/analytics/extractCorpus/sqliteSessions.js +367 -0
  107. package/dist/analytics/extractCorpus/tierReader.js +108 -0
  108. package/dist/analytics/extractCorpus.js +2 -1791
  109. package/dist/analytics/glossaryAdviceResolution.js +166 -0
  110. package/dist/analytics/glossaryInputAcquisition.js +319 -0
  111. package/dist/analytics/personalGlossaryAdmission.js +133 -0
  112. package/dist/analytics/personalGlossaryProfile.js +219 -0
  113. package/dist/analytics/profileSignals.js +211 -0
  114. package/dist/analytics/usageStats.js +169 -42
  115. package/dist/audit/terminologyDrift.js +281 -0
  116. package/dist/bin/agentera.js +3 -2
  117. package/dist/capabilities/audit/instructions.js +16 -0
  118. package/dist/capabilities/build/instructions.js +56 -0
  119. package/dist/capabilities/design/instructions.js +4 -0
  120. package/dist/capabilities/discuss/instructions.js +29 -0
  121. package/dist/capabilities/document/instructions.js +11 -0
  122. package/dist/capabilities/index.js +29 -27
  123. package/dist/capabilities/optimize/instructions.js +14 -0
  124. package/dist/capabilities/orchestrate/instructions.js +42 -0
  125. package/dist/capabilities/plan/instructions.js +33 -0
  126. package/dist/capabilities/profile/instructions.js +70 -0
  127. package/dist/capabilities/research/instructions.js +5 -0
  128. package/dist/capabilities/status/instructions.js +15 -0
  129. package/dist/capabilities/status/startupInstructions.js +34 -0
  130. package/dist/capabilities/vision/instructions.js +4 -0
  131. package/dist/cli/appContext.js +30 -24
  132. package/dist/cli/argvalidate.js +0 -1
  133. package/dist/cli/capabilityContext/benchmark.js +565 -0
  134. package/dist/cli/capabilityContext/bespoke.js +44 -0
  135. package/dist/cli/capabilityContext/build.js +242 -0
  136. package/dist/cli/capabilityContext/closeout.js +233 -0
  137. package/dist/cli/capabilityContext/contract.js +179 -0
  138. package/dist/cli/capabilityContext/evidence.js +463 -0
  139. package/dist/cli/capabilityContext/index.js +3 -0
  140. package/dist/cli/capabilityContext/orchestration.js +131 -0
  141. package/dist/cli/capabilityContext/planState.js +260 -0
  142. package/dist/cli/capabilityContext/progress.js +142 -0
  143. package/dist/cli/capabilityContext/projectVerification.js +409 -0
  144. package/dist/cli/capabilityContext/shared.js +83 -0
  145. package/dist/cli/capabilityContext/slim.js +152 -0
  146. package/dist/cli/capabilityContext/startup.js +150 -0
  147. package/dist/cli/capabilityContext/startupAggregation.js +68 -0
  148. package/dist/cli/capabilityContext/types.js +56 -0
  149. package/dist/cli/capabilityContext.js +1 -2487
  150. package/dist/cli/commands/appHome.js +22 -0
  151. package/dist/cli/commands/capability.js +4 -4
  152. package/dist/cli/commands/compact.js +164 -14
  153. package/dist/cli/commands/doctor.js +122 -97
  154. package/dist/cli/commands/durability.js +81 -0
  155. package/dist/cli/commands/glossaryAdvice.js +138 -0
  156. package/dist/cli/commands/lint.js +26 -44
  157. package/dist/cli/commands/personalGlossary.js +176 -0
  158. package/dist/cli/commands/prime/briefOrientation.js +747 -0
  159. package/dist/cli/commands/prime/buildExecutionRequest.js +165 -0
  160. package/dist/cli/commands/prime/bundleStatus.js +190 -0
  161. package/dist/cli/commands/prime/collectEntityOrientation.js +326 -0
  162. package/dist/cli/commands/prime/collectOrientationState.js +212 -0
  163. package/dist/cli/commands/prime/orientationOutput.js +399 -0
  164. package/dist/cli/commands/prime/types.js +1 -0
  165. package/dist/cli/commands/prime/v1Migration.js +38 -0
  166. package/dist/cli/commands/prime.js +80 -558
  167. package/dist/cli/commands/profileGrounding.js +55 -0
  168. package/dist/cli/commands/query.js +78 -53
  169. package/dist/cli/commands/report.js +96 -25
  170. package/dist/cli/commands/route.js +142 -0
  171. package/dist/cli/commands/schema.js +266 -143
  172. package/dist/cli/commands/state/decisions.js +387 -0
  173. package/dist/cli/commands/state/docs.js +70 -0
  174. package/dist/cli/commands/state/experimentRecords.js +151 -0
  175. package/dist/cli/commands/state/experiments.js +14 -0
  176. package/dist/cli/commands/state/get.js +129 -0
  177. package/dist/cli/commands/state/health.js +14 -0
  178. package/dist/cli/commands/state/index.js +16 -0
  179. package/dist/cli/commands/state/list.js +194 -0
  180. package/dist/cli/commands/state/objective.js +25 -0
  181. package/dist/cli/commands/state/plan.js +14 -0
  182. package/dist/cli/commands/state/planTasks.js +154 -0
  183. package/dist/cli/commands/state/plans.js +154 -0
  184. package/dist/cli/commands/state/progress.js +13 -0
  185. package/dist/cli/commands/state/shared.js +14 -0
  186. package/dist/cli/commands/state/todo.js +42 -0
  187. package/dist/cli/commands/state/write.js +430 -0
  188. package/dist/cli/commands/upgrade.js +146 -5
  189. package/dist/cli/commands/upgradeVerify.js +118 -0
  190. package/dist/cli/commands/validate.js +272 -223
  191. package/dist/cli/commands/verify.js +24 -9
  192. package/dist/cli/contracts/bundleStatus.js +1 -0
  193. package/dist/cli/contracts/orientationState.js +1 -0
  194. package/dist/cli/dispatch/argvParser.js +32 -0
  195. package/dist/cli/dispatch/check.js +372 -0
  196. package/dist/cli/dispatch/commands.js +27 -0
  197. package/dist/cli/dispatch/index.js +207 -0
  198. package/dist/cli/dispatch/lifecycle.js +652 -0
  199. package/dist/cli/dispatch/prime.js +139 -0
  200. package/dist/cli/dispatch/projections.js +17 -0
  201. package/dist/cli/dispatch/shared.js +63 -0
  202. package/dist/cli/dispatch/state.js +140 -0
  203. package/dist/cli/dispatch.js +1 -1294
  204. package/dist/cli/errors.js +16 -17
  205. package/dist/cli/help.js +394 -43
  206. package/dist/cli/migrationRequired.js +87 -0
  207. package/dist/cli/orientation/attention.js +61 -0
  208. package/dist/cli/orientation/corpusCoverage.js +102 -0
  209. package/dist/cli/orientation.js +438 -170
  210. package/dist/cli/planArtifacts.js +350 -0
  211. package/dist/cli/planEvidence.js +157 -0
  212. package/dist/cli/planLifecycleState.js +36 -0
  213. package/dist/cli/planTaskIndex.js +11 -0
  214. package/dist/cli/preCutoverCommand.js +32 -0
  215. package/dist/cli/prime-blob.js +71 -3
  216. package/dist/cli/profileAcquisition.js +167 -0
  217. package/dist/cli/startupCompletenessContract.js +59 -0
  218. package/dist/cli/stateQuery.js +56 -14
  219. package/dist/cli/structured.js +0 -1
  220. package/dist/cli/todoMarkdown.js +25 -12
  221. package/dist/cli/todoReadinessSelection.js +192 -0
  222. package/dist/cli/todoSeverity.js +18 -0
  223. package/dist/core/atomicWriter.js +20 -0
  224. package/dist/core/developmentChannel.js +1 -0
  225. package/dist/core/developmentInvocation.js +374 -0
  226. package/dist/core/difflib.js +0 -1
  227. package/dist/core/envPaths.js +20 -0
  228. package/dist/core/jsonValue.js +5 -0
  229. package/dist/core/paths.js +0 -1
  230. package/dist/core/pyjson.js +67 -3
  231. package/dist/core/shell.js +4 -0
  232. package/dist/core/skillIdentity.js +23 -0
  233. package/dist/core/sourceRoot.js +9 -12
  234. package/dist/core/text.js +10 -0
  235. package/dist/core/toml.js +0 -1
  236. package/dist/core/yaml.js +8 -1
  237. package/dist/eval/evalSkills.js +30 -26
  238. package/dist/eval/hybridRouteEvaluation.js +155 -0
  239. package/dist/eval/semanticEval.js +43 -20
  240. package/dist/eval/semanticFixtures.js +16 -5
  241. package/dist/hooks/common.js +11 -79
  242. package/dist/hooks/compaction/apply.js +446 -0
  243. package/dist/hooks/compaction/dryRun.js +184 -0
  244. package/dist/hooks/compaction/index.js +22 -0
  245. package/dist/hooks/compaction/parse.js +325 -0
  246. package/dist/hooks/compaction/retention.js +202 -0
  247. package/dist/hooks/compaction/status.js +360 -0
  248. package/dist/hooks/compaction/types.js +5 -0
  249. package/dist/hooks/validateArtifact/agentFacing.js +9 -0
  250. package/dist/hooks/validateArtifact/index.js +92 -0
  251. package/dist/hooks/validateArtifact/markdown.js +145 -0
  252. package/dist/hooks/validateArtifact/schema.js +546 -0
  253. package/dist/hooks/validateArtifact/traversal.js +107 -0
  254. package/dist/hooks/validateArtifact/violations.js +107 -0
  255. package/dist/migrate/v2HandoffManifest.js +452 -0
  256. package/dist/registries/activationContract.js +2 -0
  257. package/dist/registries/activationTuples.js +107 -0
  258. package/dist/registries/artifactProtocolIds.js +78 -0
  259. package/dist/registries/artifactRegistry.js +224 -63
  260. package/dist/registries/artifactSchemaProjection.js +45 -0
  261. package/dist/registries/capabilityContract.js +129 -2
  262. package/dist/registries/evaluatorHandoffContract.js +2 -3
  263. package/dist/registries/evidenceTierContract.js +371 -0
  264. package/dist/registries/glossaryAdviceContract.js +79 -0
  265. package/dist/registries/glossaryCaveatContract.js +67 -0
  266. package/dist/registries/glossaryConsumerContractValidation.js +787 -0
  267. package/dist/registries/glossaryEntryContract.js +757 -0
  268. package/dist/registries/glossaryTermIdentity.js +17 -0
  269. package/dist/registries/glossaryTermOccurrence.js +20 -0
  270. package/dist/registries/hybridRoute.js +274 -0
  271. package/dist/registries/hybridRouteReceipt.js +221 -0
  272. package/dist/registries/packagePublication.js +229 -0
  273. package/dist/registries/packageRegistry.js +187 -211
  274. package/dist/registries/todoReadinessContract.js +448 -0
  275. package/dist/registries/triggerLoader.js +174 -0
  276. package/dist/registries/verbosityBudgetContract.js +205 -0
  277. package/dist/release/releaseMetadata.js +385 -0
  278. package/dist/runtime/lifecycleAdapterContract.js +457 -0
  279. package/dist/runtime/lifecycleAdapters.js +777 -0
  280. package/dist/runtime/lifecycleAuthority.js +453 -0
  281. package/dist/runtime/lifecycleOperationContract.js +123 -0
  282. package/dist/runtime/lifecycleOperations.js +634 -0
  283. package/dist/runtime/lifecycleOwnershipJournal.js +622 -0
  284. package/dist/runtime/lifecyclePublication.js +415 -0
  285. package/dist/runtime/lifecycleSnapshot.js +438 -0
  286. package/dist/runtime/nativeResourceCleanup.js +502 -0
  287. package/dist/setup/opencode.js +20 -0
  288. package/dist/setup/opencodeConstants.js +1 -0
  289. package/dist/setup/sharedSkill.js +45 -0
  290. package/dist/setup/smokeChecks.js +21 -82
  291. package/dist/state/archiveDiscovery.js +580 -0
  292. package/dist/state/archivePublication.js +225 -0
  293. package/dist/state/archiveRecovery.js +237 -0
  294. package/dist/state/archiveReplay.js +53 -0
  295. package/dist/state/canonicalMigrationRecord.js +36 -0
  296. package/dist/state/changelog.js +221 -0
  297. package/dist/state/decisionEntities.js +367 -0
  298. package/dist/state/decisionLegacyValidation.js +215 -0
  299. package/dist/state/decisionMigrationProvenance.js +199 -0
  300. package/dist/state/decisionOverlay.js +254 -0
  301. package/dist/state/decisionRevision.js +256 -0
  302. package/dist/state/decisionRevisionMigration.js +89 -0
  303. package/dist/state/directRetrieval.js +323 -0
  304. package/dist/state/durability.js +324 -0
  305. package/dist/state/entityCutover.js +459 -0
  306. package/dist/state/entityCutoverGit.js +127 -0
  307. package/dist/state/entityGlossaryAuthority.js +13 -0
  308. package/dist/state/entityListProjection.js +187 -0
  309. package/dist/state/entityListRuntimeRegistry.js +85 -0
  310. package/dist/state/entityMigrationCausality.js +97 -0
  311. package/dist/state/entityMigrationIdentity.js +5 -0
  312. package/dist/state/entityMigrationPreview.js +846 -0
  313. package/dist/state/entityMigrationTodo.js +133 -0
  314. package/dist/state/entityPublicationContext.js +806 -0
  315. package/dist/state/entityRetrievalHelp.js +506 -0
  316. package/dist/state/entityStorage.js +899 -0
  317. package/dist/state/exactReplacementRecovery.js +18 -0
  318. package/dist/state/experimentArchive.js +63 -0
  319. package/dist/state/experimentIdentity.js +202 -0
  320. package/dist/state/healthEntities.js +262 -0
  321. package/dist/state/healthEntityValidation.js +18 -0
  322. package/dist/state/healthRepair.js +69 -0
  323. package/dist/state/installRoot.js +64 -25
  324. package/dist/state/legacyEntityCutoverEvidence.js +65 -0
  325. package/dist/state/legacyIdentity.js +47 -0
  326. package/dist/state/legacySummaryRecord.js +6 -0
  327. package/dist/state/listAccounting.js +54 -0
  328. package/dist/state/listClassification.js +17 -0
  329. package/dist/state/listCursor.js +42 -0
  330. package/dist/state/listRetrieval.js +781 -0
  331. package/dist/state/migrationSourceBinding.js +30 -0
  332. package/dist/state/objectiveExperimentEntities.js +322 -0
  333. package/dist/state/planEntities.js +982 -0
  334. package/dist/state/planIdentity.js +47 -0
  335. package/dist/state/planLineageValidation.js +52 -0
  336. package/dist/state/planReplacementTransaction.js +475 -0
  337. package/dist/state/planRetrieval.js +313 -0
  338. package/dist/state/planTaskRetrieval.js +294 -0
  339. package/dist/state/progressEntities.js +512 -0
  340. package/dist/state/progressGlossaryCaveat.js +168 -0
  341. package/dist/state/progressPublicationOrder.js +19 -0
  342. package/dist/state/projectRoot.js +62 -0
  343. package/dist/state/projectionPolicy.js +190 -0
  344. package/dist/state/retrievalAuthority.js +156 -0
  345. package/dist/state/safeProjectFile.js +155 -0
  346. package/dist/state/startupAnalysis/benchmark.js +432 -0
  347. package/dist/state/startupAnalysis/contract.js +160 -0
  348. package/dist/state/startupAnalysis/helpers.js +286 -0
  349. package/dist/state/startupAnalysis/index.js +6 -0
  350. package/dist/state/startupAnalysis/metrics.js +340 -0
  351. package/dist/state/startupAnalysis/records.js +198 -0
  352. package/dist/state/startupAnalysis/report.js +111 -0
  353. package/dist/state/startupAnalysis/threshold.js +512 -0
  354. package/dist/state/startupAnalysis.js +2 -1953
  355. package/dist/state/startupProjection.js +499 -0
  356. package/dist/state/stateMode.js +85 -0
  357. package/dist/state/stateStorageAuthority.js +47 -0
  358. package/dist/state/summaryEntityRead.js +25 -0
  359. package/dist/state/summaryMigrationProvenance.js +66 -0
  360. package/dist/state/summarySourceRowAuthority.js +36 -0
  361. package/dist/state/todoActivationSafety.js +58 -0
  362. package/dist/state/todoDocsEntities.js +1069 -0
  363. package/dist/state/todoDocsEntityValidation.js +158 -0
  364. package/dist/state/todoMarkdownProjection.js +51 -0
  365. package/dist/state/todoReconciliationActivation.js +140 -0
  366. package/dist/state/todoReconciliationInspection.js +169 -0
  367. package/dist/state/todoReconciliationRepair.js +170 -0
  368. package/dist/state/todoReconciliationTransaction.js +582 -0
  369. package/dist/state/write/assign.js +20 -0
  370. package/dist/state/write/candidateMutation.js +269 -0
  371. package/dist/state/write/errors.js +11 -0
  372. package/dist/state/write/experimentPublication.js +192 -0
  373. package/dist/state/write/explain.js +363 -0
  374. package/dist/state/write/fields.js +61 -0
  375. package/dist/state/write/glossaryPublication.js +263 -0
  376. package/dist/state/write/grammar.js +285 -0
  377. package/dist/state/write/helpers.js +29 -0
  378. package/dist/state/write/index.js +12 -0
  379. package/dist/state/write/input.js +367 -0
  380. package/dist/state/write/lock.js +837 -0
  381. package/dist/state/write/mutation.js +234 -0
  382. package/dist/state/write/operations.js +170 -0
  383. package/dist/state/write/planEvaluation.js +128 -0
  384. package/dist/state/write/planPublication.js +179 -0
  385. package/dist/state/write/runtimeOperations.js +144 -0
  386. package/dist/state/write/serialize.js +1 -0
  387. package/dist/state/write/transaction.js +52 -0
  388. package/dist/state/write/validate.js +7 -0
  389. package/dist/upgrade/appContentRefresh.js +383 -0
  390. package/dist/upgrade/appModel.js +8 -7
  391. package/dist/upgrade/atomicWriter.js +1 -0
  392. package/dist/upgrade/bundleEvidence.js +33 -0
  393. package/dist/upgrade/channels.js +12 -8
  394. package/dist/upgrade/compatibility.js +56 -21
  395. package/dist/upgrade/doctor.js +197 -192
  396. package/dist/upgrade/doctorClassifier.js +187 -0
  397. package/dist/upgrade/installedHooksRetirement.js +196 -0
  398. package/dist/upgrade/legacyAgentCleanup.js +282 -0
  399. package/dist/upgrade/lifecycleUpgrade.js +113 -0
  400. package/dist/upgrade/migrateArtifactsV2ToV3.js +367 -149
  401. package/dist/upgrade/migrationPublication.js +122 -0
  402. package/dist/upgrade/nextMajorDoctor.js +42 -8
  403. package/dist/upgrade/npxPlatformStatus.js +22 -0
  404. package/dist/upgrade/projectIntegration.js +145 -187
  405. package/dist/upgrade/projectIntegrationDecision.js +50 -0
  406. package/dist/upgrade/retiredResourceDiagnostics.js +117 -0
  407. package/dist/upgrade/runtimeMigration.js +273 -363
  408. package/dist/upgrade/upgradeCommands.js +14 -1
  409. package/dist/upgrade/upgradeLock.js +85 -0
  410. package/dist/upgrade/upgradeOrchestrator.js +263 -59
  411. package/dist/upgrade/v3CapabilitySurface.js +14 -0
  412. package/dist/upgrade/versionResolution.js +8 -1
  413. package/dist/validate/activationArtifactEvidence.js +844 -0
  414. package/dist/validate/activationConjunction.js +816 -0
  415. package/dist/validate/activationEvidenceManifest.js +515 -0
  416. package/dist/validate/activationPackageSemantics.js +29 -0
  417. package/dist/validate/appHomeContract.js +5 -11
  418. package/dist/validate/bootstrapAuthority.js +44 -0
  419. package/dist/validate/capability.js +133 -12
  420. package/dist/validate/crossCapability.js +15 -10
  421. package/dist/validate/retainedReferenceAuthority.js +879 -0
  422. package/dist/validate/selfAudit.js +73 -37
  423. package/dist/validate/v1LegacyCruft.js +131 -0
  424. package/dist/validate/vocabularyAuthority.js +303 -0
  425. package/package.json +53 -27
  426. package/bundle/references/adapters/cursor.md +0 -213
  427. package/bundle/references/adapters/opencode.md +0 -530
  428. package/bundle/references/adapters/package-manifest-interface-model.yaml +0 -337
  429. package/bundle/references/adapters/runtime-adapter-characterization.md +0 -79
  430. package/bundle/references/adapters/runtime-adapter-interface-model.yaml +0 -200
  431. package/bundle/references/adapters/runtime-adapter-registry.yaml +0 -548
  432. package/bundle/references/adapters/runtime-feature-parity.md +0 -189
  433. package/bundle/references/analysis/benchmark.md +0 -267
  434. package/bundle/references/analysis/startup-measurement-contract.yaml +0 -424
  435. package/bundle/references/cli/agent-ready-state-contract.yaml +0 -953
  436. package/bundle/references/cli/audience-namespace-cli-migration.yaml +0 -355
  437. package/bundle/references/cli/bundle-skill-vocabulary.yaml +0 -278
  438. package/bundle/references/cli/capability-tool-classification.yaml +0 -53
  439. package/bundle/references/cli/routing-execution-vocabulary.yaml +0 -281
  440. package/bundle/references/meta/documentation-inventory.md +0 -43
  441. package/bundle/references/v1-section-mapping.md +0 -47
  442. package/bundle/skills/agentera/.claude-plugin/plugin.json +0 -27
  443. package/bundle/skills/agentera/agents/dokumentera.toml +0 -6
  444. package/bundle/skills/agentera/agents/hej.toml +0 -6
  445. package/bundle/skills/agentera/agents/inspektera.toml +0 -6
  446. package/bundle/skills/agentera/agents/inspirera.toml +0 -6
  447. package/bundle/skills/agentera/agents/optimera.toml +0 -6
  448. package/bundle/skills/agentera/agents/orkestrera.toml +0 -6
  449. package/bundle/skills/agentera/agents/planera.toml +0 -6
  450. package/bundle/skills/agentera/agents/profilera.toml +0 -6
  451. package/bundle/skills/agentera/agents/realisera.toml +0 -6
  452. package/bundle/skills/agentera/agents/resonera.toml +0 -6
  453. package/bundle/skills/agentera/agents/visionera.toml +0 -6
  454. package/bundle/skills/agentera/agents/visualisera.toml +0 -6
  455. package/bundle/skills/agentera/capabilities/dokumentera/schemas/artifacts.yaml +0 -73
  456. package/bundle/skills/agentera/capabilities/dokumentera/schemas/triggers.yaml +0 -35
  457. package/bundle/skills/agentera/capabilities/hej/schemas/artifacts.yaml +0 -69
  458. package/bundle/skills/agentera/capabilities/hej/schemas/triggers.yaml +0 -58
  459. package/bundle/skills/agentera/capabilities/inspektera/schemas/artifacts.yaml +0 -76
  460. package/bundle/skills/agentera/capabilities/inspektera/schemas/triggers.yaml +0 -38
  461. package/bundle/skills/agentera/capabilities/inspirera/schemas/artifacts.yaml +0 -24
  462. package/bundle/skills/agentera/capabilities/inspirera/schemas/triggers.yaml +0 -34
  463. package/bundle/skills/agentera/capabilities/optimera/schemas/triggers.yaml +0 -39
  464. package/bundle/skills/agentera/capabilities/orkestrera/schemas/artifacts.yaml +0 -64
  465. package/bundle/skills/agentera/capabilities/orkestrera/schemas/triggers.yaml +0 -42
  466. package/bundle/skills/agentera/capabilities/orkestrera/schemas/validation.yaml +0 -120
  467. package/bundle/skills/agentera/capabilities/planera/schemas/artifacts.yaml +0 -62
  468. package/bundle/skills/agentera/capabilities/planera/schemas/triggers.yaml +0 -34
  469. package/bundle/skills/agentera/capabilities/planera/schemas/validation.yaml +0 -61
  470. package/bundle/skills/agentera/capabilities/profilera/schemas/artifacts.yaml +0 -18
  471. package/bundle/skills/agentera/capabilities/profilera/schemas/triggers.yaml +0 -45
  472. package/bundle/skills/agentera/capabilities/profilera/schemas/validation.yaml +0 -57
  473. package/bundle/skills/agentera/capabilities/realisera/schemas/artifacts.yaml +0 -80
  474. package/bundle/skills/agentera/capabilities/realisera/schemas/triggers.yaml +0 -39
  475. package/bundle/skills/agentera/capabilities/realisera/schemas/validation.yaml +0 -110
  476. package/bundle/skills/agentera/capabilities/resonera/schemas/artifacts.yaml +0 -47
  477. package/bundle/skills/agentera/capabilities/resonera/schemas/triggers.yaml +0 -46
  478. package/bundle/skills/agentera/capabilities/resonera/schemas/validation.yaml +0 -77
  479. package/bundle/skills/agentera/capabilities/visionera/schemas/triggers.yaml +0 -41
  480. package/bundle/skills/agentera/capabilities/visualisera/schemas/triggers.yaml +0 -33
  481. package/bundle/skills/agentera/references/contract.md +0 -1039
  482. package/bundle/skills/hej/.claude-plugin/plugin.json +0 -6
  483. package/bundle/skills/hej/SKILL.md +0 -69
  484. package/bundle/skills/hej/agents/hej.toml +0 -11
  485. package/bundle/skills/hej/agents/openai.yaml +0 -8
  486. package/dist/analytics/extractCorpus.js.map +0 -1
  487. package/dist/analytics/usageStats.js.map +0 -1
  488. package/dist/bin/agentera.js.map +0 -1
  489. package/dist/capabilities/dokumentera/instructions.js +0 -6
  490. package/dist/capabilities/dokumentera/instructions.js.map +0 -1
  491. package/dist/capabilities/hej/instructions.js +0 -6
  492. package/dist/capabilities/hej/instructions.js.map +0 -1
  493. package/dist/capabilities/index.js.map +0 -1
  494. package/dist/capabilities/inspektera/instructions.js +0 -6
  495. package/dist/capabilities/inspektera/instructions.js.map +0 -1
  496. package/dist/capabilities/inspirera/instructions.js +0 -6
  497. package/dist/capabilities/inspirera/instructions.js.map +0 -1
  498. package/dist/capabilities/optimera/instructions.js +0 -6
  499. package/dist/capabilities/optimera/instructions.js.map +0 -1
  500. package/dist/capabilities/orkestrera/instructions.js +0 -6
  501. package/dist/capabilities/orkestrera/instructions.js.map +0 -1
  502. package/dist/capabilities/planera/instructions.js +0 -6
  503. package/dist/capabilities/planera/instructions.js.map +0 -1
  504. package/dist/capabilities/profilera/instructions.js +0 -6
  505. package/dist/capabilities/profilera/instructions.js.map +0 -1
  506. package/dist/capabilities/realisera/instructions.js +0 -6
  507. package/dist/capabilities/realisera/instructions.js.map +0 -1
  508. package/dist/capabilities/resonera/instructions.js +0 -6
  509. package/dist/capabilities/resonera/instructions.js.map +0 -1
  510. package/dist/capabilities/visionera/instructions.js +0 -6
  511. package/dist/capabilities/visionera/instructions.js.map +0 -1
  512. package/dist/capabilities/visualisera/instructions.js +0 -6
  513. package/dist/capabilities/visualisera/instructions.js.map +0 -1
  514. package/dist/cli/appContext.js.map +0 -1
  515. package/dist/cli/argvalidate.js.map +0 -1
  516. package/dist/cli/capabilityContext.js.map +0 -1
  517. package/dist/cli/commands/capability.js.map +0 -1
  518. package/dist/cli/commands/compact.js.map +0 -1
  519. package/dist/cli/commands/doctor.js.map +0 -1
  520. package/dist/cli/commands/lint.js.map +0 -1
  521. package/dist/cli/commands/prime.js.map +0 -1
  522. package/dist/cli/commands/query.js.map +0 -1
  523. package/dist/cli/commands/report.js.map +0 -1
  524. package/dist/cli/commands/schema.js.map +0 -1
  525. package/dist/cli/commands/state.js +0 -1023
  526. package/dist/cli/commands/state.js.map +0 -1
  527. package/dist/cli/commands/upgrade.js.map +0 -1
  528. package/dist/cli/commands/validate.js.map +0 -1
  529. package/dist/cli/commands/verify.js.map +0 -1
  530. package/dist/cli/dispatch.js.map +0 -1
  531. package/dist/cli/errors.js.map +0 -1
  532. package/dist/cli/help.js.map +0 -1
  533. package/dist/cli/orientation.js.map +0 -1
  534. package/dist/cli/prime-blob.js.map +0 -1
  535. package/dist/cli/stateQuery.js.map +0 -1
  536. package/dist/cli/structured.js.map +0 -1
  537. package/dist/cli/todoMarkdown.js.map +0 -1
  538. package/dist/core/difflib.js.map +0 -1
  539. package/dist/core/paths.js.map +0 -1
  540. package/dist/core/pyjson.js.map +0 -1
  541. package/dist/core/sourceRoot.js.map +0 -1
  542. package/dist/core/toml.js.map +0 -1
  543. package/dist/core/yaml.js.map +0 -1
  544. package/dist/eval/evalSkills.js.map +0 -1
  545. package/dist/eval/semanticEval.js.map +0 -1
  546. package/dist/eval/semanticFixtures.js.map +0 -1
  547. package/dist/hooks/common.js.map +0 -1
  548. package/dist/hooks/compaction.js +0 -935
  549. package/dist/hooks/compaction.js.map +0 -1
  550. package/dist/hooks/cursorPreToolUse.js +0 -19
  551. package/dist/hooks/cursorPreToolUse.js.map +0 -1
  552. package/dist/hooks/cursorSessionStart.js +0 -71
  553. package/dist/hooks/cursorSessionStart.js.map +0 -1
  554. package/dist/hooks/sessionStart.js +0 -209
  555. package/dist/hooks/sessionStart.js.map +0 -1
  556. package/dist/hooks/sessionStop.js +0 -212
  557. package/dist/hooks/sessionStop.js.map +0 -1
  558. package/dist/hooks/validateArtifact.js +0 -935
  559. package/dist/hooks/validateArtifact.js.map +0 -1
  560. package/dist/registries/artifactRegistry.js.map +0 -1
  561. package/dist/registries/capabilityContract.js.map +0 -1
  562. package/dist/registries/evaluatorHandoffContract.js.map +0 -1
  563. package/dist/registries/packageRegistry.js.map +0 -1
  564. package/dist/registries/runtimeAdapterRegistry.js +0 -315
  565. package/dist/registries/runtimeAdapterRegistry.js.map +0 -1
  566. package/dist/setup/codex.js +0 -1197
  567. package/dist/setup/codex.js.map +0 -1
  568. package/dist/setup/copilot.js +0 -227
  569. package/dist/setup/copilot.js.map +0 -1
  570. package/dist/setup/cursor.js +0 -127
  571. package/dist/setup/cursor.js.map +0 -1
  572. package/dist/setup/doctor.js +0 -1297
  573. package/dist/setup/doctor.js.map +0 -1
  574. package/dist/setup/smokeChecks.js.map +0 -1
  575. package/dist/state/installRoot.js.map +0 -1
  576. package/dist/state/startupAnalysis.js.map +0 -1
  577. package/dist/upgrade/appModel.js.map +0 -1
  578. package/dist/upgrade/channels.js.map +0 -1
  579. package/dist/upgrade/compatibility.js.map +0 -1
  580. package/dist/upgrade/doctor.js.map +0 -1
  581. package/dist/upgrade/migrateArtifactsV1ToV2.js +0 -570
  582. package/dist/upgrade/migrateArtifactsV1ToV2.js.map +0 -1
  583. package/dist/upgrade/migrateArtifactsV2ToV3.js.map +0 -1
  584. package/dist/upgrade/nextMajorDoctor.js.map +0 -1
  585. package/dist/upgrade/projectIntegration.js.map +0 -1
  586. package/dist/upgrade/runtimeMigration.js.map +0 -1
  587. package/dist/upgrade/upgradeCommands.js.map +0 -1
  588. package/dist/upgrade/upgradeOrchestrator.js.map +0 -1
  589. package/dist/upgrade/versionResolution.js.map +0 -1
  590. package/dist/validate/appHomeContract.js.map +0 -1
  591. package/dist/validate/capability.js.map +0 -1
  592. package/dist/validate/crossCapability.js.map +0 -1
  593. package/dist/validate/lifecycleAdapters.js +0 -772
  594. package/dist/validate/lifecycleAdapters.js.map +0 -1
  595. package/dist/validate/selfAudit.js.map +0 -1
  596. /package/bundle/skills/agentera/capabilities/{dokumentera → document}/schemas/exit.yaml +0 -0
  597. /package/bundle/skills/agentera/capabilities/{inspirera → research}/schemas/exit.yaml +0 -0
  598. /package/bundle/skills/agentera/capabilities/{hej → status}/schemas/exit.yaml +0 -0
@@ -0,0 +1,18 @@
1
+ export class ExactReplacementConflictError extends Error {
2
+ retainedPaths;
3
+ constructor(message, retainedPaths = []) {
4
+ super(message);
5
+ this.retainedPaths = retainedPaths;
6
+ this.name = "ExactReplacementConflictError";
7
+ }
8
+ }
9
+ export class FileReplacementError extends Error {
10
+ code;
11
+ constructor(message, cause) {
12
+ super(message, { cause });
13
+ this.name = "FileReplacementError";
14
+ const code = cause?.code;
15
+ if (typeof code === "string")
16
+ this.code = code;
17
+ }
18
+ }
@@ -0,0 +1,63 @@
1
+ import { createHash } from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { dumpYamlMapping } from "../core/yaml.js";
5
+ import { assertRealpathBoundary } from "../registries/artifactRegistry.js";
6
+ import { canonicalRecordJson } from "./archiveDiscovery.js";
7
+ import { reject } from "./write/errors.js";
8
+ const AUTHORITY = "references/artifacts/state-storage-authority.yaml";
9
+ export function prepareExperimentArchive(projectRoot, objectivePath, objectiveId, experimentNumber, record) {
10
+ if (record.number !== experimentNumber)
11
+ throw new Error(`experiment archive record number must equal ${experimentNumber}`);
12
+ const stableId = `${objectiveId}/experiment:${experimentNumber}`;
13
+ const target = path.join(path.dirname(objectivePath), "archive", "experiments", `${experimentNumber}.yaml`);
14
+ assertRealpathBoundary(projectRoot, target, "experiment archive");
15
+ const canonical = canonicalRecordJson(record);
16
+ const recordSha256 = createHash("sha256").update(canonical, "utf8").digest("hex");
17
+ const provenance = {
18
+ authority: AUTHORITY,
19
+ objective_id: objectiveId,
20
+ experiment_id: stableId,
21
+ storage_scope: "objective_directory",
22
+ publication_order: "archive_before_projection",
23
+ };
24
+ const envelope = {
25
+ schemaVersion: "agentera.experimentArchive.v1",
26
+ stable_id: stableId,
27
+ objective_id: objectiveId,
28
+ experiment_number: experimentNumber,
29
+ record,
30
+ record_sha256: recordSha256,
31
+ provenance,
32
+ };
33
+ return {
34
+ target,
35
+ directoryDurabilityRoot: path.dirname(objectivePath),
36
+ bytes: dumpYamlMapping(envelope),
37
+ stableId,
38
+ recordSha256,
39
+ provenance,
40
+ };
41
+ }
42
+ export function assertExperimentArchiveReplay(publication) {
43
+ let existing;
44
+ try {
45
+ existing = fs.readFileSync(publication.target);
46
+ }
47
+ catch {
48
+ reject({
49
+ class: "conflict",
50
+ message: `immutable experiment archive '${publication.target}' could not be read; existing state was preserved`,
51
+ violations: [`immutable archive identity: ${publication.stableId}`],
52
+ recovery: "Repair the unreadable archive record before retrying publication.",
53
+ });
54
+ }
55
+ if (existing.equals(Buffer.from(publication.bytes, "utf8")))
56
+ return;
57
+ reject({
58
+ class: "conflict",
59
+ message: `immutable experiment archive '${publication.target}' already exists with different bytes; existing archive and projection were preserved`,
60
+ violations: [`immutable archive identity: ${publication.stableId}`],
61
+ recovery: "Retry only with the byte-equivalent experiment record for this stable identity.",
62
+ });
63
+ }
@@ -0,0 +1,202 @@
1
+ import { createHash } from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { loadYamlMapping } from "../core/yaml.js";
5
+ import { canonicalRecordJson } from "./archiveDiscovery.js";
6
+ export const PERSISTED_OBJECTIVE_ID = /^objective:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
7
+ export const LEGACY_OBJECTIVE_ID = /^legacy-objective:[0-9a-f]{64}$/;
8
+ export const OBJECTIVE_ID = /^(objective:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|legacy-objective:[0-9a-f]{64})$/;
9
+ function mapping(value) {
10
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
11
+ }
12
+ function list(value) {
13
+ return Array.isArray(value) ? value.filter((entry) => entry !== null && typeof entry === "object" && !Array.isArray(entry)) : [];
14
+ }
15
+ /** Identity inputs deliberately exclude title, lifecycle, closure, root, and slug. */
16
+ export function canonicalObjectiveIdentityDocument(document) {
17
+ const objective = mapping(document.objective);
18
+ return {
19
+ baseline: mapping(document.baseline),
20
+ constraints: document.constraints ?? objective.constraints ?? [],
21
+ description: objective.description ?? document.description ?? "",
22
+ gates: mapping(document.gates),
23
+ measurement: objective.measurement ?? document.measurement ?? "",
24
+ metric: mapping(document.metric),
25
+ scope: mapping(document.scope),
26
+ };
27
+ }
28
+ export function resolveObjectiveIdentity(document) {
29
+ const canonicalJson = canonicalRecordJson(canonicalObjectiveIdentityDocument(document));
30
+ const persisted = mapping(document.header).id;
31
+ if (persisted !== undefined) {
32
+ if (typeof persisted !== "string" || !PERSISTED_OBJECTIVE_ID.test(persisted)) {
33
+ throw new Error("objective header.id is not a valid persisted objective identity");
34
+ }
35
+ return { stableId: persisted, persisted: true, canonicalJson, compatibility: "canonical" };
36
+ }
37
+ return {
38
+ stableId: `legacy-objective:${createHash("sha256").update(canonicalJson, "utf8").digest("hex")}`,
39
+ persisted: false,
40
+ canonicalJson,
41
+ compatibility: "legacy_derived_identity",
42
+ };
43
+ }
44
+ export function discoverObjectiveArtifacts(projectRoot, sourceBytes) {
45
+ const inspected = [];
46
+ const diagnostics = [];
47
+ for (const root of ["optimize", "optimera"]) {
48
+ const directory = path.join(projectRoot, ".agentera", root);
49
+ let sources = [];
50
+ if (sourceBytes) {
51
+ sources = [...sourceBytes.entries()].flatMap(([objectivePath, bytes]) => {
52
+ const candidate = path.relative(directory, objectivePath).split(path.sep);
53
+ return candidate.length === 2 && candidate[1] === "objective.yaml"
54
+ ? [{ slug: candidate[0], objectivePath, bytes }]
55
+ : [];
56
+ }).sort((left, right) => left.slug.localeCompare(right.slug));
57
+ }
58
+ else {
59
+ let slugs = [];
60
+ try {
61
+ slugs = fs.readdirSync(directory).sort();
62
+ }
63
+ catch {
64
+ continue;
65
+ }
66
+ sources = slugs.flatMap((slug) => {
67
+ const objectivePath = path.join(directory, slug, "objective.yaml");
68
+ return fs.existsSync(objectivePath) ? [{ slug, objectivePath }] : [];
69
+ });
70
+ }
71
+ for (const { slug, objectivePath, bytes } of sources) {
72
+ try {
73
+ const identity = resolveObjectiveIdentity(loadYamlMapping(bytes !== undefined ? bytes.toString("utf8") : fs.readFileSync(objectivePath, "utf8")));
74
+ inspected.push({ ...identity, path: objectivePath, slug, root });
75
+ }
76
+ catch (error) {
77
+ diagnostics.push({
78
+ class: "corrupt",
79
+ message: error.message,
80
+ candidate_paths: [objectivePath],
81
+ });
82
+ }
83
+ }
84
+ }
85
+ const objectives = [];
86
+ for (const stableId of [...new Set(inspected.map((candidate) => candidate.stableId))].sort()) {
87
+ const candidates = inspected.filter((candidate) => candidate.stableId === stableId);
88
+ const ambiguous = new Set(candidates.map((candidate) => candidate.canonicalJson)).size > 1;
89
+ if (ambiguous) {
90
+ diagnostics.push({
91
+ class: "ambiguous",
92
+ message: `objective identity ${stableId} resolves to conflicting documents`,
93
+ stable_id: stableId,
94
+ candidate_paths: candidates.map((candidate) => candidate.path).sort(),
95
+ });
96
+ }
97
+ objectives.push({
98
+ stableId,
99
+ persisted: candidates.every((candidate) => candidate.persisted),
100
+ canonicalJson: candidates[0].canonicalJson,
101
+ compatibility: candidates.every((candidate) => candidate.persisted) ? "canonical" : "legacy_derived_identity",
102
+ paths: candidates.map((candidate) => candidate.path).sort(),
103
+ slugs: [...new Set(candidates.map((candidate) => candidate.slug))].sort(),
104
+ roots: [...new Set(candidates.map((candidate) => candidate.root))].sort(),
105
+ ambiguous,
106
+ });
107
+ }
108
+ for (const slug of [...new Set(inspected.map((candidate) => candidate.slug))].sort()) {
109
+ const candidates = inspected.filter((candidate) => candidate.slug === slug);
110
+ const roots = new Set(candidates.map((candidate) => candidate.root));
111
+ const candidateIds = [...new Set(candidates.map((candidate) => candidate.stableId))].sort();
112
+ if (roots.size === 2 && candidateIds.length > 1) {
113
+ const candidatePaths = candidates.map((candidate) => candidate.path).sort();
114
+ diagnostics.push({
115
+ class: "ambiguous",
116
+ message: `canonical and legacy objective roots conflict for slug ${slug}`,
117
+ slug,
118
+ candidate_ids: candidateIds,
119
+ candidate_paths: candidatePaths,
120
+ });
121
+ for (const objective of objectives.filter((candidate) => candidateIds.includes(candidate.stableId)))
122
+ objective.ambiguous = true;
123
+ }
124
+ }
125
+ return { objectives, diagnostics };
126
+ }
127
+ export function inspectExperimentIdentities(objectiveId, document) {
128
+ if (!OBJECTIVE_ID.test(objectiveId))
129
+ throw new Error("objective identity is invalid");
130
+ const collections = [
131
+ ["experiments", list(document.experiments)],
132
+ ["archive", list(document.archive)],
133
+ ["archived_experiments", list(document.archived_experiments)],
134
+ ];
135
+ const counts = new Map();
136
+ for (const [, entries] of collections) {
137
+ for (const entry of entries) {
138
+ if (Number.isInteger(entry.number) && Number(entry.number) >= 0)
139
+ counts.set(Number(entry.number), (counts.get(Number(entry.number)) ?? 0) + 1);
140
+ }
141
+ }
142
+ const caveats = [];
143
+ const entries = collections.flatMap(([collection, values]) => values.map((data, index) => {
144
+ const number = Number.isInteger(data.number) && Number(data.number) >= 0 ? Number(data.number) : null;
145
+ let compatibility = "canonical";
146
+ const entryCaveats = [];
147
+ if (data.number === undefined || data.number === null) {
148
+ compatibility = "legacy_missing_identity";
149
+ entryCaveats.push(`legacy ${collection}[${index}] is missing experiment number and remains visible but unaddressable`);
150
+ }
151
+ else if (number === null) {
152
+ compatibility = "legacy_invalid_identity";
153
+ entryCaveats.push(`legacy ${collection}[${index}] has invalid experiment number and remains visible but unaddressable`);
154
+ }
155
+ else if ((counts.get(number) ?? 0) > 1) {
156
+ compatibility = "legacy_duplicate_identity";
157
+ entryCaveats.push(`duplicate experiment number ${number} remains visible but ambiguous and unaddressable`);
158
+ }
159
+ caveats.push(...entryCaveats);
160
+ return {
161
+ stableId: number === null ? null : `${objectiveId}/experiment:${number}`,
162
+ number,
163
+ addressable: compatibility === "canonical",
164
+ compatibility,
165
+ provenance: { collection, index },
166
+ data,
167
+ caveats: entryCaveats,
168
+ };
169
+ }));
170
+ return { objectiveId, entries, caveats: [...new Set(caveats)] };
171
+ }
172
+ export class ExperimentIdentityError extends Error {
173
+ className;
174
+ details;
175
+ constructor(className, message, details = {}) {
176
+ super(message);
177
+ this.className = className;
178
+ this.details = details;
179
+ this.name = "ExperimentIdentityError";
180
+ }
181
+ }
182
+ /** Pure preflight used by later publication work; this function never changes project state. */
183
+ export function validateExperimentPublicationIdentity(discovery, projection, objectiveId, number) {
184
+ if (!OBJECTIVE_ID.test(objectiveId) || !Number.isInteger(number) || number < 0) {
185
+ throw new ExperimentIdentityError("invalid_request", "publication requires a valid objective identity and non-negative integer experiment number");
186
+ }
187
+ const objective = discovery.objectives.find((candidate) => candidate.stableId === objectiveId);
188
+ if (!objective)
189
+ throw new ExperimentIdentityError("not_found", `objective identity ${objectiveId} was not found`);
190
+ if (objective.ambiguous || projection.objectiveId !== objectiveId || projection.entries.some((entry) => !entry.addressable)) {
191
+ throw new ExperimentIdentityError("ambiguous", "experiment publication identity is ambiguous; no effects are allowed", {
192
+ stable_id: `${objectiveId}/experiment:${number}`,
193
+ candidate_paths: objective.paths,
194
+ compatibility_caveats: projection.caveats,
195
+ });
196
+ }
197
+ const stableId = `${objectiveId}/experiment:${number}`;
198
+ if (projection.entries.some((entry) => entry.stableId === stableId)) {
199
+ throw new ExperimentIdentityError("ambiguous", `experiment identity ${stableId} already exists; no effects are allowed`, { stable_id: stableId });
200
+ }
201
+ return stableId;
202
+ }
@@ -0,0 +1,262 @@
1
+ import path from "node:path";
2
+ import { resolveSourceRoot } from "../core/sourceRoot.js";
3
+ import { canonicalRecordJson } from "./archiveDiscovery.js";
4
+ import { StateRetrievalFailure } from "./directRetrieval.js";
5
+ import { allocateAndPublishEntity, allocateEntityId, assertEntityDiscoveryOrigin, discoverEntities, publishEntity, } from "./entityStorage.js";
6
+ import { healthEntityViolations } from "./healthEntityValidation.js";
7
+ import { detailMetadata, detailProvenance, isSummaryEntity } from "./summaryEntityRead.js";
8
+ import { decodeListCursor, encodeListCursor, projectedListSnapshot } from "./listCursor.js";
9
+ import { localDate } from "./write/assign.js";
10
+ import { loadStateStorageAuthority } from "./stateStorageAuthority.js";
11
+ import { entityListSelectorFlags, entityListSelectorKey, projectEntityList, resolveEntityListSelector } from "./entityListProjection.js";
12
+ import { shellQuoteArgument } from "../core/shell.js";
13
+ const ARTIFACT = "health";
14
+ const BOUNDARY = "health_audit";
15
+ const SUMMARY = "health_summary";
16
+ const ORDER = "appended_at_desc_then_id_asc_then_legacy_date_desc_then_id_asc";
17
+ const ID_PATTERN = /^[a-z]{10}$/;
18
+ function mapping(value) {
19
+ return value !== null && typeof value === "object" && !Array.isArray(value);
20
+ }
21
+ function positive(value, field) {
22
+ const result = Number(value);
23
+ if (!Number.isSafeInteger(result) || result < 1)
24
+ throw new Error(`invalid health entity ${field} authority`);
25
+ return result;
26
+ }
27
+ function contract(sourceRoot = resolveSourceRoot()) {
28
+ const { authorityPath, document: authority } = loadStateStorageAuthority(sourceRoot);
29
+ const target = mapping(authority.entity_target) ? authority.entity_target : {};
30
+ const storage = mapping(target.storage_boundary) && mapping(target.storage_boundary.shared_primitives)
31
+ ? target.storage_boundary.shared_primitives
32
+ : {};
33
+ const entities = Array.isArray(target.entities) ? target.entities : [];
34
+ const health = entities.find((value) => mapping(value) && value.boundary === BOUNDARY && value.artifact === ARTIFACT);
35
+ const retrieval = mapping(health) && mapping(health.retrieval) ? health.retrieval : {};
36
+ if (typeof storage.canonical_root !== "string")
37
+ throw new Error(`invalid health entity contract in '${authorityPath}'`);
38
+ return {
39
+ authorityPath,
40
+ entityRoot: storage.canonical_root,
41
+ defaultLimit: positive(retrieval.default_limit, "default_limit"),
42
+ maximumLimit: positive(retrieval.maximum_limit, "maximum_limit"),
43
+ maxUtf8Bytes: positive(retrieval.max_utf8_bytes, "max_utf8_bytes"),
44
+ };
45
+ }
46
+ function failure(kind, message, recovery, id, exitCode = 1) {
47
+ return new StateRetrievalFailure({
48
+ schemaVersion: "agentera.stateFailure.v1",
49
+ status: "fail",
50
+ error: {
51
+ class: kind,
52
+ message,
53
+ syntax: "agentera state health get --id ID --format json",
54
+ example: `agentera state health get --id ${id ?? "qjtrmnpvka"} --format json`,
55
+ recovery,
56
+ artifact: ARTIFACT,
57
+ ...(id ? { id } : {}),
58
+ },
59
+ }, exitCode);
60
+ }
61
+ function listFailure(kind, message, recovery, exitCode = 1) {
62
+ const result = failure(kind, message, recovery, undefined, exitCode);
63
+ result.body.error.syntax = "agentera state health list [--limit N] [--cursor TOKEN] --format json";
64
+ result.body.error.example = "agentera state health list --limit 20 --format json";
65
+ return result;
66
+ }
67
+ function healthRecord(values) {
68
+ for (const field of ["id", "artifact", "number", "stable_id", "artifact_id", "entry_number", "appended_at"]) {
69
+ if (field in values)
70
+ throw new Error(`health entity record forbids identity field '${field}'`);
71
+ }
72
+ const record = structuredClone(values);
73
+ if (typeof record.date !== "string")
74
+ record.date = localDate();
75
+ record.appended_at = new Date().toISOString();
76
+ const violations = healthEntityViolations(record);
77
+ if (violations.length)
78
+ throw new Error(`health entity violates the canonical audit schema: ${violations.join("; ")}`);
79
+ return record;
80
+ }
81
+ function envelope(published, record, dryRun) {
82
+ return {
83
+ schemaVersion: "agentera.stateWrite.v1",
84
+ command: "state health append",
85
+ status: "pass",
86
+ path: published.path,
87
+ id: published.id,
88
+ artifact: published.artifact,
89
+ record,
90
+ operation: { verb: "append", dry_run: dryRun, idempotent_replay: published.replay },
91
+ validation: { status: "pass", violations: [] },
92
+ };
93
+ }
94
+ function withoutAppendTimestamp(record) {
95
+ const copy = structuredClone(record);
96
+ delete copy.appended_at;
97
+ return copy;
98
+ }
99
+ function existingReplay(root, sourceRoot, id, record) {
100
+ const existing = discoverEntities(root, sourceRoot).entities.find((entity) => entity.id === id
101
+ && entity.artifact === ARTIFACT
102
+ && entity.boundary === BOUNDARY
103
+ && entity.classification === "valid"
104
+ && entity.record !== null
105
+ && healthEntityViolations(entity.record).length === 0);
106
+ if (!existing || canonicalRecordJson(withoutAppendTimestamp(existing.record)) !== canonicalRecordJson(withoutAppendTimestamp(record)))
107
+ return null;
108
+ return existing;
109
+ }
110
+ export function appendHealthEntity(req, options = {}) {
111
+ const sourceRoot = options.sourceRoot ?? resolveSourceRoot();
112
+ const declared = contract(sourceRoot);
113
+ const record = healthRecord(req.input ?? req.values);
114
+ if (req.dryRun) {
115
+ options.publicationContext?.assertValid();
116
+ const id = options.id ?? allocateEntityId(options.publicationContext?.pinnedPath() ?? req.projectRoot, options.candidate, sourceRoot);
117
+ options.publicationContext?.assertValid();
118
+ return envelope({ path: path.join(path.resolve(req.projectRoot), declared.entityRoot, ARTIFACT, BOUNDARY, `${id}.yaml`), id, artifact: ARTIFACT, replay: false }, record, true);
119
+ }
120
+ if (options.id) {
121
+ const replay = existingReplay(req.projectRoot, sourceRoot, options.id, record);
122
+ if (replay)
123
+ return envelope({ path: replay.path, id: options.id, artifact: ARTIFACT, replay: true }, replay.record, false);
124
+ }
125
+ const published = options.id
126
+ ? publishEntity({ projectRoot: req.projectRoot, sourceRoot, publicationContext: options.publicationContext, artifact: ARTIFACT, boundary: BOUNDARY, id: options.id, record })
127
+ : allocateAndPublishEntity({ projectRoot: req.projectRoot, sourceRoot, publicationContext: options.publicationContext, artifact: ARTIFACT, boundary: BOUNDARY, record }, options.candidate);
128
+ return envelope(published, record, false);
129
+ }
130
+ function relative(root, candidate) {
131
+ return path.relative(path.resolve(root), candidate).split(path.sep).join("/");
132
+ }
133
+ function healthEntities(root, sourceRoot, supplied) {
134
+ if (supplied)
135
+ assertEntityDiscoveryOrigin(root, sourceRoot, supplied);
136
+ const discovery = supplied ?? discoverEntities(root, sourceRoot);
137
+ const relevant = discovery.entities.filter((entity) => entity.artifact === ARTIFACT || entity.boundary === BOUNDARY);
138
+ const bad = relevant.find((entity) => entity.classification !== "valid" || !entity.id || !entity.record || (entity.boundary === BOUNDARY && healthEntityViolations(entity.record).length > 0));
139
+ if (bad)
140
+ throw listFailure(bad.classification === "duplicate" ? "ambiguous" : "corrupt", `health entity '${bad.relativePath}' is not canonical`, "Run agentera check validate state, preserve conflicting evidence, and repair the canonical entity files.");
141
+ return relevant;
142
+ }
143
+ function entry(root, entity) {
144
+ const id = entity.id;
145
+ return {
146
+ id,
147
+ artifact: ARTIFACT,
148
+ record: entity.record,
149
+ ...detailMetadata(entity),
150
+ provenance: detailProvenance(relative(root, entity.path), entity),
151
+ retrieval: { get: `agentera state health get --id ${id} --format json` },
152
+ };
153
+ }
154
+ export function getHealthEntity(root, id, sourceRoot = resolveSourceRoot()) {
155
+ if (!ID_PATTERN.test(id))
156
+ throw failure("invalid_request", `health ID '${id}' must be ten lowercase letters`, "Use a bare ID returned by health append or list.", id, 2);
157
+ const all = discoverEntities(root, sourceRoot).entities;
158
+ const matches = all.filter((entity) => entity.id === id);
159
+ if (matches.length > 1 || matches.some((entity) => entity.classification === "duplicate"))
160
+ throw failure("ambiguous", `health entity ID '${id}' has multiple canonical candidates`, "Run agentera check validate state, preserve conflicting evidence, and assign unique IDs.", id);
161
+ const selected = matches[0];
162
+ const expectedPath = path.join(path.resolve(root), contract(sourceRoot).entityRoot, ARTIFACT, selected?.boundary === SUMMARY ? SUMMARY : BOUNDARY, `${id}.yaml`);
163
+ const canonical = all.find((entity) => entity.path === expectedPath);
164
+ if (!selected && canonical)
165
+ throw failure("corrupt", `health entity '${canonical.relativePath}' does not match its canonical ID envelope`, "Run agentera check validate state and repair the canonical entity file.", id);
166
+ if (!selected || selected.artifact !== ARTIFACT || ![BOUNDARY, SUMMARY].includes(selected.boundary ?? ""))
167
+ throw failure("not_found", `no health entity exists with ID '${id}'`, "Copy an ID from agentera state health list --format json and retry.", id);
168
+ if (selected.classification !== "valid" || !selected.record || (selected.boundary === BOUNDARY && healthEntityViolations(selected.record).length > 0))
169
+ throw failure("corrupt", `health entity '${selected.relativePath}' is corrupt or violates the audit contract`, "Run agentera check validate state, preserve its evidence, and repair the canonical entity file.", id);
170
+ return {
171
+ schemaVersion: "agentera.stateRetrieval.v1",
172
+ command: "state health get",
173
+ status: isSummaryEntity(selected) ? "degraded" : "ok",
174
+ entry: entry(root, selected),
175
+ source: { artifact: ARTIFACT, authority: "canonical_entity_file" },
176
+ source_contract: { authority: "references/artifacts/state-storage-authority.yaml", detail: isSummaryEntity(selected) ? "summary" : "full" },
177
+ };
178
+ }
179
+ function appendTimestamp(entity) {
180
+ return typeof entity.record?.appended_at === "string" ? entity.record.appended_at : null;
181
+ }
182
+ function compareHealthEntities(left, right) {
183
+ const leftTimestamp = appendTimestamp(left);
184
+ const rightTimestamp = appendTimestamp(right);
185
+ if (leftTimestamp !== null && rightTimestamp !== null) {
186
+ return rightTimestamp.localeCompare(leftTimestamp) || left.id.localeCompare(right.id);
187
+ }
188
+ if (leftTimestamp !== null)
189
+ return -1;
190
+ if (rightTimestamp !== null)
191
+ return 1;
192
+ return String(right.record.date ?? "").localeCompare(String(left.record.date ?? "")) || left.id.localeCompare(right.id);
193
+ }
194
+ function key(entity) {
195
+ return `${appendTimestamp(entity) ?? String(entity.record.date ?? "")}\0${entity.id}`;
196
+ }
197
+ function snapshot(root, entities, dimension, entityRoot) {
198
+ return projectedListSnapshot({
199
+ schemaVersion: "agentera.stateList.v1",
200
+ command: "state health list",
201
+ order: ORDER,
202
+ filters: { dimension: dimension ?? null },
203
+ source: { artifact: ARTIFACT, authority: "canonical_entity_files", root: entityRoot },
204
+ source_contract: { authority: "references/artifacts/state-storage-authority.yaml", detail: entities.some(isSummaryEntity) ? "mixed" : "full", cursor: "opaque_snapshot_cursor" },
205
+ entries: entities.map((entity) => entry(root, entity)),
206
+ });
207
+ }
208
+ function encode(payload, root, authorityPath) {
209
+ return encodeListCursor(payload, root, authorityPath);
210
+ }
211
+ function decode(token, root, authorityPath) {
212
+ try {
213
+ return decodeListCursor(token, root, authorityPath);
214
+ }
215
+ catch {
216
+ throw listFailure("cursor_invalid", "health cursor is malformed or belongs to another project", "Copy next_cursor exactly, or omit --cursor to restart.");
217
+ }
218
+ }
219
+ export function listHealthEntities(root, limit, dimension, cursor, options = {}) {
220
+ const sourceRoot = options.sourceRoot ?? resolveSourceRoot();
221
+ const declared = contract(sourceRoot);
222
+ const effectiveLimit = limit ?? declared.defaultLimit;
223
+ if (!Number.isSafeInteger(effectiveLimit) || effectiveLimit < 1 || effectiveLimit > declared.maximumLimit)
224
+ throw listFailure("invalid_request", `health list limit must be 1..${declared.maximumLimit}`, "Use a limit in the declared range.", 2);
225
+ const all = healthEntities(root, sourceRoot, options.discovery);
226
+ let filtered = [...all].sort(compareHealthEntities);
227
+ if (dimension) {
228
+ const needle = dimension.toLowerCase();
229
+ filtered = filtered.filter((entity) => canonicalRecordJson(entity.record).toLowerCase().includes(needle));
230
+ }
231
+ const format = options.format ?? "json";
232
+ const projectionOptions = { family: "health", artifact: ARTIFACT, boundary: BOUNDARY, format, maxUtf8Bytes: declared.maxUtf8Bytes, selector: options.selector };
233
+ const selector = resolveEntityListSelector(options.selector, filtered.map((entity) => entry(root, entity)), projectionOptions);
234
+ const selectorKey = entityListSelectorKey(selector);
235
+ const snap = snapshot(root, filtered, dimension, declared.entityRoot);
236
+ let start = 0;
237
+ if (cursor) {
238
+ const value = decode(cursor, root, declared.authorityPath);
239
+ if (value.selector !== selectorKey)
240
+ throw listFailure("cursor_invalid", "health cursor selectors do not match this request", "Repeat the original selector, or omit --cursor to restart from the current snapshot.");
241
+ if (value.version !== 1 || value.artifact !== ARTIFACT || value.order !== ORDER || value.snapshot_id !== snap || value.dimension !== (dimension ?? null))
242
+ throw listFailure("cursor_snapshot_unavailable", "health state or filters changed after this cursor snapshot", "Repeat the original filters, or omit --cursor to restart from the current snapshot.");
243
+ const found = filtered.findIndex((entity) => key(entity) === value.after_key);
244
+ if (found < 0)
245
+ throw listFailure("cursor_snapshot_unavailable", "health cursor continuation is unavailable", "Omit --cursor to restart.");
246
+ start = found + 1;
247
+ }
248
+ const selected = filtered.slice(start, start + effectiveLimit);
249
+ const response = () => {
250
+ const remaining = filtered.length - start - selected.length;
251
+ const next = remaining && selected.length ? encode({ version: 1, artifact: ARTIFACT, order: ORDER, snapshot_id: snap, selector: selectorKey, dimension: dimension ?? null, after_key: key(selected.at(-1)) }, root, declared.authorityPath) : undefined;
252
+ const selectorFlags = entityListSelectorFlags(selector);
253
+ return {
254
+ schemaVersion: "agentera.stateList.v1", command: "state health list", status: remaining || filtered.some(isSummaryEntity) ? "degraded" : "ok", entries: selected.map((entity) => entry(root, entity)),
255
+ counts: { total: filtered.length, returned: selected.length, remaining }, filters: { dimension: dimension ?? null }, snapshot: { id: snap, first_page: !cursor, order: ORDER, has_more: Boolean(remaining), candidate_count: filtered.length },
256
+ source: { artifact: ARTIFACT, authority: "canonical_entity_files", root: declared.entityRoot }, source_contract: { authority: "references/artifacts/state-storage-authority.yaml", detail: filtered.some(isSummaryEntity) ? "mixed" : "full", cursor: "opaque_snapshot_cursor" },
257
+ retrieval: { ...(next ? { continue: `agentera state health list${dimension ? ` --dimension ${shellQuoteArgument(dimension)}` : ""}${selectorFlags} --limit ${effectiveLimit} --cursor ${next} --format json` } : {}) },
258
+ ...(remaining ? { omitted: true, omitted_count: remaining, omission_reason: "page_limit", next_cursor: next } : {}),
259
+ };
260
+ };
261
+ return projectEntityList(response(), selector, projectionOptions);
262
+ }
@@ -0,0 +1,18 @@
1
+ import { dumpYamlMapping } from "../core/yaml.js";
2
+ import { validateArtifactBytes } from "./write/validate.js";
3
+ export function healthEntityViolations(record) {
4
+ const violations = validateArtifactBytes("health", dumpYamlMapping({ audits: [{ number: 1, ...record }] }));
5
+ if (record.appended_at !== undefined) {
6
+ const value = record.appended_at;
7
+ let canonical = false;
8
+ if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(value)) {
9
+ try {
10
+ canonical = new Date(value).toISOString() === value;
11
+ }
12
+ catch { /* invalid timestamp */ }
13
+ }
14
+ if (!canonical)
15
+ violations.push("appended_at must be a canonical UTC ISO-8601 timestamp");
16
+ }
17
+ return violations;
18
+ }
@@ -0,0 +1,69 @@
1
+ import YAML from "yaml";
2
+ function healthIdentityNumber(value) {
3
+ if (value && typeof value === "object" && !Array.isArray(value)) {
4
+ const mapping = value;
5
+ const number = mapping.number;
6
+ if (Number.isSafeInteger(number) && Number(number) > 0)
7
+ return Number(number);
8
+ return healthIdentityNumber(mapping.summary);
9
+ }
10
+ if (typeof value !== "string")
11
+ return null;
12
+ const match = /^Audit\s+([1-9][0-9]*)\b/.exec(value.trim());
13
+ return match ? Number(match[1]) : null;
14
+ }
15
+ export function repairHealthDuplicates(doc, number, keep) {
16
+ const candidate = structuredClone(doc);
17
+ let removed = 0;
18
+ for (const key of ["audits", "archive"]) {
19
+ const values = Array.isArray(candidate[key]) ? candidate[key] : [];
20
+ const matches = values.flatMap((value, index) => (healthIdentityNumber(value) === number ? [index] : []));
21
+ if (matches.length < 2)
22
+ continue;
23
+ const retained = keep === "last" ? matches.at(-1) : matches[0];
24
+ candidate[key] = values.filter((_, index) => !matches.includes(index) || index === retained);
25
+ removed += matches.length - 1;
26
+ }
27
+ return { candidate, removed };
28
+ }
29
+ export function repairHealthProjectionBytes(bytes, number, keep) {
30
+ const document = YAML.parseDocument(bytes);
31
+ const removals = [];
32
+ let removed = 0;
33
+ for (const key of ["audits", "archive"]) {
34
+ const sequence = document.get(key, true);
35
+ const items = sequence?.items ?? [];
36
+ const sequenceEnd = sequence?.range?.[1];
37
+ const matches = items.flatMap((item, index) => {
38
+ const value = typeof item?.toJSON === "function"
39
+ ? item.toJSON()
40
+ : item;
41
+ return healthIdentityNumber(value) === number ? [index] : [];
42
+ });
43
+ if (matches.length < 2)
44
+ continue;
45
+ const retained = keep === "last" ? matches.at(-1) : matches[0];
46
+ for (const index of matches) {
47
+ if (index === retained)
48
+ continue;
49
+ const range = items[index]?.range;
50
+ if (!range || range.length < 2)
51
+ throw new Error(`health ${key} row ${index} has no stable YAML range`);
52
+ const lineStart = bytes.lastIndexOf("\n", range[0] - 1) + 1;
53
+ const nextRange = items[index + 1]?.range;
54
+ const rawEnd = nextRange?.[0] ?? sequenceEnd ?? bytes.length;
55
+ const lineEnd = nextRange
56
+ ? bytes.lastIndexOf("\n", rawEnd - 1) + 1
57
+ : (() => {
58
+ const newline = bytes.indexOf("\n", Math.max(range[1], rawEnd));
59
+ return newline < 0 ? bytes.length : newline + 1;
60
+ })();
61
+ removals.push([lineStart, Math.max(lineStart, lineEnd)]);
62
+ removed += 1;
63
+ }
64
+ }
65
+ for (const [start, end] of removals.sort((left, right) => right[0] - left[0])) {
66
+ bytes = bytes.slice(0, start) + bytes.slice(end);
67
+ }
68
+ return { bytes, removed };
69
+ }