@defrex/autobuild 0.10.1 → 0.12.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 (120) hide show
  1. package/README.md +3 -1
  2. package/SPEC.md +237 -23
  3. package/bin/ab-dispatch-kernel.ts +1 -1
  4. package/docs/architecture.md +66 -4
  5. package/docs/configuration.md +137 -19
  6. package/docs/hosted-dispatcher.md +17 -2
  7. package/docs/hosted-service.md +214 -0
  8. package/docs/mcp.md +8 -2
  9. package/docs/operator-api.md +107 -7
  10. package/docs/remote-store-protocol.md +189 -144
  11. package/docs/remote-ticket-protocol.md +111 -18
  12. package/docs/setup.md +173 -16
  13. package/docs/ticket-dependencies.md +1 -1
  14. package/package.json +7 -6
  15. package/packages/core/src/cli/attention.ts +209 -0
  16. package/packages/core/src/cli/binary.ts +3 -3
  17. package/packages/core/src/cli/configured-store.ts +17 -0
  18. package/packages/core/src/cli/connect.ts +261 -0
  19. package/packages/core/src/cli/dashboard/model.ts +8 -5
  20. package/packages/core/src/cli/dashboard/render.ts +37 -7
  21. package/packages/core/src/cli/dispatch-frontend.ts +16 -1
  22. package/packages/core/src/cli/dispatch-process.ts +25 -7
  23. package/packages/core/src/cli/dispatch.ts +247 -47
  24. package/packages/core/src/cli/dispatcher-liveness.ts +91 -0
  25. package/packages/core/src/cli/help.ts +45 -13
  26. package/packages/core/src/cli/init-validation.ts +27 -2
  27. package/packages/core/src/cli/main.ts +41 -15
  28. package/packages/core/src/cli/mcp.ts +7 -1
  29. package/packages/core/src/cli/pr-summary-publication.ts +220 -0
  30. package/packages/core/src/cli/pr-summary.ts +2 -0
  31. package/packages/core/src/cli/project-credentials.ts +132 -0
  32. package/packages/core/src/cli/repo-state.ts +15 -2
  33. package/packages/core/src/cli/repository-status.ts +45 -5
  34. package/packages/core/src/cli/self-update.ts +10 -0
  35. package/packages/core/src/cli/store-opening.ts +22 -14
  36. package/packages/core/src/cli/terminals.ts +0 -10
  37. package/packages/core/src/cli/ticket.ts +8 -1
  38. package/packages/core/src/cli/upgrade-compatibility.ts +80 -0
  39. package/packages/core/src/cli/wait.ts +84 -17
  40. package/packages/core/src/cli/watch.ts +350 -68
  41. package/packages/core/src/config/live.ts +4 -0
  42. package/packages/core/src/config/schema.ts +65 -11
  43. package/packages/core/src/connection.ts +86 -0
  44. package/packages/core/src/events/catalog.ts +10 -2
  45. package/packages/core/src/events/globs.ts +8 -3
  46. package/packages/core/src/events/payloads.ts +44 -0
  47. package/packages/core/src/events/repository.ts +64 -2
  48. package/packages/core/src/integration/harness.ts +2 -1
  49. package/packages/core/src/kernel/__snapshots__/reducer-registry.test.ts.snap +148 -83
  50. package/packages/core/src/kernel/dispatch-status.ts +9 -1
  51. package/packages/core/src/kernel/dispatcher-liveness.ts +70 -0
  52. package/packages/core/src/kernel/engine.ts +24 -0
  53. package/packages/core/src/kernel/generators/build-log.ts +10 -0
  54. package/packages/core/src/kernel/log-index.ts +17 -0
  55. package/packages/core/src/kernel/pr-summary.ts +89 -0
  56. package/packages/core/src/kernel/reducer-registry.ts +12 -0
  57. package/packages/core/src/kernel/reducer.ts +8 -0
  58. package/packages/core/src/kernel/ticket-source-health.ts +30 -0
  59. package/packages/core/src/ontology.ts +1 -0
  60. package/packages/core/src/operator/index.ts +7 -0
  61. package/packages/core/src/operator/query.ts +44 -19
  62. package/packages/core/src/operator/ticket-source-backend.ts +1 -0
  63. package/packages/core/src/operator/tickets.ts +6 -8
  64. package/packages/core/src/plugin-sdk/index.ts +1 -0
  65. package/packages/core/src/plugins/manifest.ts +5 -3
  66. package/packages/core/src/plugins/runtimes.ts +3 -1
  67. package/packages/core/src/ports/forge/fake.ts +13 -1
  68. package/packages/core/src/ports/forge/github-transport.ts +36 -1
  69. package/packages/core/src/ports/forge/github.ts +29 -5
  70. package/packages/core/src/ports/forge/local-git.ts +12 -4
  71. package/packages/core/src/ports/forge/summary-marker.ts +8 -0
  72. package/packages/core/src/ports/runner/session-env.ts +1 -0
  73. package/packages/core/src/ports/tickets/contract.ts +25 -0
  74. package/packages/core/src/ports/tickets/create.ts +11 -3
  75. package/packages/core/src/ports/tickets/fake.ts +10 -8
  76. package/packages/core/src/ports/tickets/file.ts +1 -0
  77. package/packages/core/src/ports/tickets/hosted-index.ts +0 -3
  78. package/packages/core/src/ports/tickets/linear.ts +241 -49
  79. package/packages/core/src/ports/tickets/remote-protocol.ts +3 -0
  80. package/packages/core/src/ports/tickets/remote.ts +1 -0
  81. package/packages/core/src/ports/types.ts +17 -0
  82. package/packages/core/src/ports/workspace/build-execution.ts +5 -1
  83. package/packages/core/src/ports/workspace/create.ts +11 -2
  84. package/packages/core/src/ports/workspace/distribution-archive.ts +3 -5
  85. package/packages/core/src/ports/workspace/harvest-execution.ts +3 -1
  86. package/packages/core/src/ports/workspace/local-build-execution.ts +8 -1
  87. package/packages/core/src/ports/workspace/vercel-sandbox.ts +47 -19
  88. package/packages/core/src/processes/build-execution-state.ts +1 -1
  89. package/packages/core/src/processes/build-runner.ts +87 -26
  90. package/packages/core/src/processes/dispatch-failure-budget.ts +83 -0
  91. package/packages/core/src/processes/dispatcher.ts +336 -83
  92. package/packages/core/src/processes/harvest-child.ts +1 -0
  93. package/packages/core/src/processes/harvest-execution-state.ts +19 -2
  94. package/packages/core/src/processes/harvest-runner.ts +142 -100
  95. package/packages/core/src/processes/phase-failure-budget.ts +1 -0
  96. package/packages/core/src/processes/publication-loss.ts +18 -1
  97. package/packages/core/src/processes/repo-view.ts +2 -1
  98. package/packages/core/src/store/adapter.ts +1 -0
  99. package/packages/core/src/store/contract.ts +109 -0
  100. package/packages/core/src/store/digest.ts +14 -2
  101. package/packages/core/src/store/local/store.ts +18 -5
  102. package/packages/core/src/store/remote/client.ts +1 -1
  103. package/packages/core/src/store/remote/compatibility.ts +90 -0
  104. package/packages/core/src/store/remote/credentials.ts +28 -0
  105. package/packages/core/src/store/remote/index.ts +14 -1
  106. package/packages/core/src/store/remote/protocol.ts +26 -0
  107. package/packages/core/src/store/remote/token.ts +74 -13
  108. package/packages/core/src/store/repo-state-events.ts +49 -59
  109. package/packages/core/src/store/types.ts +5 -1
  110. package/packages/core/src/testing/index.ts +7 -0
  111. package/skills/guide/SKILL.md +175 -26
  112. package/skills/guide/references/operator-api.md +135 -13
  113. package/skills/guide/references/plugin-authoring.md +10 -2
  114. package/skills/guide/references/remote-store-protocol.md +189 -144
  115. package/skills/guide/references/remote-ticket-protocol.md +111 -18
  116. package/skills/guide/references/setup.md +173 -16
  117. package/skills/guide/references/ticket-dependencies.md +1 -1
  118. package/skills/operate/SKILL.md +159 -34
  119. package/templates/hosted-service/package.json +13 -0
  120. package/templates/hosted-service/vercel.json +7 -0
