@boxxy/model 0.1.2 → 0.1.4

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 (234) hide show
  1. package/README.md +8 -6
  2. package/dist/closeouts/build-close-out-schema.d.ts +31 -0
  3. package/dist/closeouts/close-out-answer-schema.d.ts +3 -0
  4. package/dist/closeouts/close-out-schema.d.ts +3 -0
  5. package/dist/closeouts/design-close-out-schema.d.ts +30 -0
  6. package/dist/closeouts/friction-schema.d.ts +14 -0
  7. package/dist/closeouts/open-item-schema.d.ts +3 -0
  8. package/dist/closeouts/review-close-out-schema.d.ts +40 -0
  9. package/dist/closeouts/triage-close-out-schema.d.ts +40 -0
  10. package/dist/closeouts/types.d.ts +221 -0
  11. package/dist/dispatch/build-applied-change.d.ts +6 -0
  12. package/dist/dispatch/collect-seat-sync.d.ts +14 -0
  13. package/dist/dispatch/pick-seat-take.d.ts +10 -0
  14. package/dist/dispatch/types.d.ts +11 -0
  15. package/dist/{outbox-fact-schema.d.ts → facts/outbox-fact-schema.d.ts} +1 -1
  16. package/dist/facts/parse-new-fact.d.ts +2 -0
  17. package/dist/facts/types.d.ts +191 -0
  18. package/dist/facts/work-fact-schemas.d.ts +293 -0
  19. package/dist/index.d.ts +64 -23
  20. package/dist/items/build-item-views.d.ts +4 -0
  21. package/dist/items/collect-retries.d.ts +3 -0
  22. package/dist/items/collect-settled.d.ts +3 -0
  23. package/dist/items/derive-item-state.d.ts +17 -0
  24. package/dist/{live-levers.d.ts → items/live-levers.d.ts} +1 -1
  25. package/dist/items/types.d.ts +12 -0
  26. package/dist/pipeline/advance.d.ts +10 -0
  27. package/dist/pipeline/constants.d.ts +2 -0
  28. package/dist/pipeline/count-trailing.d.ts +1 -0
  29. package/dist/pipeline/find-position.d.ts +9 -0
  30. package/dist/pipeline/find-step.d.ts +3 -0
  31. package/dist/pipeline/merge-by-time.d.ts +9 -0
  32. package/dist/pipeline/position-schema.d.ts +3 -0
  33. package/dist/pipeline/types.d.ts +28 -0
  34. package/dist/planning/define-admission.d.ts +3 -0
  35. package/dist/planning/define-gate.d.ts +3 -0
  36. package/dist/planning/define-signal.d.ts +3 -0
  37. package/dist/planning/is-filter-match.d.ts +3 -0
  38. package/dist/planning/limit-fill-schema.d.ts +3 -0
  39. package/dist/{signal-key.d.ts → planning/signal-key.d.ts} +1 -1
  40. package/dist/planning/types.d.ts +155 -0
  41. package/dist/ports/types.d.ts +289 -0
  42. package/dist/reports/constants.d.ts +3 -0
  43. package/dist/reports/cost-to-land-schema.d.ts +29 -0
  44. package/dist/reports/outbox-status.d.ts +16 -0
  45. package/dist/reports/stage-time-schema.d.ts +25 -0
  46. package/dist/reports/types.d.ts +5 -0
  47. package/dist/rows/actor-ref-schema.d.ts +2 -0
  48. package/dist/rows/attempt-row-schema.d.ts +3 -0
  49. package/dist/rows/attempt-session-row-schema.d.ts +3 -0
  50. package/dist/{brand-schemas.d.ts → rows/brand-schemas.d.ts} +5 -2
  51. package/dist/rows/caller-identity-schema.d.ts +3 -0
  52. package/dist/rows/criterion-schema.d.ts +3 -0
  53. package/dist/rows/decision-row-schema.d.ts +34 -0
  54. package/dist/rows/evidence-row-schema.d.ts +3 -0
  55. package/dist/rows/item-row-schema.d.ts +28 -0
  56. package/dist/rows/item-view-schema.d.ts +3 -0
  57. package/dist/rows/lease-row-schema.d.ts +3 -0
  58. package/dist/rows/lever-row-schema.d.ts +12 -0
  59. package/dist/rows/limit-schema.d.ts +3 -0
  60. package/dist/rows/limit-seat-row-schema.d.ts +3 -0
  61. package/dist/rows/note-row-schema.d.ts +3 -0
  62. package/dist/rows/notice-row-schema.d.ts +3 -0
  63. package/dist/rows/pull-request-row-schema.d.ts +3 -0
  64. package/dist/rows/relation-row-schema.d.ts +3 -0
  65. package/dist/{row-schemas.d.ts → rows/row-schemas.d.ts} +2 -2
  66. package/dist/rows/runner-row-schema.d.ts +3 -0
  67. package/dist/rows/spawn-request-schema.d.ts +3 -0
  68. package/dist/rules/define-phase.d.ts +3 -0
  69. package/dist/rules/load-rejection-schema.d.ts +3 -0
  70. package/dist/rules/types.d.ts +244 -0
  71. package/dist/test-utils/build-mock-cost-to-land.d.ts +2 -0
  72. package/dist/test-utils/build-mock-evidence-row.d.ts +1 -1
  73. package/dist/test-utils/build-mock-fact-id.d.ts +1 -1
  74. package/dist/test-utils/build-mock-frame-item.d.ts +1 -1
  75. package/dist/test-utils/build-mock-frame.d.ts +2 -0
  76. package/dist/test-utils/build-mock-item-rules.d.ts +2 -0
  77. package/dist/test-utils/build-mock-lease-row.d.ts +1 -1
  78. package/dist/test-utils/build-mock-outbox-fact.d.ts +2 -0
  79. package/dist/test-utils/build-mock-phase-kind.d.ts +7 -0
  80. package/dist/test-utils/build-mock-pipeline.d.ts +7 -0
  81. package/dist/test-utils/build-mock-relation-row.d.ts +2 -0
  82. package/dist/test-utils/build-mock-settled.d.ts +9 -0
  83. package/dist/test-utils/build-mock-snapshot.d.ts +1 -1
  84. package/dist/test-utils/build-mock-v1-attempt-row.d.ts +2 -0
  85. package/dist/test-utils/build-mock-v1-item-row.d.ts +2 -0
  86. package/dist/test-utils/build-mock-v1-item-view.d.ts +2 -0
  87. package/dist/test-utils/build-mock-v1-lever-row.d.ts +2 -0
  88. package/dist/test-utils/build-mock-v1-pull-request-row.d.ts +2 -0
  89. package/dist/test-utils/build-mock-work-fact.d.ts +12 -0
  90. package/dist/test-utils/create-fake-notifier.d.ts +9 -0
  91. package/dist/test-utils/create-fake-pepys-reader.d.ts +13 -0
  92. package/dist/test-utils/fact-sink-contract.d.ts +14 -0
  93. package/dist/test-utils/pepys-reader-contract.d.ts +17 -0
  94. package/dist/{types/core.d.ts → types.d.ts} +93 -43
  95. package/package.json +13 -3
  96. package/src/{build-close-out-schema.ts → closeouts/build-close-out-schema.ts} +5 -10
  97. package/src/closeouts/close-out-answer-schema.ts +42 -0
  98. package/src/closeouts/close-out-schema.ts +13 -0
  99. package/src/closeouts/design-close-out-schema.ts +20 -0
  100. package/src/closeouts/friction-schema.ts +11 -0
  101. package/src/closeouts/open-item-schema.ts +8 -0
  102. package/src/closeouts/review-close-out-schema.ts +38 -0
  103. package/src/closeouts/triage-close-out-schema.ts +29 -0
  104. package/src/closeouts/types.ts +230 -0
  105. package/src/dispatch/build-applied-change.ts +57 -0
  106. package/src/dispatch/collect-seat-sync.ts +86 -0
  107. package/src/dispatch/pick-seat-take.ts +39 -0
  108. package/src/dispatch/types.ts +14 -0
  109. package/src/facts/outbox-fact-schema.ts +22 -0
  110. package/src/facts/parse-new-fact.ts +16 -0
  111. package/src/facts/types.ts +235 -0
  112. package/src/facts/work-fact-schemas.ts +201 -0
  113. package/src/index.ts +147 -55
  114. package/src/items/build-item-views.ts +146 -0
  115. package/src/items/collect-retries.ts +12 -0
  116. package/src/items/collect-settled.ts +20 -0
  117. package/src/items/derive-item-state.ts +95 -0
  118. package/src/{live-levers.ts → items/live-levers.ts} +1 -1
  119. package/src/items/types.ts +26 -0
  120. package/src/pipeline/advance.ts +78 -0
  121. package/src/pipeline/constants.ts +12 -0
  122. package/src/pipeline/count-trailing.ts +16 -0
  123. package/src/pipeline/find-position.ts +74 -0
  124. package/src/pipeline/find-step.ts +6 -0
  125. package/src/pipeline/merge-by-time.ts +33 -0
  126. package/src/pipeline/position-schema.ts +27 -0
  127. package/src/pipeline/types.ts +47 -0
  128. package/src/{define-admission.ts → planning/define-admission.ts} +2 -2
  129. package/src/{define-gate.ts → planning/define-gate.ts} +2 -2
  130. package/src/{define-signal.ts → planning/define-signal.ts} +2 -2
  131. package/src/planning/is-filter-match.ts +10 -0
  132. package/src/planning/limit-fill-schema.ts +12 -0
  133. package/src/{signal-key.ts → planning/signal-key.ts} +1 -1
  134. package/src/planning/types.ts +216 -0
  135. package/src/ports/types.ts +412 -0
  136. package/src/reports/constants.ts +6 -0
  137. package/src/reports/cost-to-land-schema.ts +47 -0
  138. package/src/reports/outbox-status.ts +22 -0
  139. package/src/reports/stage-time-schema.ts +21 -0
  140. package/src/reports/types.ts +7 -0
  141. package/src/rows/actor-ref-schema.ts +17 -0
  142. package/src/rows/attempt-row-schema.ts +69 -0
  143. package/src/rows/attempt-session-row-schema.ts +12 -0
  144. package/src/{brand-schemas.ts → rows/brand-schemas.ts} +5 -2
  145. package/src/rows/caller-identity-schema.ts +7 -0
  146. package/src/rows/criterion-schema.ts +9 -0
  147. package/src/rows/decision-row-schema.ts +34 -0
  148. package/src/rows/evidence-row-schema.ts +13 -0
  149. package/src/rows/item-row-schema.ts +32 -0
  150. package/src/rows/item-view-schema.ts +31 -0
  151. package/src/rows/lease-row-schema.ts +12 -0
  152. package/src/rows/lever-row-schema.ts +17 -0
  153. package/src/rows/limit-schema.ts +12 -0
  154. package/src/rows/limit-seat-row-schema.ts +12 -0
  155. package/src/rows/note-row-schema.ts +12 -0
  156. package/src/rows/notice-row-schema.ts +17 -0
  157. package/src/rows/pull-request-row-schema.ts +15 -0
  158. package/src/rows/relation-row-schema.ts +13 -0
  159. package/src/rows/row-schemas.ts +30 -0
  160. package/src/rows/runner-row-schema.ts +9 -0
  161. package/src/rows/spawn-request-schema.ts +15 -0
  162. package/src/rules/define-phase.ts +12 -0
  163. package/src/rules/load-rejection-schema.ts +51 -0
  164. package/src/rules/types.ts +291 -0
  165. package/src/test-utils/build-mock-cost-to-land.ts +31 -0
  166. package/src/test-utils/build-mock-evidence-row.ts +2 -2
  167. package/src/test-utils/build-mock-fact-id.ts +3 -3
  168. package/src/test-utils/build-mock-frame-item.ts +10 -5
  169. package/src/test-utils/build-mock-frame.ts +13 -0
  170. package/src/test-utils/build-mock-item-rules.ts +17 -0
  171. package/src/test-utils/build-mock-lease-row.ts +2 -2
  172. package/src/test-utils/build-mock-outbox-fact.ts +22 -0
  173. package/src/test-utils/build-mock-phase-kind.ts +29 -0
  174. package/src/test-utils/build-mock-pipeline.ts +52 -0
  175. package/src/test-utils/{build-mock-link-row.ts → build-mock-relation-row.ts} +4 -4
  176. package/src/test-utils/build-mock-settled.ts +18 -0
  177. package/src/test-utils/build-mock-snapshot.ts +10 -2
  178. package/src/test-utils/{build-mock-attempt-row.ts → build-mock-v1-attempt-row.ts} +28 -9
  179. package/src/test-utils/{build-mock-item-row.ts → build-mock-v1-item-row.ts} +7 -3
  180. package/src/test-utils/build-mock-v1-item-view.ts +29 -0
  181. package/src/test-utils/{build-mock-lever-row.ts → build-mock-v1-lever-row.ts} +5 -3
  182. package/src/test-utils/build-mock-v1-pull-request-row.ts +26 -0
  183. package/src/test-utils/build-mock-work-fact.ts +179 -0
  184. package/src/test-utils/create-fake-notifier.ts +54 -0
  185. package/src/test-utils/create-fake-pepys-reader.ts +73 -0
  186. package/src/test-utils/fact-sink-contract.ts +103 -0
  187. package/src/test-utils/pepys-reader-contract.ts +117 -0
  188. package/src/{types/core.ts → types.ts} +183 -59
  189. package/dist/actor-ref-schema.d.ts +0 -2
  190. package/dist/build-applied-change.d.ts +0 -13
  191. package/dist/build-close-out-schema.d.ts +0 -3
  192. package/dist/build-item-views.d.ts +0 -2
  193. package/dist/define-admission.d.ts +0 -3
  194. package/dist/define-gate.d.ts +0 -3
  195. package/dist/define-signal.d.ts +0 -3
  196. package/dist/derive-item-state.d.ts +0 -2
  197. package/dist/fact-schemas.d.ts +0 -5
  198. package/dist/find-slot-take.d.ts +0 -5
  199. package/dist/friction-schema.d.ts +0 -3
  200. package/dist/test-utils/build-mock-attempt-row.d.ts +0 -2
  201. package/dist/test-utils/build-mock-item-row.d.ts +0 -2
  202. package/dist/test-utils/build-mock-item-rows.d.ts +0 -2
  203. package/dist/test-utils/build-mock-item-view.d.ts +0 -2
  204. package/dist/test-utils/build-mock-lever-row.d.ts +0 -2
  205. package/dist/test-utils/build-mock-link-row.d.ts +0 -2
  206. package/dist/test-utils/build-mock-pull-request-row.d.ts +0 -2
  207. package/dist/types/dispatch.d.ts +0 -82
  208. package/dist/types/facts.d.ts +0 -154
  209. package/dist/types/planning.d.ts +0 -94
  210. package/dist/types/ports.d.ts +0 -218
  211. package/dist/types/registries.d.ts +0 -150
  212. package/src/actor-ref-schema.ts +0 -9
  213. package/src/build-applied-change.ts +0 -71
  214. package/src/build-item-views.ts +0 -128
  215. package/src/derive-item-state.ts +0 -76
  216. package/src/fact-schemas.ts +0 -132
  217. package/src/find-slot-take.ts +0 -12
  218. package/src/friction-schema.ts +0 -10
  219. package/src/outbox-fact-schema.ts +0 -25
  220. package/src/row-schemas.ts +0 -89
  221. package/src/test-utils/build-mock-item-rows.ts +0 -14
  222. package/src/test-utils/build-mock-item-view.ts +0 -8
  223. package/src/test-utils/build-mock-pull-request-row.ts +0 -16
  224. package/src/types/dispatch.ts +0 -95
  225. package/src/types/facts.ts +0 -166
  226. package/src/types/planning.ts +0 -136
  227. package/src/types/ports.ts +0 -262
  228. package/src/types/registries.ts +0 -185
  229. /package/dist/test-utils/{seed-faker.d.ts → pin-faker-seed.d.ts} +0 -0
  230. /package/dist/{format-key-path.d.ts → utils/format-key-path.d.ts} +0 -0
  231. /package/dist/{to-json-value.d.ts → utils/to-json-value.d.ts} +0 -0
  232. /package/src/test-utils/{seed-faker.ts → pin-faker-seed.ts} +0 -0
  233. /package/src/{format-key-path.ts → utils/format-key-path.ts} +0 -0
  234. /package/src/{to-json-value.ts → utils/to-json-value.ts} +0 -0
