@smartmemory/compose 0.3.7 → 0.4.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 (217) hide show
  1. package/.claude/skills/compose/SKILL.md +12 -3
  2. package/.compose-deps.json +51 -25
  3. package/README.md +79 -7
  4. package/bin/compose.js +495 -360
  5. package/bin/judgment-migrate.js +387 -0
  6. package/contracts/comp-obs-contract.schema.json +9 -3
  7. package/contracts/fluid-record.schema.json +209 -0
  8. package/contracts/lifecycle-backfill.schema.json +322 -0
  9. package/dist/assets/App-Z4MU-H_F.js +916 -0
  10. package/dist/assets/{_baseUniq-Bo837sRJ.js → _baseUniq-ClWoCPFl.js} +1 -1
  11. package/dist/assets/{arc-BafGpyqE.js → arc-DY26UIVo.js} +1 -1
  12. package/dist/assets/{architectureDiagram-Q4EWVU46-BOBfUsqL.js → architectureDiagram-Q4EWVU46-6Ggq4DqJ.js} +1 -1
  13. package/dist/assets/{blockDiagram-DXYQGD6D-Dwodev1a.js → blockDiagram-DXYQGD6D-CH3Ked0l.js} +1 -1
  14. package/dist/assets/{browser-1ntj1-x_.js → browser-BWkrenen.js} +1 -1
  15. package/dist/assets/{c4Diagram-AHTNJAMY-CU_bhYag.js → c4Diagram-AHTNJAMY-Bk8dYilu.js} +1 -1
  16. package/dist/assets/channel-SnZzzh7k.js +1 -0
  17. package/dist/assets/{chunk-4BX2VUAB-p8WsDwnO.js → chunk-4BX2VUAB-BMR0XaAQ.js} +1 -1
  18. package/dist/assets/{chunk-4TB4RGXK-B8h7-eR0.js → chunk-4TB4RGXK-JytR14a9.js} +1 -1
  19. package/dist/assets/{chunk-55IACEB6-DxeEr98s.js → chunk-55IACEB6-B4Q97BCP.js} +1 -1
  20. package/dist/assets/{chunk-EDXVE4YY-BYt8F151.js → chunk-EDXVE4YY-R_qarkSf.js} +1 -1
  21. package/dist/assets/{chunk-FMBD7UC4-DGSOVeie.js → chunk-FMBD7UC4-C9s7KR9m.js} +1 -1
  22. package/dist/assets/{chunk-OYMX7WX6-B-QdgYR2.js → chunk-OYMX7WX6-BySQzVxc.js} +1 -1
  23. package/dist/assets/{chunk-QZHKN3VN-Du5UAZLs.js → chunk-QZHKN3VN-DdpSYZsW.js} +1 -1
  24. package/dist/assets/{chunk-YZCP3GAM-C8JbNBSk.js → chunk-YZCP3GAM-iE_tzriw.js} +1 -1
  25. package/dist/assets/classDiagram-6PBFFD2Q-CBu92dSH.js +1 -0
  26. package/dist/assets/classDiagram-v2-HSJHXN6E-CBu92dSH.js +1 -0
  27. package/dist/assets/clone-DgklGjHm.js +1 -0
  28. package/dist/assets/{cose-bilkent-S5V4N54A-O1ESaqge.js → cose-bilkent-S5V4N54A-BdlU6ZX_.js} +1 -1
  29. package/dist/assets/{dagre-KV5264BT-CPTmFPHw.js → dagre-KV5264BT-Cp3F5KTn.js} +1 -1
  30. package/dist/assets/{diagram-5BDNPKRD-B3PNrWs5.js → diagram-5BDNPKRD-DiR6_2q_.js} +1 -1
  31. package/dist/assets/{diagram-G4DWMVQ6-Cscfr6vc.js → diagram-G4DWMVQ6-w0i-p5HX.js} +1 -1
  32. package/dist/assets/{diagram-MMDJMWI5-CSfqZ-TM.js → diagram-MMDJMWI5-tIHhwUv3.js} +1 -1
  33. package/dist/assets/{diagram-TYMM5635-Cg4aYS7W.js → diagram-TYMM5635-BAeY3B19.js} +1 -1
  34. package/dist/assets/{erDiagram-SMLLAGMA-_ZqwG5pl.js → erDiagram-SMLLAGMA-Ckx_Knko.js} +1 -1
  35. package/dist/assets/{flowDiagram-DWJPFMVM-C83boxFT.js → flowDiagram-DWJPFMVM-DeoNka6J.js} +1 -1
  36. package/dist/assets/{ganttDiagram-T4ZO3ILL-CWnIjuEi.js → ganttDiagram-T4ZO3ILL-BmGnFbEg.js} +1 -1
  37. package/dist/assets/{gitGraphDiagram-UUTBAWPF-DrMdxZfH.js → gitGraphDiagram-UUTBAWPF-Dk48IHsx.js} +1 -1
  38. package/dist/assets/{graph-RE4I7Ty7.js → graph-BNzKGvoy.js} +1 -1
  39. package/dist/assets/{graph-Bi99_6Yf.js → graph-CI_1htl0.js} +1 -1
  40. package/dist/assets/{index-Rm2RE-c0.js → index-BEfrNBp8.js} +3 -3
  41. package/dist/assets/index-yyrA5OZd.css +1 -0
  42. package/dist/assets/{infoDiagram-42DDH7IO-BLmP4Epr.js → infoDiagram-42DDH7IO-BRf827i0.js} +1 -1
  43. package/dist/assets/{ishikawaDiagram-UXIWVN3A-yuWWshKN.js → ishikawaDiagram-UXIWVN3A-0kCZaeCM.js} +1 -1
  44. package/dist/assets/{journeyDiagram-VCZTEJTY-BOfhaJov.js → journeyDiagram-VCZTEJTY-rvU7ayRt.js} +1 -1
  45. package/dist/assets/{kanban-definition-6JOO6SKY-Bbolde15.js → kanban-definition-6JOO6SKY-DpQwX1C5.js} +1 -1
  46. package/dist/assets/{layout-BSf33zm8.js → layout-BI8cXFPI.js} +1 -1
  47. package/dist/assets/{linear-AvSTWMqx.js → linear-a0glcDiw.js} +1 -1
  48. package/dist/assets/{min-QBM8H4xN.js → min-vPHfnXcC.js} +1 -1
  49. package/dist/assets/{mindmap-definition-QFDTVHPH-BuvgtqIc.js → mindmap-definition-QFDTVHPH-D14eF-7C.js} +1 -1
  50. package/dist/assets/mobile-B7m9EO9D.js +17 -0
  51. package/dist/assets/{pieDiagram-DEJITSTG-DIzF16vh.js → pieDiagram-DEJITSTG-Cno-gETh.js} +1 -1
  52. package/dist/assets/{quadrantDiagram-34T5L4WZ-D-mbUIjS.js → quadrantDiagram-34T5L4WZ-BUQM1Hfm.js} +1 -1
  53. package/dist/assets/{requirementDiagram-MS252O5E-CEs4kCLd.js → requirementDiagram-MS252O5E-pOXlN2-q.js} +1 -1
  54. package/dist/assets/{sankeyDiagram-XADWPNL6-DFsnCr9n.js → sankeyDiagram-XADWPNL6-Crynd3_b.js} +1 -1
  55. package/dist/assets/{sequenceDiagram-FGHM5R23-BEJYdTjQ.js → sequenceDiagram-FGHM5R23-D9fZdCM8.js} +1 -1
  56. package/dist/assets/{stateDiagram-FHFEXIEX-BBXs57uY.js → stateDiagram-FHFEXIEX-CW9qVec8.js} +1 -1
  57. package/dist/assets/stateDiagram-v2-QKLJ7IA2-DkVLzHbY.js +1 -0
  58. package/dist/assets/{timeline-definition-GMOUNBTQ-BGvLoVAY.js → timeline-definition-GMOUNBTQ-BcHzhm_8.js} +1 -1
  59. package/dist/assets/{vennDiagram-DHZGUBPP-9LaBTMe0.js → vennDiagram-DHZGUBPP-BfytJcWk.js} +1 -1
  60. package/dist/assets/{wardley-RL74JXVD-P4MEqMTP.js → wardley-RL74JXVD-DLj-IjyB.js} +1 -1
  61. package/dist/assets/{wardleyDiagram-NUSXRM2D-o-tmxnlC.js → wardleyDiagram-NUSXRM2D-Ds0Ue68c.js} +1 -1
  62. package/dist/assets/{xychartDiagram-5P7HB3ND-Dpn7V6qk.js → xychartDiagram-5P7HB3ND-vjWDXFL6.js} +1 -1
  63. package/dist/index.html +3 -3
  64. package/lib/agent-string.js +7 -5
  65. package/lib/append-integrity.js +81 -0
  66. package/lib/backfill-evidence.js +109 -0
  67. package/lib/bug-escalation.js +9 -0
  68. package/lib/build-stream-schema.js +3 -1
  69. package/lib/build-stream-writer.js +25 -0
  70. package/lib/build.js +874 -170
  71. package/lib/canon-guard.js +28 -6
  72. package/lib/canon-override.js +196 -0
  73. package/lib/canon-registry.js +104 -0
  74. package/lib/cli-commands.js +144 -0
  75. package/lib/codex-preflight.js +26 -13
  76. package/lib/colleague/context.js +215 -0
  77. package/lib/colleague/writeback.js +95 -0
  78. package/lib/completion-gate.js +1421 -0
  79. package/lib/completion-writer.js +47 -47
  80. package/lib/consumer-fanout.js +105 -11
  81. package/lib/coverage-gate.js +200 -0
  82. package/lib/deps.js +164 -7
  83. package/lib/dir-lock.js +170 -0
  84. package/lib/dispatch-ledger.js +3 -3
  85. package/lib/feature-json.js +1 -1
  86. package/lib/feature-reconciler.js +8 -0
  87. package/lib/feature-validator.js +64 -1
  88. package/lib/feature-writer.js +57 -2
  89. package/lib/fluid/factory.js +167 -0
  90. package/lib/fluid/ideabox-dates.js +73 -0
  91. package/lib/fluid/ideabox-migrate.js +154 -0
  92. package/lib/fluid/ideabox-ops.js +585 -0
  93. package/lib/fluid/ideabox-view.js +146 -0
  94. package/lib/fluid/import-ideabox.js +186 -0
  95. package/lib/fluid/local-provider.js +606 -0
  96. package/lib/fluid/provider.js +684 -0
  97. package/lib/fluid/record-shape.js +214 -0
  98. package/lib/fluid/record-store.js +328 -0
  99. package/lib/fluid/render-ideabox.js +261 -0
  100. package/lib/fluid/schema.js +40 -0
  101. package/lib/fluid/smartmemory-provider.js +1695 -0
  102. package/lib/gsd.js +63 -23
  103. package/lib/guard-cli.js +175 -0
  104. package/lib/guard-custody.js +141 -0
  105. package/lib/guard-descriptors.js +530 -0
  106. package/lib/guard-enrol.js +254 -0
  107. package/lib/health-score.js +1 -1
  108. package/lib/ideabox-cli.js +315 -0
  109. package/lib/ideabox.js +121 -21
  110. package/lib/judgment/store/index.js +9 -1
  111. package/lib/judgment/store/records.js +1 -1
  112. package/lib/judgment/trace.js +380 -0
  113. package/lib/judgment-decision-write.js +277 -0
  114. package/lib/judgment-decisions.js +466 -0
  115. package/lib/judgment-gen.js +5 -1
  116. package/lib/judgment-writer.js +56 -2
  117. package/lib/lifecycle-modes.js +4 -4
  118. package/lib/lineage.js +400 -0
  119. package/lib/local-claude-connector.js +52 -1
  120. package/lib/maya-client.js +302 -0
  121. package/lib/maya-config.js +53 -0
  122. package/lib/maya-identity.js +283 -0
  123. package/lib/migrate-anon.js +5 -0
  124. package/lib/migrate-roadmap.js +15 -0
  125. package/lib/new.js +13 -1
  126. package/lib/pipeline-compat.js +104 -0
  127. package/lib/policy-catalog.js +295 -0
  128. package/lib/policy-check.js +0 -0
  129. package/lib/process-termination.js +98 -0
  130. package/lib/resolve-workspace.js +5 -1
  131. package/lib/result-normalizer.js +396 -199
  132. package/lib/roadmap-errors.js +65 -0
  133. package/lib/roadmap-preservers.js +24 -4
  134. package/lib/roadmap-residue.js +299 -0
  135. package/lib/smartmemory-client.js +614 -78
  136. package/lib/smartmemory-config.js +54 -0
  137. package/lib/smartmemory-ingest.js +19 -2
  138. package/lib/step-prompt.js +7 -6
  139. package/lib/stratum-engine.js +53 -4
  140. package/lib/stratum-mcp-client.js +271 -36
  141. package/lib/test-bootstrap.js +31 -0
  142. package/lib/tool-inventory.js +122 -0
  143. package/lib/version-check.js +91 -19
  144. package/lib/vision-writer.js +88 -1
  145. package/package.json +7 -6
  146. package/pipelines/bug-fix.stratum.yaml +205 -211
  147. package/pipelines/build-quick.profiles.json +12 -0
  148. package/pipelines/build-quick.stratum.yaml +263 -350
  149. package/pipelines/content.stratum.yaml +81 -77
  150. package/pipelines/coverage-sweep.stratum.yaml +49 -30
  151. package/pipelines/plan.stratum.yaml +76 -86
  152. package/pipelines/refactor.stratum.yaml +125 -125
  153. package/pipelines/research.stratum.yaml +56 -58
  154. package/pipelines/review-fix.profiles.json +6 -0
  155. package/pipelines/review-fix.stratum.yaml +110 -83
  156. package/presets/team-feature.profiles.json +6 -0
  157. package/presets/team-feature.stratum.yaml +93 -66
  158. package/presets/team-research.profiles.json +6 -0
  159. package/presets/team-research.stratum.yaml +89 -80
  160. package/presets/team-review.profiles.json +8 -0
  161. package/presets/team-review.stratum.yaml +98 -80
  162. package/scripts/cost-census.mjs +70 -0
  163. package/scripts/guard-sign/compose-guard-sign.sh +62 -0
  164. package/server/agent-health.js +22 -0
  165. package/server/agent-hooks.js +14 -1
  166. package/server/agent-server.js +5 -248
  167. package/server/agent-spawn.js +3 -4
  168. package/server/agent-workspace.js +294 -0
  169. package/server/build-routes.js +6 -5
  170. package/server/build-stream-bridge.js +53 -0
  171. package/server/cc-session-watcher.js +4 -1
  172. package/server/coalescing-buffer.js +7 -1
  173. package/server/completion-projection.js +228 -0
  174. package/server/compose-mcp-tools.js +109 -23
  175. package/server/compose-mcp.js +88 -882
  176. package/server/decision-event-emit.js +41 -2
  177. package/server/decision-event-id.js +17 -0
  178. package/server/decision-events-snapshot.js +3 -0
  179. package/server/design-routes.js +14 -8
  180. package/server/feature-scan.js +76 -2
  181. package/server/file-watcher.js +170 -21
  182. package/server/ideabox-routes.js +166 -224
  183. package/server/index.js +70 -100
  184. package/server/lifecycle-guard.js +240 -10
  185. package/server/lifecycle-phase-history.js +276 -0
  186. package/server/maya-routes.js +507 -0
  187. package/server/mcp-tool-defs.js +940 -0
  188. package/server/mcp-tool-policy.js +34 -2
  189. package/server/model-tiers.js +22 -5
  190. package/server/pipeline-routes.js +21 -11
  191. package/server/project-root.js +58 -19
  192. package/server/remote-utils.js +3 -1
  193. package/server/schema-validator.js +7 -1
  194. package/server/session-manager.js +5 -6
  195. package/server/session-routes.js +3 -1
  196. package/server/stratum-client.js +57 -10
  197. package/server/stratum-sync.js +6 -3
  198. package/server/summarizer.js +3 -4
  199. package/server/supervisor.js +0 -1
  200. package/server/vision-routes.js +208 -98
  201. package/server/vision-server.js +86 -23
  202. package/server/vision-store.js +60 -6
  203. package/server/vision-utils.js +3 -4
  204. package/server/workspace-activity.js +18 -0
  205. package/server/workspace-middleware.js +2 -2
  206. package/server/workspace-runtime.js +243 -0
  207. package/server/worktree-gc.js +1 -0
  208. package/dist/assets/App-PkZzHeMj.js +0 -894
  209. package/dist/assets/channel-qVK_qn4E.js +0 -1
  210. package/dist/assets/classDiagram-6PBFFD2Q-B8UcfC1q.js +0 -1
  211. package/dist/assets/classDiagram-v2-HSJHXN6E-B8UcfC1q.js +0 -1
  212. package/dist/assets/clone-Pu3RyLUh.js +0 -1
  213. package/dist/assets/index-LIwREYgH.css +0 -1
  214. package/dist/assets/mobile-BnXEOE3U.js +0 -17
  215. package/dist/assets/stateDiagram-v2-QKLJ7IA2-BqKuX4rj.js +0 -1
  216. package/lib/staleness.js +0 -87
  217. package/server/ideabox-cache.js +0 -77