@@ -138,27 +138,28 @@ exports[`reducer registry accumulator shapes 1`] = `
138
138
  "$.lastEvent.actor.user: string",
139
139
  "$.lastEvent.actor: object",
140
140
  "$.lastEvent.build: string",
141
+ "$.lastEvent.payload.answer: string",
141
142
  "$.lastEvent.payload.artifact.kind: string",
142
143
  "$.lastEvent.payload.artifact.rev: number",
143
144
  "$.lastEvent.payload.artifact: object",
144
- "$.lastEvent.payload.asset.assetId: number",
145
- "$.lastEvent.payload.asset.provider: string",
146
- "$.lastEvent.payload.asset.releaseId: number",
147
- "$.lastEvent.payload.asset.repository: string",
148
- "$.lastEvent.payload.asset.url: string",
149
- "$.lastEvent.payload.asset: object",
145
+ "$.lastEvent.payload.assets: array",
146
+ "$.lastEvent.payload.assets[].dirCount: number",
147
+ "$.lastEvent.payload.assets[].fileCount: number",
148
+ "$.lastEvent.payload.assets[].kind: string",
149
+ "$.lastEvent.payload.assets[].layout: string",
150
+ "$.lastEvent.payload.assets[].name: string",
151
+ "$.lastEvent.payload.assets[].revision: number",
152
+ "$.lastEvent.payload.assets[].size: number",
153
+ "$.lastEvent.payload.assets[]: object",
150
154
  "$.lastEvent.payload.attempt: number",
151
155
  "$.lastEvent.payload.base.sha: string",
152
156
  "$.lastEvent.payload.base.source: string",
153
157
  "$.lastEvent.payload.base: object",
158
+ "$.lastEvent.payload.base: string",
159
+ "$.lastEvent.payload.baseSha: string",
160
+ "$.lastEvent.payload.boundary: number",
154
161
  "$.lastEvent.payload.branch: string",
155
162
  "$.lastEvent.payload.cause: string",
156
- "$.lastEvent.payload.cleanupPending: boolean",
157
- "$.lastEvent.payload.command: string",
158
- "$.lastEvent.payload.commandSeq: number",
159
- "$.lastEvent.payload.commits.base: string",
160
- "$.lastEvent.payload.commits.head: string",
161
- "$.lastEvent.payload.commits: object",
162
163
  "$.lastEvent.payload.counters.claimed: number",
163
164
  "$.lastEvent.payload.counters.closed: number",
164
165
  "$.lastEvent.payload.counters.conflicted: number",
@@ -174,7 +175,6 @@ exports[`reducer registry accumulator shapes 1`] = `
174
175
  "$.lastEvent.payload.decision: string",
175
176
  "$.lastEvent.payload.defaultSeq: number",
176
177
  "$.lastEvent.payload.dependencyDiagnostics: array",
177
- "$.lastEvent.payload.designationSeq: number",
178
178
  "$.lastEvent.payload.dispositions: array",
179
179
  "$.lastEvent.payload.dispositions[].action: string",
180
180
  "$.lastEvent.payload.dispositions[].occurrence.build: string",