@@ -0,0 +1,235 @@
1
+ import type { ContentHash, JsonValue } from '@boxxy/content-hash';
2
+ import type { CloseOut, Effect, Friction } from '../closeouts/types.ts';
3
+ import type { SeatRef } from '../planning/types.ts';
4
+ import type { WakeSource } from '../ports/types.ts';
5
+ import type {
6
+ ActorRef,
7
+ AgentSessionId,
8
+ AtcSessionId,
9
+ AttemptId,
10
+ CallerIdentity,
11
+ ClosingCause,
12
+ CommitSha,
13
+ Criterion,
14
+ CriterionId,
15
+ DecisionRow,
16
+ DesignMode,
17
+ EvidenceKind,
18
+ FactId,
19
+ FieldValues,
20
+ Instant,
21
+ ItemId,
22
+ ItemKindName,
23
+ ItemRow,
24
+ LeverRow,
25
+ Minutes,
26
+ PhaseName,
27
+ PoolName,
28
+ Product,
29
+ RecipeHash,
30
+ RelationTypeName,
31
+ RepoName,
32
+ RulesVersion,
33
+ SessionMode,
34
+ } from '../types.ts';
35
+
36
+ // Every fact on the stream names an item; a fact about an attempt names the attempt in its
37
+ // payload. The consumer rebuilds an item from the one scope of the item.
38
+ export type EntityRef = `boxxy:item:${string}`;
39
+
40
+ export interface FactEnvelope<M extends object, T extends keyof M & string = keyof M & string> {
41
+ readonly id: FactId;
42
+ readonly type: T;
43
+
44
+ // oxlint-disable-next-line id-length -- pepys's fact envelope names this field
45
+ readonly v: 1;
46
+ readonly api: 'boxxy/1';
47
+ readonly entity: EntityRef;
48
+ readonly actor: ActorRef;
49
+ readonly at: Instant;
50
+ readonly payload: M[T] & { readonly storeVersion: number };
51
+ }
52
+
53
+ export interface NewFact<T extends FactType> {
54
+ readonly id: FactId;
55
+ readonly type: T;
56
+ readonly entity: EntityRef;
57
+ readonly actor: ActorRef;
58
+ readonly payload: WorkFacts[T];
59
+ }
60
+
61
+ export type AnyNewFact = { [T in FactType]: NewFact<T> }[FactType];
62
+
63
+ export interface OutboxFact {
64
+ readonly seq: number;
65
+ readonly factId: FactId;
66
+ readonly type: FactType;
67
+ readonly entity: EntityRef;
68
+ readonly actor: ActorRef;
69
+ readonly at: Instant;
70
+ readonly payload: JsonValue;
71
+ readonly sentAt: Instant | null;
72
+
73
+ // when the sender gave the row up, and why: `too-large`, `unknown-type` or the sink's refusal
74
+ readonly parkedAt: Instant | null;
75
+ readonly parkedReason: string | null;
76
+ }
77
+
78
+ export type FactType = keyof WorkFacts;
79
+
80
+ export interface RelationPayload {
81
+ readonly source: ItemId;
82
+ readonly target: ItemId;
83
+ readonly type: RelationTypeName;
84
+ }
85
+
86
+ interface OfAttempt {
87
+ readonly attempt: AttemptId;
88
+ }
89
+
90
+ // The fact catalog of the stream `facts.boxxy.work`. A payload holds no `storeVersion`: the sink
91
+ // adds it to the wire form. Instants in a payload are epoch milliseconds.
92
+ export interface WorkFacts {
93
+ // the kept session is state between two attempts, and no fact carries it
94
+ 'boxxy.item.filed': Omit<ItemRow, 'keptSession'>;
95
+ 'boxxy.item.noted': { readonly seq: number; readonly text: string };
96
+ 'boxxy.item.edited': {
97
+ readonly title?: string;
98
+ readonly outcome?: string;
99
+ readonly acceptance?: readonly Criterion[];
100
+ readonly kind?: ItemKindName;
101
+ readonly fields?: { readonly designMode?: DesignMode };
102
+
103
+ // the whole new list
104
+ readonly labels?: readonly string[];
105
+
106
+ // the attempt whose settle made the edit
107
+ readonly byAttempt: AttemptId | null;
108
+ };
109
+ 'boxxy.item.closed': {
110
+ readonly reason: string;
111
+ readonly byAttempt: AttemptId | null;
112
+ readonly ended: readonly {
113
+ readonly attempt: AttemptId;
114
+ readonly how: 'settled' | 'closing';
115
+ }[];
116
+ };
117
+
118
+ // the entity is the source item
119
+ 'boxxy.relation.added': RelationPayload;
120
+ 'boxxy.relation.removed': RelationPayload;
121
+ 'boxxy.lever.recorded': LeverRow;
122
+ 'boxxy.attempt.claimed': OfAttempt & {
123
+ readonly phase: PhaseName;
124
+ readonly number: number;
125
+ readonly round: number;
126
+ readonly mode: SessionMode;
127
+ readonly pool: PoolName;
128
+ readonly fields: FieldValues;
129
+ readonly seats: readonly SeatRef[];
130
+ readonly recipeHash: RecipeHash;
131
+
132
+ // the skills pin of the rules at the claim
133
+ readonly skillsRef: CommitSha | null;
134
+ readonly rulesVersion: RulesVersion;
135
+ };
136
+ 'boxxy.decision.recorded': DecisionRow;
137
+ 'boxxy.attempt.started': OfAttempt & {
138
+ readonly atcSession: AtcSessionId;
139
+ readonly resumeFallback: boolean;
140
+ readonly caller: CallerIdentity | null;
141
+ };
142
+ 'boxxy.attempt.spawn_unanswered': OfAttempt & { readonly reason: string };
143
+ 'boxxy.attempt.reconciled': OfAttempt & {
144
+ readonly atcSession: AtcSessionId;
145
+ readonly caller: CallerIdentity | null;
146
+ };
147
+ 'boxxy.attempt.session_bound': OfAttempt & {
148
+ readonly atcSession: AtcSessionId;
149
+ readonly agentSession: AgentSessionId;
150
+ readonly product: Product;
151
+ readonly source: 'worker' | 'hook';
152
+ readonly seenAt: Instant;
153
+ };
154
+ 'boxxy.attempt.slept': OfAttempt & { readonly idleSince: Instant; readonly asked: boolean };
155
+ 'boxxy.attempt.wake_requested': OfAttempt & { readonly by: WakeSource };
156
+ 'boxxy.attempt.woken': OfAttempt & {
157
+ readonly holdGen: number;
158
+ readonly seats: readonly SeatRef[];
159
+ readonly overCap: boolean;
160
+ };
161
+ 'boxxy.attempt.resumed': OfAttempt & {
162
+ readonly atcSession: AtcSessionId;
163
+ readonly caller: CallerIdentity | null;
164
+ };
165
+ 'boxxy.attempt.closed_out': OfAttempt & {
166
+ readonly phase: PhaseName;
167
+ readonly closeOut: CloseOut;
168
+ readonly hash: ContentHash;
169
+ };
170
+ 'boxxy.attempt.timed_out': OfAttempt & {
171
+ readonly phase: PhaseName;
172
+ readonly afterMinutes: Minutes;
173
+ };
174
+ 'boxxy.attempt.turn_ended': OfAttempt & { readonly phase: PhaseName };
175
+ 'boxxy.attempt.settled': OfAttempt & {
176
+ readonly phase: PhaseName;
177
+ readonly round: number;
178
+ readonly state: 'succeeded' | 'failed';
179
+ readonly failure: string | null;
180
+
181
+ // a name the phase kind lists, or an engine outcome
182
+ readonly outcome: string;
183
+ readonly returnedOutcome: string | null;
184
+
185
+ // null: the session was already gone
186
+ readonly closing: ClosingCause | null;
187
+ readonly asleep: boolean;
188
+
189
+ // false: settled by the give-up rule
190
+ readonly sessionStopped: boolean;
191
+ readonly endedAt: Instant;
192
+ readonly criteria: readonly {
193
+ readonly criterion: CriterionId;
194
+ readonly evidence: readonly FactId[];
195
+ }[];
196
+ readonly matched: readonly ItemId[];
197
+ readonly frictionDropped: number;
198
+ readonly skippedEffects: readonly {
199
+ readonly effect: Effect['kind'];
200
+ readonly reason: string;
201
+ }[];
202
+ };
203
+ 'boxxy.proposal.recorded': OfAttempt & Friction;
204
+ 'boxxy.evidence.recorded': OfAttempt & {
205
+ readonly kind: EvidenceKind;
206
+ readonly ref: string;
207
+ readonly observed: string | null;
208
+ };
209
+ 'boxxy.pull_request.linked': OfAttempt & {
210
+ readonly repo: RepoName;
211
+ readonly number: number;
212
+ readonly url: string;
213
+
214
+ // the decimal text of GitHub's own ID of the pull request
215
+ readonly pullRequestId: string;
216
+ readonly source: 'hook' | 'watch';
217
+ };
218
+ 'boxxy.commit.linked': OfAttempt & {
219
+ readonly repo: RepoName;
220
+ readonly sha: CommitSha;
221
+ readonly pullRequestNumber: number;
222
+ };
223
+ 'boxxy.pull_request.merged': OfAttempt & {
224
+ readonly repo: RepoName;
225
+ readonly number: number;
226
+ readonly pullRequestId: string;
227
+ readonly mergedAt: Instant;
228
+ readonly mergeCommit: CommitSha;
229
+ };
230
+ 'boxxy.pull_request.closed': OfAttempt & {
231
+ readonly repo: RepoName;
232
+ readonly number: number;
233
+ readonly pullRequestId: string;
234
+ };
235
+ }
@@ -0,0 +1,201 @@
1
+ import { contentHashSchema } from '@boxxy/content-hash';
2
+ import { z } from 'zod';
3
+ import { closeOutSchema } from '../closeouts/close-out-schema.ts';
4
+ import { frictionSchema } from '../closeouts/friction-schema.ts';
5
+ import { brandSchemas } from '../rows/brand-schemas.ts';
6
+ import { callerIdentitySchema } from '../rows/caller-identity-schema.ts';
7
+ import { criterionSchema } from '../rows/criterion-schema.ts';
8
+ import { decisionRowSchema } from '../rows/decision-row-schema.ts';
9
+ import { itemRowSchema } from '../rows/item-row-schema.ts';
10
+ import { leverRowSchema } from '../rows/lever-row-schema.ts';
11
+ import type { FactType, WorkFacts } from './types.ts';
12
+
13
+ const countSchema = z.number().int().nonnegative();
14
+ const roundSchema = z.number().int().positive();
15
+ const pullRequestNumberSchema = z.number().int().positive();
16
+ const pullRequestIdSchema = z.string().regex(/^[1-9]\d*$/u);
17
+ const ofAttempt = { attempt: brandSchemas.attemptId };
18
+ const seatsSchema = z.array(z.object({ limit: brandSchemas.limitName }));
19
+ const designModeSchema = z.enum(['agent', 'attended']);
20
+ const editedFieldsSchema = z.object({ designMode: designModeSchema.exactOptional() });
21
+ const fieldValuesSchema = z.record(z.string(), z.array(z.string()));
22
+
23
+ const relationSchema = z.object({
24
+ source: brandSchemas.itemId,
25
+ target: brandSchemas.itemId,
26
+ type: brandSchemas.relationTypeName,
27
+ });
28
+
29
+ const endedAttemptSchema = z.object({
30
+ attempt: brandSchemas.attemptId,
31
+ how: z.enum(['settled', 'closing']),
32
+ });
33
+
34
+ const closingCauseSchema = z.enum([
35
+ 'closed-out',
36
+ 'timed-out',
37
+ 'item-closed',
38
+ 'stalled',
39
+ 'skill-missing',
40
+ 'turn-ended',
41
+ 'usage-limit',
42
+ ]);
43
+
44
+ const copiedEvidenceSchema = z.object({
45
+ criterion: brandSchemas.criterionId,
46
+ evidence: z.array(brandSchemas.factId),
47
+ });
48
+
49
+ const effectKindSchema = z.enum([
50
+ 'file-item',
51
+ 'close-item',
52
+ 'add-relation',
53
+ 'set-kind',
54
+ 'set-field',
55
+ 'set-acceptance',
56
+ 'record-proposal',
57
+ ]);
58
+
59
+ const skippedEffectSchema = z.object({ effect: effectKindSchema, reason: z.string().min(1) });
60
+
61
+ // One schema per fact type, with no `storeVersion`: the wire schema of the pepys package adds
62
+ // it. Each schema's output equals its payload type; the test of this file checks both ways.
63
+ export const workFactSchemas = {
64
+ 'boxxy.item.filed': itemRowSchema.omit({ keptSession: true }),
65
+ 'boxxy.item.noted': z.object({ seq: roundSchema, text: z.string().min(1) }),
66
+ 'boxxy.item.edited': z.object({
67
+ title: z.string().min(1).exactOptional(),
68
+ outcome: z.string().min(1).exactOptional(),
69
+ acceptance: z.array(criterionSchema).exactOptional(),
70
+ kind: brandSchemas.itemKindName.exactOptional(),
71
+ fields: editedFieldsSchema.exactOptional(),
72
+ labels: z.array(z.string().min(1)).exactOptional(),
73
+ byAttempt: brandSchemas.attemptId.nullable(),
74
+ }),
75
+ 'boxxy.item.closed': z.object({
76
+ reason: z.string().min(1),
77
+ byAttempt: brandSchemas.attemptId.nullable(),
78
+ ended: z.array(endedAttemptSchema),
79
+ }),
80
+ 'boxxy.relation.added': relationSchema,
81
+ 'boxxy.relation.removed': relationSchema,
82
+ 'boxxy.lever.recorded': leverRowSchema,
83
+ 'boxxy.attempt.claimed': z.object({
84
+ ...ofAttempt,
85
+ phase: brandSchemas.phaseName,
86
+ number: roundSchema,
87
+ round: roundSchema,
88
+ mode: designModeSchema,
89
+ pool: brandSchemas.poolName,
90
+ fields: fieldValuesSchema,
91
+ seats: seatsSchema,
92
+ recipeHash: brandSchemas.recipeHash,
93
+ skillsRef: brandSchemas.commitSha.nullable(),
94
+ rulesVersion: brandSchemas.rulesVersion,
95
+ }),
96
+ 'boxxy.decision.recorded': decisionRowSchema,
97
+ 'boxxy.attempt.started': z.object({
98
+ ...ofAttempt,
99
+ atcSession: brandSchemas.atcSessionId,
100
+ resumeFallback: z.boolean(),
101
+ caller: callerIdentitySchema.nullable(),
102
+ }),
103
+ 'boxxy.attempt.spawn_unanswered': z.object({ ...ofAttempt, reason: z.string().min(1) }),
104
+ 'boxxy.attempt.reconciled': z.object({
105
+ ...ofAttempt,
106
+ atcSession: brandSchemas.atcSessionId,
107
+ caller: callerIdentitySchema.nullable(),
108
+ }),
109
+ 'boxxy.attempt.session_bound': z.object({
110
+ ...ofAttempt,
111
+ atcSession: brandSchemas.atcSessionId,
112
+ agentSession: brandSchemas.agentSessionId,
113
+ product: z.enum(['claude_code', 'codex']),
114
+ source: z.enum(['worker', 'hook']),
115
+ seenAt: brandSchemas.instant,
116
+ }),
117
+ 'boxxy.attempt.slept': z.object({
118
+ ...ofAttempt,
119
+ idleSince: brandSchemas.instant,
120
+ asked: z.boolean(),
121
+ }),
122
+ 'boxxy.attempt.wake_requested': z.object({
123
+ ...ofAttempt,
124
+ by: z.enum(['command', 'session-ran', 'host-slept-unasked']),
125
+ }),
126
+ 'boxxy.attempt.woken': z.object({
127
+ ...ofAttempt,
128
+ holdGen: roundSchema,
129
+ seats: seatsSchema,
130
+ overCap: z.boolean(),
131
+ }),
132
+ 'boxxy.attempt.resumed': z.object({
133
+ ...ofAttempt,
134
+ atcSession: brandSchemas.atcSessionId,
135
+ caller: callerIdentitySchema.nullable(),
136
+ }),
137
+ 'boxxy.attempt.closed_out': z.object({
138
+ ...ofAttempt,
139
+ phase: brandSchemas.phaseName,
140
+ closeOut: closeOutSchema,
141
+ hash: contentHashSchema,
142
+ }),
143
+ 'boxxy.attempt.timed_out': z.object({
144
+ ...ofAttempt,
145
+ phase: brandSchemas.phaseName,
146
+ afterMinutes: z.number().positive(),
147
+ }),
148
+ 'boxxy.attempt.turn_ended': z.object({ ...ofAttempt, phase: brandSchemas.phaseName }),
149
+ 'boxxy.attempt.settled': z.object({
150
+ ...ofAttempt,
151
+ phase: brandSchemas.phaseName,
152
+ round: roundSchema,
153
+ state: z.enum(['succeeded', 'failed']),
154
+ failure: z.string().min(1).nullable(),
155
+ outcome: z.string().min(1),
156
+ returnedOutcome: z.string().min(1).nullable(),
157
+ closing: closingCauseSchema.nullable(),
158
+ asleep: z.boolean(),
159
+ sessionStopped: z.boolean(),
160
+ endedAt: brandSchemas.instant,
161
+ criteria: z.array(copiedEvidenceSchema),
162
+ matched: z.array(brandSchemas.itemId),
163
+ frictionDropped: countSchema,
164
+ skippedEffects: z.array(skippedEffectSchema),
165
+ }),
166
+ 'boxxy.proposal.recorded': frictionSchema.extend(ofAttempt),
167
+ 'boxxy.evidence.recorded': z.object({
168
+ ...ofAttempt,
169
+ kind: z.enum(['pull_request', 'commit', 'test', 'ci-run', 'live-check']),
170
+ ref: z.string().min(1),
171
+ observed: z.string().nullable(),
172
+ }),
173
+ 'boxxy.pull_request.linked': z.object({
174
+ ...ofAttempt,
175
+ repo: brandSchemas.repoName,
176
+ number: pullRequestNumberSchema,
177
+ url: z.url(),
178
+ pullRequestId: pullRequestIdSchema,
179
+ source: z.enum(['hook', 'watch']),
180
+ }),
181
+ 'boxxy.commit.linked': z.object({
182
+ ...ofAttempt,
183
+ repo: brandSchemas.repoName,
184
+ sha: brandSchemas.commitSha,
185
+ pullRequestNumber: pullRequestNumberSchema,
186
+ }),
187
+ 'boxxy.pull_request.merged': z.object({
188
+ ...ofAttempt,
189
+ repo: brandSchemas.repoName,
190
+ number: pullRequestNumberSchema,
191
+ pullRequestId: pullRequestIdSchema,
192
+ mergedAt: brandSchemas.instant,
193
+ mergeCommit: brandSchemas.commitSha,
194
+ }),
195
+ 'boxxy.pull_request.closed': z.object({
196
+ ...ofAttempt,
197
+ repo: brandSchemas.repoName,
198
+ number: pullRequestNumberSchema,
199
+ pullRequestId: pullRequestIdSchema,
200
+ }),
201
+ } satisfies { readonly [T in FactType]: z.ZodType<WorkFacts[T]> };