@@ -0,0 +1,322 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://forge.local/contracts/lifecycle-backfill.schema.json",
4
+ "title": "COMP-LIFECYCLE-BACKFILL — backfill request, occurrence, intent and batch record",
5
+ "version": "1.0.0",
6
+ "_source": "docs/features/COMP-LIFECYCLE-BACKFILL/design.md",
7
+ "_roadmap": "COMP-LIFECYCLE-BACKFILL",
8
+ "_changelog": {
9
+ "1.0.0": "Initial: BackfillRequest, BackfilledOccurrence, BackfillIntent, BackfillRecord."
10
+ },
11
+
12
+ "definitions": {
13
+
14
+ "EvidenceRef": {
15
+ "type": "object",
16
+ "required": ["kind", "ref"],
17
+ "properties": {
18
+ "kind": { "type": "string", "enum": ["commit", "path"] },
19
+ "ref": {
20
+ "type": "string",
21
+ "minLength": 1,
22
+ "description": "40-char lowercase hex SHA when kind=commit; repo-relative regular file path when kind=path."
23
+ }
24
+ },
25
+ "additionalProperties": false
26
+ },
27
+
28
+ "ResolvedEvidence": {
29
+ "description": "EvidenceRef after server resolution. verifiedAt and observedTime are server-written and MUST NOT be caller-supplied.",
30
+ "type": "object",
31
+ "required": ["kind", "ref", "verifiedAt", "observedTime", "observedEpochMs"],
32
+ "properties": {
33
+ "kind": { "type": "string", "enum": ["commit", "path"] },
34
+ "ref": { "type": "string", "minLength": 1 },
35
+ "verifiedAt": { "type": "string", "format": "date-time" },
36
+ "observedTime": {
37
+ "type": "string",
38
+ "format": "date-time",
39
+ "description": "Commit AUTHOR date (git show -s --format=%aI, which carries the committer's UTC OFFSET) or file mtime."
40
+ },
41
+ "observedEpochMs": {
42
+ "type": "integer",
43
+ "description": "BP-8: the epoch form, computed once at resolution. Every ordering and interval comparison uses THIS, never the string."
44
+ }
45
+ },
46
+ "additionalProperties": false
47
+ },
48
+
49
+ "BackfillRequest": {
50
+ "type": "object",
51
+ "required": ["feature_code", "commit_sha", "tests_pass", "files_changed", "reason", "occurrences"],
52
+ "properties": {
53
+ "feature_code": { "type": "string", "minLength": 1 },
54
+ "commit_sha": { "type": "string", "pattern": "^[0-9a-f]{40}$" },
55
+ "tests_pass": {
56
+ "type": "boolean",
57
+ "description": "Ignored when guard.testCommand is configured and exits 0; otherwise must be true. No silent default."
58
+ },
59
+ "files_changed": { "type": "array", "items": { "type": "string" } },
60
+ "reason": {
61
+ "type": "string",
62
+ "minLength": 1,
63
+ "description": "Mandatory prose. Why this work skipped the phase walk."
64
+ },
65
+ "notes": { "type": "string" },
66
+ "occurrences": {
67
+ "type": "array",
68
+ "minItems": 0,
69
+ "items": { "$ref": "#/definitions/RequestedOccurrence" }
70
+ }
71
+ },
72
+ "additionalProperties": false
73
+ },
74
+
75
+ "RequestedOccurrence": {
76
+ "type": "object",
77
+ "required": ["phase", "evidence"],
78
+ "properties": {
79
+ "phase": {
80
+ "type": "string",
81
+ "minLength": 1,
82
+ "description": "BP-9: MUST be a node of the item's mode graph. An unknown phase is refused, never accepted as out-of-graph."
83
+ },
84
+ "evidence": { "$ref": "#/definitions/EvidenceRef" }
85
+ },
86
+ "additionalProperties": false
87
+ },
88
+
89
+ "StoredHistoryEntry": {
90
+ "description": "R3-3: an entry that is ALREADY on disk in lifecycle.phaseHistory[]. appendPhaseHistory writes exactly eight fields and nothing else, and every live entry written before this feature — including the lifecycle genesis record — carries no origin, recordedAt, confidence, episode or evidence. The merge preserves those records byte-for-byte, so the stored-history schema MUST accept them. The backfill-only fields are optional here precisely so an entry this feature wrote validates under this branch too.",
91
+ "type": "object",
92
+ "required": ["phase", "step", "enteredAt", "exitedAt", "from", "to", "outcome", "timestamp"],
93
+ "properties": {
94
+ "phase": { "type": "string" },
95
+ "step": { "type": "string" },
96
+ "enteredAt": { "type": "string", "format": "date-time" },
97
+ "exitedAt": { "type": ["string", "null"], "format": "date-time" },
98
+ "from": { "type": ["string", "null"] },
99
+ "to": { "type": "string" },
100
+ "outcome": { "type": ["string", "null"] },
101
+ "timestamp": { "type": "string", "format": "date-time" },
102
+ "recordedAt": { "type": "string", "format": "date-time" },
103
+ "origin": { "type": "string", "enum": ["live", "backfill"] },
104
+ "confidence": { "type": "number", "minimum": 0, "maximum": 1 },
105
+ "episode": { "type": "integer", "minimum": 1 },
106
+ "operation_id": { "type": "string", "format": "uuid" },
107
+ "evidence": { "$ref": "#/definitions/ResolvedEvidence" }
108
+ },
109
+ "additionalProperties": false
110
+ },
111
+
112
+ "BackfilledOccurrence": {
113
+ "description": "BP-9: the FULL entry this feature CONSTRUCTS and appends to lifecycle.phaseHistory[]. R3-3: this is the constructor's contract, not the store's — it governs `occurrences` and `terminal_occurrence`, which the gate builds field by field in §5.5, and never a record read off disk.",
114
+ "type": "object",
115
+ "required": ["phase", "step", "enteredAt", "exitedAt", "from", "to", "outcome", "timestamp", "recordedAt", "origin", "confidence", "evidence", "episode"],
116
+ "properties": {
117
+ "phase": { "type": "string" },
118
+ "step": { "type": "string", "description": "Equals phase, as appendPhaseHistory writes it." },
119
+ "enteredAt": { "type": "string", "format": "date-time", "description": "Valid-time start = evidence.observedTime." },
120
+ "exitedAt": {
121
+ "type": ["string", "null"],
122
+ "format": "date-time",
123
+ "description": "Recomputed by the merge from valid time: the next occurrence's enteredAt, or null when last."
124
+ },
125
+ "from": {
126
+ "type": ["string", "null"],
127
+ "description": "The phase of the preceding occurrence in the merged order, or null when first."
128
+ },
129
+ "to": { "type": "string", "description": "Equals phase." },
130
+ "outcome": { "type": ["string", "null"], "description": "'backfilled' for a backfilled occurrence." },
131
+ "timestamp": { "type": "string", "format": "date-time", "description": "Equals enteredAt." },
132
+ "recordedAt": {
133
+ "type": "string",
134
+ "format": "date-time",
135
+ "description": "Transaction time, minted ONCE and persisted in the intent (BP-6). Never re-stamped on a re-drive."
136
+ },
137
+ "origin": { "type": "string", "enum": ["live", "backfill"] },
138
+ "confidence": { "type": "number", "minimum": 0, "maximum": 1 },
139
+ "episode": {
140
+ "type": "integer",
141
+ "minimum": 1,
142
+ "description": "1 = pre-adoption (enteredAt < lifecycle.startedAt), 2 = at or after it."
143
+ },
144
+ "operation_id": {
145
+ "type": "string",
146
+ "format": "uuid",
147
+ "description": "Present on the TERMINAL occurrence only (BP-6), so a re-drive can detect it and skip the append."
148
+ },
149
+ "evidence": { "$ref": "#/definitions/ResolvedEvidence" }
150
+ },
151
+ "additionalProperties": false
152
+ },
153
+
154
+ "BackfillIntent": {
155
+ "description": "BP-3: the SINGLE recovery DTO, at .compose/data/completion-intents/<CODE>.json. Everything recovery needs is here; the batch record is a marker only.",
156
+ "type": "object",
157
+ "required": ["operation_id", "feature_code", "commit_sha", "files_changed", "notes",
158
+ "tests_attested", "started_at", "intent", "request_digest", "reason", "mode",
159
+ "occurrences", "terminal_occurrence", "write_plan", "envelope", "guarded",
160
+ "guard_initial", "upgrade", "policy_checksum"],
161
+ "comment": "R3-4: notes, guard_initial, upgrade and policy_checksum are REQUIRED although all four are nullable. Every one of them is a write input §5.10 consumes; making presence mandatory means a resumed attempt can tell 'the operation had none' from 'the intent did not record it'.",
162
+ "properties": {
163
+ "operation_id": { "type": "string", "format": "uuid" },
164
+ "feature_code": { "type": "string" },
165
+ "commit_sha": { "type": ["string", "null"] },
166
+ "tests_attested": { "type": "boolean" },
167
+ "started_at": { "type": "string", "format": "date-time" },
168
+ "intent": { "type": "string", "const": "backfill" },
169
+ "request_digest": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
170
+ "reason": { "type": "string" },
171
+ "mode": { "type": "string" },
172
+ "guarded": {
173
+ "type": "boolean",
174
+ "description": "R2B-4: the EFFECTIVE guard flag for the WHOLE operation, captured once."
175
+ },
176
+ "files_changed": {
177
+ "type": "array",
178
+ "items": { "type": "string" },
179
+ "description": "R2B-3: a recordCompletion argument, so it must survive to a resume."
180
+ },
181
+ "notes": { "type": ["string", "null"] },
182
+ "guard_initial": {
183
+ "type": ["object", "null"],
184
+ "description": "R2B-3: copied onto the batch record; recovery must not recompute it."
185
+ },
186
+ "upgrade": {
187
+ "type": ["object", "null"],
188
+ "description": "R2B-3: the descriptor stamp for the batch record."
189
+ },
190
+ "write_plan": {
191
+ "description": "R2B-3: the VALIDATED write plan from §5.5. anyOf, not oneOf: a backfilled entry satisfies BOTH branches by construction.",
192
+ "type": "object",
193
+ "required": ["history", "written", "skipped"],
194
+ "properties": {
195
+ "history": {
196
+ "type": "array",
197
+ "items": {
198
+ "anyOf": [
199
+ { "$ref": "#/definitions/BackfilledOccurrence" },
200
+ { "$ref": "#/definitions/StoredHistoryEntry" }
201
+ ]
202
+ }
203
+ },
204
+ "written": { "type": "array", "items": { "type": "string" } },
205
+ "skipped": { "type": "array", "items": { "type": "string" } }
206
+ },
207
+ "additionalProperties": false
208
+ },
209
+ "occurrences": {
210
+ "type": "array",
211
+ "items": { "$ref": "#/definitions/BackfilledOccurrence" },
212
+ "description": "BP-4/BP-6: the MATERIALISED, VALIDATED, stably-timestamped occurrences, persisted before the transition."
213
+ },
214
+ "terminal_occurrence": { "$ref": "#/definitions/BackfilledOccurrence" },
215
+ "policy_checksum": {
216
+ "type": ["string", "null"],
217
+ "pattern": "^[0-9a-f]{64}$",
218
+ "description": "BP-1: the checksum from `guard policy` AFTER any upgrade and BEFORE the transition. Null when guarded is false."
219
+ },
220
+ "envelope": {
221
+ "type": "object",
222
+ "required": ["from", "to", "artifacts", "resolved_by", "idempotency_key", "modified_files"],
223
+ "properties": {
224
+ "from": { "type": "string" },
225
+ "to": { "type": "string", "const": "complete_backfilled" },
226
+ "artifacts": { "type": "object" },
227
+ "modified_files": { "type": "array", "items": { "type": "string" } },
228
+ "resolved_by": { "type": "string", "const": "agent" },
229
+ "idempotency_key": { "type": "string", "format": "uuid" },
230
+ "expected_policy_checksum": {
231
+ "type": "string",
232
+ "pattern": "^[0-9a-f]{64}$",
233
+ "description": "R2B-7/R3-2/R4-1: ALWAYS present and non-null on a guarded operation, and EQUAL to the intent's policy_checksum. The equality is enforced here by an Ajv $data reference (validators must be constructed with {$data:true}), because the two fields cannot disagree without a recovery replaying under a policy the plan was never validated against.",
234
+ "const": { "$data": "2/policy_checksum" }
235
+ }
236
+ },
237
+ "additionalProperties": false
238
+ }
239
+ },
240
+ "additionalProperties": false,
241
+ "allOf": [
242
+ {
243
+ "description": "R4-1: a GUARDED intent must carry a real checksum on both the intent and the envelope. Making them merely optional let an intent with neither validate, which is exactly the shape that reopens the pre-transition window.",
244
+ "if": {
245
+ "required": ["guarded"],
246
+ "properties": { "guarded": { "const": true } }
247
+ },
248
+ "then": {
249
+ "properties": {
250
+ "policy_checksum": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
251
+ "envelope": { "required": ["expected_policy_checksum"] }
252
+ }
253
+ }
254
+ },
255
+ {
256
+ "description": "An UNGUARDED intent has no policy to pin, so it must carry neither.",
257
+ "if": {
258
+ "required": ["guarded"],
259
+ "properties": { "guarded": { "const": false } }
260
+ },
261
+ "then": {
262
+ "properties": {
263
+ "policy_checksum": { "type": "null" },
264
+ "envelope": { "not": { "required": ["expected_policy_checksum"] } }
265
+ }
266
+ }
267
+ }
268
+ ]
269
+ },
270
+
271
+ "BackfillRecord": {
272
+ "description": "BP-3: one entry in lifecycle.backfills[], keyed by operation_id. A MARKER, not a recovery source.",
273
+ "type": "object",
274
+ "required": ["operation_id", "request_digest", "state", "reason", "recordedAt", "completionEvidence", "actor"],
275
+ "properties": {
276
+ "operation_id": { "type": "string", "format": "uuid" },
277
+ "request_digest": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
278
+ "state": { "type": "string", "enum": ["pending", "finalized"] },
279
+ "reason": { "type": "string", "minLength": 1 },
280
+ "recordedAt": { "type": "string", "format": "date-time" },
281
+ "finalizedAt": { "type": ["string", "null"], "format": "date-time" },
282
+ "completionEvidence": {
283
+ "type": "object",
284
+ "required": ["commit_sha", "tests_attested"],
285
+ "properties": {
286
+ "commit_sha": { "type": ["string", "null"], "pattern": "^[0-9a-f]{40}$" },
287
+ "tests_attested": { "type": "boolean" },
288
+ "verified_at": { "type": "string", "format": "date-time" }
289
+ },
290
+ "additionalProperties": false
291
+ },
292
+ "guardRef": { "type": ["string", "null"] },
293
+ "guard_initial": {
294
+ "type": ["object", "null"],
295
+ "required": ["registered", "lifecycle_phase"],
296
+ "properties": {
297
+ "registered": { "type": "string" },
298
+ "lifecycle_phase": { "type": "string" }
299
+ },
300
+ "additionalProperties": false
301
+ },
302
+ "upgrade": {
303
+ "type": ["object", "null"],
304
+ "required": ["descriptor_id", "status"],
305
+ "properties": {
306
+ "descriptor_id": { "type": "string" },
307
+ "status": { "type": "string", "enum": ["applied", "unchanged"] },
308
+ "ledger_ref": { "type": ["string", "null"] }
309
+ },
310
+ "additionalProperties": false
311
+ },
312
+ "actor": { "type": "string", "description": "resolved_by plus the transport, e.g. 'agent:rest'." },
313
+ "occurrenceKeys": {
314
+ "type": "array",
315
+ "items": { "type": "string" },
316
+ "description": "phase + U+001F + evidence.ref for each occurrence this operation wrote. Uses UNIT SEPARATOR, never a NUL byte."
317
+ }
318
+ },
319
+ "additionalProperties": false
320
+ }
321
+ }
322
+ }