@@ -185,15 +185,15 @@ exports[`reducer registry accumulator shapes 1`] = `
185
185
  "$.lastEvent.payload.effectiveConfig.kind: string",
186
186
  "$.lastEvent.payload.effectiveConfig.rev: number",
187
187
  "$.lastEvent.payload.effectiveConfig: object",
188
+ "$.lastEvent.payload.eligibleAfter: string",
188
189
  "$.lastEvent.payload.enabled: boolean",
189
190
  "$.lastEvent.payload.environmentId: string",
190
191
  "$.lastEvent.payload.error: string",
191
192
  "$.lastEvent.payload.escalation: number",
192
193
  "$.lastEvent.payload.execution: string",
193
- "$.lastEvent.payload.exitStatus: number",
194
194
  "$.lastEvent.payload.filename: string",
195
- "$.lastEvent.payload.files: array",
196
195
  "$.lastEvent.payload.findings: array",
196
+ "$.lastEvent.payload.generation: number",
197
197
  "$.lastEvent.payload.globs: array",
198
198
  "$.lastEvent.payload.globs[]: string",
199
199
  "$.lastEvent.payload.host: string",
@@ -207,36 +207,36 @@ exports[`reducer registry accumulator shapes 1`] = `
207
207
  "$.lastEvent.payload.kind: string",
208
208
  "$.lastEvent.payload.level: string",
209
209
  "$.lastEvent.payload.mediaType: string",
210
+ "$.lastEvent.payload.mergeCommit: string",
210
211
  "$.lastEvent.payload.message: string",
211
212
  "$.lastEvent.payload.observations: array",
212
213
  "$.lastEvent.payload.observations[].build: string",
213
214
  "$.lastEvent.payload.observations[].seq: number",
214
215
  "$.lastEvent.payload.observations[]: object",
216
+ "$.lastEvent.payload.occurrence: number",
215
217
  "$.lastEvent.payload.ok: boolean",
216
218
  "$.lastEvent.payload.operation: string",
217
219
  "$.lastEvent.payload.operator: string",
218
220
  "$.lastEvent.payload.outcome: string",
219
- "$.lastEvent.payload.output: string",
220
221
  "$.lastEvent.payload.phase: string",
221
222
  "$.lastEvent.payload.pid: number",
222
- "$.lastEvent.payload.policyCause: string",
223
223
  "$.lastEvent.payload.pr.headSha: string",
224
224
  "$.lastEvent.payload.pr.number: number",
225
225
  "$.lastEvent.payload.pr.url: string",
226
+ "$.lastEvent.payload.pr: number",
226
227
  "$.lastEvent.payload.pr: object",
227
228
  "$.lastEvent.payload.provider: string",
228
229
  "$.lastEvent.payload.question: string",
229
230
  "$.lastEvent.payload.queued: number",
230
231
  "$.lastEvent.payload.reason: string",
231
232
  "$.lastEvent.payload.ref: string",
232
- "$.lastEvent.payload.refs: array",
233
- "$.lastEvent.payload.refs[]: string",
234
233
  "$.lastEvent.payload.remote: boolean",
235
234
  "$.lastEvent.payload.report.kind: string",
236
235
  "$.lastEvent.payload.report.rev: number",
237
236
  "$.lastEvent.payload.report: object",
238
237
  "$.lastEvent.payload.request: number",
239
- "$.lastEvent.payload.retryable: boolean",
238
+ "$.lastEvent.payload.resetAt: string",
239
+ "$.lastEvent.payload.resolution: string",
240
240
  "$.lastEvent.payload.role: string",
241
241
  "$.lastEvent.payload.roleWarnings: array",
242
242
  "$.lastEvent.payload.roleWarnings[]: string",
@@ -252,10 +252,8 @@ exports[`reducer registry accumulator shapes 1`] = `
252
252
  "$.lastEvent.payload.snapshots.outcome: string",
253
253
  "$.lastEvent.payload.snapshots: object",
254
254
  "$.lastEvent.payload.source: string",
255
- "$.lastEvent.payload.stage: string",
256
255
  "$.lastEvent.payload.step: string",
257
256
  "$.lastEvent.payload.stream: string",
258
- "$.lastEvent.payload.summary: string",
259
257
  "$.lastEvent.payload.text: string",
260
258
  "$.lastEvent.payload.ticket.id: string",
261
259
  "$.lastEvent.payload.ticket.source: string",
@@ -323,7 +321,6 @@ exports[`reducer registry accumulator shapes 1`] = `
323
321
  "$.pending.abort[].actor.user: string",
324
322
  "$.pending.abort[].actor: object",
325
323
  "$.pending.abort[].command: string",
326
- "$.pending.abort[].reason: string",
327
324
  "$.pending.abort[].reason: undefined",
328
325
  "$.pending.abort[].seq: number",
329
326
  "$.pending.abort[]: object",
@@ -392,7 +389,6 @@ exports[`reducer registry accumulator shapes 1`] = `
392
389
  "$.terminal: string",
393
390
  "$.terminal: undefined",
394
391
  "$.verify.currentStep: string",
395
- "$.verify.currentStep: undefined",
396
392
  "$.verify.cycleSince: number",
397
393
  "$.verify.maxAttemptSeen: number",
398
394
  "$.verify.results: array",
@@ -414,6 +410,7 @@ exports[`reducer registry accumulator shapes 1`] = `
414
410
  "$.observations[].ts: string",
415
411
  "$.observations[]: object",
416
412
  "$.openInstance: null",
413
+ "$.readinessChangeSeq: number",
417
414
  "$.reclaimed: array",
418
415
  "$.terminal: string",
419
416
  "$: object",
@@ -436,6 +433,7 @@ exports[`reducer registry accumulator shapes 1`] = `
436
433
  "$.entries[].event: object",
437
434
  "$.entries[].headSha: string",
438
435
  "$.entries[].seq: number",
436
+ "$.entries[].seq: undefined",
439
437
  "$.entries[].t: string",
440
438
  "$.entries[]: object",
441
439
  "$: object",
@@ -517,19 +515,11 @@ exports[`reducer registry accumulator shapes 1`] = `
517
515
  "$.intervals.reconcile.open: object",
518
516
  "$.intervals.reconcile: object",
519
517
  "$.intervals.verify:types.closed: array",
520
- "$.intervals.verify:types.closed[].end: number",
521
- "$.intervals.verify:types.closed[].start: number",
522
- "$.intervals.verify:types.closed[].startSeq: number",
523
- "$.intervals.verify:types.closed[]: object",
524
518
  "$.intervals.verify:types.open.start: number",
525
519
  "$.intervals.verify:types.open.startSeq: number",
526
520
  "$.intervals.verify:types.open: object",
527
521
  "$.intervals.verify:types: object",
528
522
  "$.intervals.verify:unit.closed: array",
529
- "$.intervals.verify:unit.closed[].end: number",
530
- "$.intervals.verify:unit.closed[].start: number",
531
- "$.intervals.verify:unit.closed[].startSeq: number",
532
- "$.intervals.verify:unit.closed[]: object",
533
523
  "$.intervals.verify:unit.open.start: number",
534
524
  "$.intervals.verify:unit.open.startSeq: number",
535
525
  "$.intervals.verify:unit.open: object",
@@ -584,6 +574,23 @@ exports[`reducer registry accumulator shapes 1`] = `
584
574
  "$.workspaceReleased: number",
585
575
  "$: object",
586
576
  ],
577
+ "dispatchFailureBudget": [
578
+ "$.aborted: boolean",
579
+ "$.failures: array",
580
+ "$.failures[].actor.kind: string",
581
+ "$.failures[].actor: object",
582
+ "$.failures[].build: string",
583
+ "$.failures[].payload.attempt: number",
584
+ "$.failures[].payload.error: string",
585
+ "$.failures[].payload.stage: string",
586
+ "$.failures[].payload: object",
587
+ "$.failures[].seq: number",
588
+ "$.failures[].ts: string",
589
+ "$.failures[].type: string",
590
+ "$.failures[]: object",
591
+ "$.open: array",
592
+ "$: object",
593
+ ],
587
594
  "dispatchSettings": [
588
595
  "$.autoMergeSeq: number",
589
596
  "$.defaultAutoMerge: boolean",
@@ -611,6 +618,7 @@ exports[`reducer registry accumulator shapes 1`] = `
611
618
  "$.run: string",
612
619
  "$.warningNotice: string",
613
620
  "$.warningNotice: undefined",
621
+ "$.yieldedHolder: undefined",
614
622
  "$: object",
615
623
  ],
616
624
  "frozenPrImageHost": [
@@ -715,20 +723,6 @@ exports[`reducer registry accumulator shapes 1`] = `
715
723
  "$.byKind.screenshot.type: string",
716
724
  "$.byKind.screenshot: object",
717
725
  "$.byKind: object",
718
- "$.hostedByDesignation.1.actor.kind: string",
719
- "$.hostedByDesignation.1.actor: object",
720
- "$.hostedByDesignation.1.build: string",
721
- "$.hostedByDesignation.1.payload.asset.assetId: number",
722
- "$.hostedByDesignation.1.payload.asset.provider: string",
723
- "$.hostedByDesignation.1.payload.asset.releaseId: number",
724
- "$.hostedByDesignation.1.payload.asset.repository: string",
725
- "$.hostedByDesignation.1.payload.asset.url: string",
726
- "$.hostedByDesignation.1.payload.asset: object",
727
- "$.hostedByDesignation.1.payload.designationSeq: number",
728
- "$.hostedByDesignation.1.payload: object",
729
- "$.hostedByDesignation.1.seq: number",
730
- "$.hostedByDesignation.1.ts: string",
731
- "$.hostedByDesignation.1.type: string",
732
726
  "$.hostedByDesignation.12.actor.kind: string",
733
727
  "$.hostedByDesignation.12.actor: object",
734
728
  "$.hostedByDesignation.12.build: string",
@@ -744,37 +738,36 @@ exports[`reducer registry accumulator shapes 1`] = `
744
738
  "$.hostedByDesignation.12.ts: string",
745
739
  "$.hostedByDesignation.12.type: string",
746
740
  "$.hostedByDesignation.12: object",
747
- "$.hostedByDesignation.15.actor.kind: string",
748
- "$.hostedByDesignation.15.actor: object",
749
- "$.hostedByDesignation.15.build: string",
750
- "$.hostedByDesignation.15.payload.asset.assetId: number",
751
- "$.hostedByDesignation.15.payload.asset.provider: string",
752
- "$.hostedByDesignation.15.payload.asset.releaseId: number",
753
- "$.hostedByDesignation.15.payload.asset.repository: string",
754
- "$.hostedByDesignation.15.payload.asset.url: string",
755
- "$.hostedByDesignation.15.payload.asset: object",
756
- "$.hostedByDesignation.15.payload.designationSeq: number",
757
- "$.hostedByDesignation.15.payload: object",
758
- "$.hostedByDesignation.15.seq: number",
759
- "$.hostedByDesignation.15.ts: string",
760
- "$.hostedByDesignation.15.type: string",
761
- "$.hostedByDesignation.15: object",
762
- "$.hostedByDesignation.1: object",
763
- "$.hostedByDesignation.2.actor.kind: string",
764
- "$.hostedByDesignation.2.actor: object",
765
- "$.hostedByDesignation.2.build: string",
766
- "$.hostedByDesignation.2.payload.asset.assetId: number",
767
- "$.hostedByDesignation.2.payload.asset.provider: string",
768
- "$.hostedByDesignation.2.payload.asset.releaseId: number",
769
- "$.hostedByDesignation.2.payload.asset.repository: string",
770
- "$.hostedByDesignation.2.payload.asset.url: string",
771
- "$.hostedByDesignation.2.payload.asset: object",
772
- "$.hostedByDesignation.2.payload.designationSeq: number",
773
- "$.hostedByDesignation.2.payload: object",
774
- "$.hostedByDesignation.2.seq: number",
775
- "$.hostedByDesignation.2.ts: string",
776
- "$.hostedByDesignation.2.type: string",
777
- "$.hostedByDesignation.2: object",
741
+ "$.hostedByDesignation.13.actor.kind: string",
742
+ "$.hostedByDesignation.13.actor: object",
743
+ "$.hostedByDesignation.13.build: string",
744
+ "$.hostedByDesignation.13.payload.asset.assetId: number",
745
+ "$.hostedByDesignation.13.payload.asset.provider: string",
746
+ "$.hostedByDesignation.13.payload.asset.releaseId: number",
747
+ "$.hostedByDesignation.13.payload.asset.repository: string",
748
+ "$.hostedByDesignation.13.payload.asset.url: string",
749
+ "$.hostedByDesignation.13.payload.asset: object",
750
+ "$.hostedByDesignation.13.payload.designationSeq: number",
751
+ "$.hostedByDesignation.13.payload: object",
752
+ "$.hostedByDesignation.13.seq: number",
753
+ "$.hostedByDesignation.13.ts: string",
754
+ "$.hostedByDesignation.13.type: string",
755
+ "$.hostedByDesignation.13: object",
756
+ "$.hostedByDesignation.19.actor.kind: string",
757
+ "$.hostedByDesignation.19.actor: object",
758
+ "$.hostedByDesignation.19.build: string",
759
+ "$.hostedByDesignation.19.payload.asset.assetId: number",
760
+ "$.hostedByDesignation.19.payload.asset.provider: string",
761
+ "$.hostedByDesignation.19.payload.asset.releaseId: number",
762
+ "$.hostedByDesignation.19.payload.asset.repository: string",
763
+ "$.hostedByDesignation.19.payload.asset.url: string",
764
+ "$.hostedByDesignation.19.payload.asset: object",
765
+ "$.hostedByDesignation.19.payload.designationSeq: number",
766
+ "$.hostedByDesignation.19.payload: object",
767
+ "$.hostedByDesignation.19.seq: number",
768
+ "$.hostedByDesignation.19.ts: string",
769
+ "$.hostedByDesignation.19.type: string",
770
+ "$.hostedByDesignation.19: object",
778
771
  "$.hostedByDesignation.3.actor.kind: string",
779
772
  "$.hostedByDesignation.3.actor: object",
780
773
  "$.hostedByDesignation.3.build: string",
@@ -805,6 +798,36 @@ exports[`reducer registry accumulator shapes 1`] = `
805
798
  "$.hostedByDesignation.4.ts: string",
806
799
  "$.hostedByDesignation.4.type: string",
807
800
  "$.hostedByDesignation.4: object",
801
+ "$.hostedByDesignation.5.actor.kind: string",
802
+ "$.hostedByDesignation.5.actor: object",
803
+ "$.hostedByDesignation.5.build: string",
804
+ "$.hostedByDesignation.5.payload.asset.assetId: number",
805
+ "$.hostedByDesignation.5.payload.asset.provider: string",
806
+ "$.hostedByDesignation.5.payload.asset.releaseId: number",
807
+ "$.hostedByDesignation.5.payload.asset.repository: string",
808
+ "$.hostedByDesignation.5.payload.asset.url: string",
809
+ "$.hostedByDesignation.5.payload.asset: object",
810
+ "$.hostedByDesignation.5.payload.designationSeq: number",
811
+ "$.hostedByDesignation.5.payload: object",
812
+ "$.hostedByDesignation.5.seq: number",
813
+ "$.hostedByDesignation.5.ts: string",
814
+ "$.hostedByDesignation.5.type: string",
815
+ "$.hostedByDesignation.5: object",
816
+ "$.hostedByDesignation.6.actor.kind: string",
817
+ "$.hostedByDesignation.6.actor: object",
818
+ "$.hostedByDesignation.6.build: string",
819
+ "$.hostedByDesignation.6.payload.asset.assetId: number",
820
+ "$.hostedByDesignation.6.payload.asset.provider: string",
821
+ "$.hostedByDesignation.6.payload.asset.releaseId: number",
822
+ "$.hostedByDesignation.6.payload.asset.repository: string",
823
+ "$.hostedByDesignation.6.payload.asset.url: string",
824
+ "$.hostedByDesignation.6.payload.asset: object",
825
+ "$.hostedByDesignation.6.payload.designationSeq: number",
826
+ "$.hostedByDesignation.6.payload: object",
827
+ "$.hostedByDesignation.6.seq: number",
828
+ "$.hostedByDesignation.6.ts: string",
829
+ "$.hostedByDesignation.6.type: string",
830
+ "$.hostedByDesignation.6: object",
808
831
  "$.hostedByDesignation.8.actor.kind: string",
809
832
  "$.hostedByDesignation.8.actor: object",
810
833
  "$.hostedByDesignation.8.build: string",
@@ -837,6 +860,7 @@ exports[`reducer registry accumulator shapes 1`] = `
837
860
  "$.retained[].actor.user: string",
838
861
  "$.retained[].actor: object",
839
862
  "$.retained[].payload.attempt: number",
863
+ "$.retained[].payload.cause: string",
840
864
  "$.retained[].payload.counters.claimed: number",
841
865
  "$.retained[].payload.counters.closed: number",
842
866
  "$.retained[].payload.counters.conflicted: number",
@@ -872,6 +896,7 @@ exports[`reducer registry accumulator shapes 1`] = `
872
896
  "$.retained[].payload.observations[].build: string",
873
897
  "$.retained[].payload.observations[].seq: number",
874
898
  "$.retained[].payload.observations[]: object",
899
+ "$.retained[].payload.operation: string",
875
900
  "$.retained[].payload.operator: string",
876
901
  "$.retained[].payload.outcome: string",
877
902
  "$.retained[].payload.pid: number",
@@ -881,6 +906,7 @@ exports[`reducer registry accumulator shapes 1`] = `
881
906
  "$.retained[].payload.report.kind: string",
882
907
  "$.retained[].payload.report.rev: number",
883
908
  "$.retained[].payload.report: object",
909
+ "$.retained[].payload.resetAt: string",
884
910
  "$.retained[].payload.roleWarnings: array",
885
911
  "$.retained[].payload.roleWarnings[]: string",
886
912
  "$.retained[].payload.run: string",
@@ -1018,16 +1044,15 @@ exports[`reducer registry accumulator shapes 1`] = `
1018
1044
  "$.hosted[].type: string",
1019
1045
  "$.hosted[]: object",
1020
1046
  "$.reclaimedAt.10: number",
1021
- "$.reclaimedAt.11: number",
1022
- "$.reclaimedAt.15: number",
1047
+ "$.reclaimedAt.12: number",
1023
1048
  "$.reclaimedAt.16: number",
1024
- "$.reclaimedAt.18: number",
1025
- "$.reclaimedAt.1: number",
1049
+ "$.reclaimedAt.17: number",
1050
+ "$.reclaimedAt.19: number",
1026
1051
  "$.reclaimedAt.20: number",
1027
- "$.reclaimedAt.3: number",
1052
+ "$.reclaimedAt.2: number",
1028
1053
  "$.reclaimedAt.6: number",
1029
- "$.reclaimedAt.7: number",
1030
1054
  "$.reclaimedAt.8: number",
1055
+ "$.reclaimedAt.9: number",
1031
1056
  "$.reclaimedAt: object",
1032
1057
  "$: object",
1033
1058
  ],
@@ -1103,6 +1128,7 @@ exports[`reducer registry accumulator shapes 1`] = `
1103
1128
  "$.ledger[].request: number",
1104
1129
  "$.ledger[].round: number",
1105
1130
  "$.ledger[].seq: number",
1131
+ "$.ledger[].seq: undefined",
1106
1132
  "$.ledger[].step: string",
1107
1133
  "$.ledger[].t: string",
1108
1134
  "$.ledger[]: object",
@@ -1156,6 +1182,7 @@ exports[`reducer registry accumulator shapes 1`] = `
1156
1182
  "$.retained[].payload.base: string",
1157
1183
  "$.retained[].payload.baseBranch: string",
1158
1184
  "$.retained[].payload.baseSha: string",
1185
+ "$.retained[].payload.boundary: number",
1159
1186
  "$.retained[].payload.branch: string",
1160
1187
  "$.retained[].payload.cause: string",
1161
1188
  "$.retained[].payload.cleanupPending: boolean",
@@ -1180,6 +1207,7 @@ exports[`reducer registry accumulator shapes 1`] = `
1180
1207
  "$.retained[].payload.defaultSeq: number",
1181
1208
  "$.retained[].payload.dependencyDiagnostics: array",
1182
1209
  "$.retained[].payload.designationSeq: number",
1210
+ "$.retained[].payload.disposition: string",
1183
1211
  "$.retained[].payload.dispositions: array",
1184
1212
  "$.retained[].payload.dispositions[].action: string",
1185
1213
  "$.retained[].payload.dispositions[].occurrence.build: string",
@@ -1190,6 +1218,7 @@ exports[`reducer registry accumulator shapes 1`] = `
1190
1218
  "$.retained[].payload.effectiveConfig.kind: string",
1191
1219
  "$.retained[].payload.effectiveConfig.rev: number",
1192
1220
  "$.retained[].payload.effectiveConfig: object",
1221
+ "$.retained[].payload.eligibleAfter: string",
1193
1222
  "$.retained[].payload.enabled: boolean",
1194
1223
  "$.retained[].payload.environmentId: string",
1195
1224
  "$.retained[].payload.error: string",
@@ -1219,6 +1248,7 @@ exports[`reducer registry accumulator shapes 1`] = `
1219
1248
  "$.retained[].payload.observations[].build: string",
1220
1249
  "$.retained[].payload.observations[].seq: number",
1221
1250
  "$.retained[].payload.observations[]: object",
1251
+ "$.retained[].payload.occurrence: number",
1222
1252
  "$.retained[].payload.ok: boolean",
1223
1253
  "$.retained[].payload.operation: string",
1224
1254
  "$.retained[].payload.operator: string",
@@ -1231,6 +1261,7 @@ exports[`reducer registry accumulator shapes 1`] = `
1231
1261
  "$.retained[].payload.pr.headSha: string",
1232
1262
  "$.retained[].payload.pr.number: number",
1233
1263
  "$.retained[].payload.pr.url: string",
1264
+ "$.retained[].payload.pr: number",
1234
1265
  "$.retained[].payload.pr: object",
1235
1266
  "$.retained[].payload.provider: string",
1236
1267
  "$.retained[].payload.question: string",
@@ -1245,6 +1276,7 @@ exports[`reducer registry accumulator shapes 1`] = `
1245
1276
  "$.retained[].payload.report.rev: number",
1246
1277
  "$.retained[].payload.report: object",
1247
1278
  "$.retained[].payload.request: number",
1279
+ "$.retained[].payload.resetAt: string",
1248
1280
  "$.retained[].payload.resolution: string",
1249
1281
  "$.retained[].payload.retryable: boolean",
1250
1282
  "$.retained[].payload.role: string",
@@ -1359,6 +1391,39 @@ exports[`reducer registry accumulator shapes 1`] = `
1359
1391
  "$.failures[]: number",
1360
1392
  "$: object",
1361
1393
  ],
1394
+ "summaryBoundary": [
1395
+ "$.boundary: number",
1396
+ "$.occurrence: number",
1397
+ "$.sessions.s_a: boolean",
1398
+ "$.sessions.s_b: boolean",
1399
+ "$.sessions: object",
1400
+ "$: object",
1401
+ ],
1402
+ "summaryRequests": [
1403
+ "$.requests: array",
1404
+ "$.requests[].actor.kind: string",
1405
+ "$.requests[].actor: object",
1406
+ "$.requests[].build: string",
1407
+ "$.requests[].payload.boundary: number",
1408
+ "$.requests[].payload.occurrence: number",
1409
+ "$.requests[].payload: object",
1410
+ "$.requests[].seq: number",
1411
+ "$.requests[].ts: string",
1412
+ "$.requests[].type: string",
1413
+ "$.requests[]: object",
1414
+ "$.settled: array",
1415
+ "$.settled[]: number",
1416
+ "$: object",
1417
+ ],
1418
+ "ticketSourceHealth": [
1419
+ "$.active.cause: string",
1420
+ "$.active.message: string",
1421
+ "$.active.operation: string",
1422
+ "$.active.resetAt: string",
1423
+ "$.active.source: string",
1424
+ "$.active: object",
1425
+ "$: object",
1426
+ ],
1362
1427
  "verifyDiffBase": [
1363
1428
  "$.pendingReconcileBase: string",
1364
1429
  "$.promoted: string",
@@ -12,6 +12,7 @@ export interface DispatchStatus {
12
12
  queued?: number
13
13
  /** Standing count from newer tick facts; absent for historical events. */
14
14
  creationWithheld?: number
15
+ yieldedHolder?: string
15
16
  availableUpgrade?: string
16
17
  roleWarnings: string[]
17
18
  diagnostics: string[]
@@ -58,6 +59,7 @@ export function reduceDispatchStatus(
58
59
  state.lastSeq = event.seq
59
60
  switch (event.type) {
60
61
  case 'dispatcher.run-started':
62
+ state.yieldedHolder = undefined
61
63
  state.health = 'running'
62
64
  state.effectiveConfig = event.payload.effectiveConfig
63
65
  state.roleWarnings = [...event.payload.roleWarnings]
@@ -88,6 +90,12 @@ export function reduceDispatchStatus(
88
90
  )
89
91
  state.warningNotice = state.notice
90
92
  break
93
+ case 'dispatcher.tick-yielded':
94
+ state.yieldedHolder = event.payload.holder
95
+ break
96
+ case 'dispatcher.tick-started':
97
+ state.yieldedHolder = undefined
98
+ break
91
99
  case 'dispatcher.tick-completed':
92
100
  state.queued = event.payload.queued
93
101
  state.creationWithheld = event.payload.counters.creationWithheld
@@ -143,7 +151,7 @@ export function reduceDispatchStatus(
143
151
 
144
152
  /** Bump when `DispatchStatus`'s shape or fold semantics change (it is its own
145
153
  * accumulator: `lastSeq` makes the fold skip already-seen events). */
146
- export const DISPATCH_STATUS_REDUCER_VERSION = 1
154
+ export const DISPATCH_STATUS_REDUCER_VERSION = 2
147
155
 
148
156
  /** The incremental form of `reduceDispatchStatus` for one run. The run is a
149
157
  * parameter, so this is a factory. */
@@ -0,0 +1,70 @@
1
+ import type { RepositoryEvent } from '../events/repository'
2
+ import type { RepositoryRecord } from '../store/types'
3
+
4
+ export function projectDispatcherLiveness(
5
+ record: RepositoryRecord | null,
6
+ events: RepositoryEvent[],
7
+ now: Date,
8
+ ) {
9
+ const ordered = [...events].sort((a, b) => a.seq - b.seq)
10
+ const age = (at: string) => Math.max(0, now.getTime() - Date.parse(at))
11
+ const run = (id: string) => {
12
+ const start = ordered.find((e) => e.type === 'dispatcher.run-started' && e.payload.run === id)
13
+ const stop = ordered.findLast(
14
+ (e) => e.type === 'dispatcher.run-stopped' && e.payload.run === id,
15
+ )
16
+ const tick = ordered.findLast(
17
+ (e) =>
18
+ (e.type === 'dispatcher.tick-completed' || e.type === 'dispatcher.tick-failed') &&
19
+ e.payload.run === id,
20
+ )
21
+ const heartbeatAt =
22
+ record?.lease?.holder === id && start && record.heartbeatAt && record.heartbeatAt >= start.ts
23
+ ? record.heartbeatAt
24
+ : null
25
+ return {
26
+ run: id,
27
+ pid: start?.type === 'dispatcher.run-started' ? start.payload.pid : null,
28
+ host: start?.type === 'dispatcher.run-started' ? (start.payload.host ?? null) : null,
29
+ startedAt: start?.ts ?? null,
30
+ heartbeatAt,
31
+ heartbeatAgeMs: heartbeatAt === null ? null : age(heartbeatAt),
32
+ leaseExpiresAt: record?.lease?.holder === id ? record.lease.expiresAt : null,
33
+ lastTickAt: tick?.ts ?? null,
34
+ lastTickAgeMs: tick ? age(tick.ts) : null,
35
+ lastTick:
36
+ tick?.type === 'dispatcher.tick-completed'
37
+ ? { outcome: 'completed' as const, counters: tick.payload.counters }
38
+ : tick?.type === 'dispatcher.tick-failed'
39
+ ? { outcome: 'failed' as const, error: tick.payload.error }
40
+ : null,
41
+ stoppedAt: stop?.ts ?? null,
42
+ stop: stop?.type === 'dispatcher.run-stopped' ? stop.payload : null,
43
+ }
44
+ }
45
+ const latest = ordered.findLast((e) => e.type === 'dispatcher.run-started')
46
+ const latestStop = ordered.findLast((e) => e.type === 'dispatcher.run-stopped')
47
+ const owner = record?.lease ? run(record.lease.holder) : null
48
+ const held =
49
+ owner !== null && owner.stop === null && Date.parse(record!.lease!.expiresAt) > now.getTime()
50
+ return {
51
+ state: held
52
+ ? ('held' as const)
53
+ : owner && !owner.stop
54
+ ? ('lost' as const)
55
+ : latestStop
56
+ ? ('stopped' as const)
57
+ : ('absent' as const),
58
+ holder: held ? owner : null,
59
+ expiredHolder: !held && owner && !owner.stop ? owner : null,
60
+ latestRun: latest?.type === 'dispatcher.run-started' ? run(latest.payload.run) : null,
61
+ latestStoppedRun:
62
+ latestStop?.type === 'dispatcher.run-stopped' ? run(latestStop.payload.run) : null,
63
+ }
64
+ }
65
+ export type DispatcherLiveness = ReturnType<typeof projectDispatcherLiveness>
66
+ export function formatDispatcherIdentity(
67
+ run: NonNullable<DispatcherLiveness['latestRun']>,
68
+ ): string {
69
+ return `${run.run}; pid ${run.pid ?? 'unknown'}; host ${run.host ?? 'unknown'}; started ${run.startedAt ?? 'unknown'}; ${run.heartbeatAgeMs === null ? 'heartbeat unknown (no renewal attributable to this run)' : `heartbeat ${run.heartbeatAgeMs}ms ago`}`
70
+ }
@@ -55,6 +55,7 @@ export type VerifyAction =
55
55
  export type FinalizeAction = { kind: 'check'; command: string } | { kind: 'agent'; skill: string }
56
56
 
57
57
  export type Decision =
58
+ | { kind: 'capture-reconcile-policy'; reconcileSeq: number }
58
59
  | { kind: 'wait'; reason: WaitReason }
59
60
  | { kind: 'acknowledge'; command: 'pause' | 'resume' | 'abort' }
60
61
  | {
@@ -80,6 +81,7 @@ export type Decision =
80
81
  step: string
81
82
  attempt: number
82
83
  reason: string
84
+ skipCause?: 'post-reconcile-policy'
83
85
  }
84
86
  | { kind: 'run-finalize-step'; step: string; action: FinalizeAction }
85
87
  | {
@@ -313,6 +315,18 @@ export function decideNextFromFacts(state: BuildState, log: LogIndex, config: Co
313
315
  const cycleAttempts = [...cycleResults, ...cycleStarted].map((v) => v.attempt)
314
316
  const attempt =
315
317
  cycleAttempts.length > 0 ? Math.max(...cycleAttempts) : log.maxVerifyAttemptEver + 1
318
+ // Freeze only a fresh reconcile cycle. A newer code-review approval restores
319
+ // ordinary verification. Already-started historical cycles have no snapshot:
320
+ // preserve their rerun-all policy, including after a crashed verify start.
321
+ const reconcileCycle =
322
+ log.lastReconcileCompletedSeq > Math.max(log.code.latestApproveSeq, log.restartSeq)
323
+ const policy =
324
+ reconcileCycle && log.reconcilePolicy?.reconcileSeq === cycleBoundary
325
+ ? log.reconcilePolicy
326
+ : undefined
327
+ if (reconcileCycle && policy === undefined && cycleAttempts.length === 0) {
328
+ return { kind: 'capture-reconcile-policy', reconcileSeq: cycleBoundary }
329
+ }
316
330
  const approvedPlan = log.plan.approvedPlan
317
331
  const approvedSelection =
318
332
  approvedPlan?.verifySteps === undefined
@@ -333,6 +347,16 @@ export function decideNextFromFacts(state: BuildState, log: LogIndex, config: Co
333
347
  const stepConfig = config.verify.stepConfigs[step]
334
348
  if (stepConfig === undefined) continue // unreachable: configSchema cross-validates (§16.1)
335
349
 
350
+ const exclusion = policy?.steps.find((s) => s.step === step && !s.enabled)
351
+ if (exclusion !== undefined)
352
+ return {
353
+ kind: 'skip-verify',
354
+ step,
355
+ attempt,
356
+ skipCause: 'post-reconcile-policy',
357
+ reason: `skipped by post-reconcile policy: ${exclusion.source} = false`,
358
+ }
359
+
336
360
  // Missing selection data is the historical default-all behavior. For a
337
361
  // current explicit selection, exclusion precedes path evaluation and any
338
362
  // verifier construction. Mandatory gates remain selected defensively even
@@ -207,6 +207,16 @@ function randomWrite(rand: () => number): EventWrite {
207
207
  },
208
208
  }),
209
209
  () => w('pr-attachment.reclaimed', { hostedSeq: 1 + Math.floor(rand() * 20) }),
210
+ () => w('pr-summary.requested', { occurrence: round, boundary: round + 1 }),
211
+ () => w('pr-summary.settled', { request: round, disposition: 'attempted' }),
212
+ () => w('pr-summary.attempted', { occurrence: round, boundary: round + 1, pr: 7 }),
213
+ () =>
214
+ w('pr-summary.result', {
215
+ attempt: round,
216
+ outcome: 'rate-limited',
217
+ eligibleAfter: '2026-10-15T00:01:00.000Z',
218
+ }),
219
+ () => w('finalize.step-started', { step: 'notes' }),
210
220
  () => w('build.auto-merge-default-observed', { defaultSeq: round }),
211
221
  () =>
212
222
  w('observation.recorded', {