halfcycle 0.4.12 → 0.4.14

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 (54) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/bin/bin.bundle.mjs +9366 -567
  3. package/bin/pre-tool-use-filter.sh +470 -0
  4. package/dist/bin.d.ts +1 -0
  5. package/dist/bin.d.ts.map +1 -1
  6. package/dist/build-record/pair-io.d.ts +49 -0
  7. package/dist/build-record/pair-io.d.ts.map +1 -0
  8. package/dist/build-record/render-change.d.ts +25 -0
  9. package/dist/build-record/render-change.d.ts.map +1 -0
  10. package/dist/build-record/render-v2.d.ts +10 -5
  11. package/dist/build-record/render-v2.d.ts.map +1 -1
  12. package/dist/build-record/write-change.d.ts +63 -0
  13. package/dist/build-record/write-change.d.ts.map +1 -0
  14. package/dist/build-record/write.d.ts +5 -15
  15. package/dist/build-record/write.d.ts.map +1 -1
  16. package/dist/change.d.ts +136 -0
  17. package/dist/change.d.ts.map +1 -0
  18. package/dist/check-tasks.d.ts +71 -0
  19. package/dist/check-tasks.d.ts.map +1 -0
  20. package/dist/claude-plan.d.ts +114 -0
  21. package/dist/claude-plan.d.ts.map +1 -0
  22. package/dist/cli-contract.d.ts +12 -1
  23. package/dist/cli-contract.d.ts.map +1 -1
  24. package/dist/cli.js +9017 -5228
  25. package/dist/cli.js.map +3 -3
  26. package/dist/close-phase.d.ts +113 -10
  27. package/dist/close-phase.d.ts.map +1 -1
  28. package/dist/halfcycle-monorepo.d.ts +26 -0
  29. package/dist/halfcycle-monorepo.d.ts.map +1 -0
  30. package/dist/index.js +1617 -845
  31. package/dist/index.js.map +3 -3
  32. package/dist/install-manifest.d.ts +18 -1
  33. package/dist/install-manifest.d.ts.map +1 -1
  34. package/dist/install.d.ts +225 -3
  35. package/dist/install.d.ts.map +1 -1
  36. package/dist/merge-settings.d.ts +25 -2
  37. package/dist/merge-settings.d.ts.map +1 -1
  38. package/dist/open-phase.d.ts +1 -1
  39. package/dist/open-phase.d.ts.map +1 -1
  40. package/dist/pace.d.ts +138 -0
  41. package/dist/pace.d.ts.map +1 -0
  42. package/dist/phase-work.d.ts +37 -0
  43. package/dist/phase-work.d.ts.map +1 -0
  44. package/dist/plan.d.ts +5 -2
  45. package/dist/plan.d.ts.map +1 -1
  46. package/dist/prove.d.ts +77 -0
  47. package/dist/prove.d.ts.map +1 -0
  48. package/dist/review.d.ts +19 -1
  49. package/dist/review.d.ts.map +1 -1
  50. package/dist/system-report.d.ts +4 -4
  51. package/dist/system-report.d.ts.map +1 -1
  52. package/dist/uninstall.d.ts +4 -1
  53. package/dist/uninstall.d.ts.map +1 -1
  54. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2,7 +2,8 @@
2
2
  import { execFileSync as execFileSync2 } from "node:child_process";
3
3
  import { createHash as createHash2 } from "node:crypto";
4
4
  import { existsSync as existsSync3, lstatSync, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "node:fs";
5
- import { dirname as dirname3, join as join5, posix, relative } from "node:path";
5
+ import { homedir as homedir2 } from "node:os";
6
+ import { dirname as dirname3, isAbsolute as isAbsolute2, join as join5, posix, relative, resolve } from "node:path";
6
7
  import { fileURLToPath } from "node:url";
7
8
 
8
9
  // ../core/dist/guard-record.js
@@ -115,12 +116,15 @@ ${block}`);
115
116
  // ../core/dist/hook-wiring.js
116
117
  var SETTINGS_REL = ".claude/settings.json";
117
118
  var VENDORED_BIN_REL = ".halfcycle/bin/bin.bundle.mjs";
119
+ var PRE_FILTER_SHELL_FILENAME = "pre-tool-use-filter.sh";
120
+ var PRE_FILTER_FUNCTION = "halfcycle_pt_skip";
118
121
  var SESSION_START_MARKER_REL = ".claude/hooks/session-start-marker.sh";
119
122
  var GUARD_RUNNER_REL = ".claude/hooks/guard-runner.sh";
120
123
  var USER_PROMPT_REMINDER_REL = ".claude/hooks/user-prompt-reminder.sh";
121
124
  var USER_PROMPT_SCOPE_REL = ".claude/hooks/user-prompt-scope.sh";
122
125
  var SESSION_END_MARKER_REL = ".claude/hooks/session-end-marker.sh";
123
126
  var WRITE_CAPABLE_TOOLS = ["Edit", "MultiEdit", "NotebookEdit", "Write"];
127
+ var PRE_TOOL_USE_MATCHER = "^(Bash|Read|Grep|Write|Edit|MultiEdit|NotebookEdit|Agent)$";
124
128
  function hookCommand(script, verb) {
125
129
  const invocation = `bash "$CLAUDE_PROJECT_DIR"/${script}`;
126
130
  return verb === void 0 ? invocation : `${invocation} ${verb}`;
@@ -154,6 +158,13 @@ var HALFCYCLE_HOOK_WIRING = [
154
158
  command: hookCommand(USER_PROMPT_SCOPE_REL),
155
159
  script: USER_PROMPT_SCOPE_REL
156
160
  },
161
+ {
162
+ event: "PreToolUse",
163
+ matcher: PRE_TOOL_USE_MATCHER,
164
+ timeout: 10,
165
+ command: hookCommand(GUARD_RUNNER_REL, "hook pre-tool-use"),
166
+ script: GUARD_RUNNER_REL
167
+ },
157
168
  {
158
169
  event: "PostToolUse",
159
170
  matcher: WRITE_CAPABLE_TOOLS.join("|"),
@@ -257,13 +268,69 @@ var REMOTE_FORMS = [
257
268
  var SIGN_IN_COMMAND = '"npx halfcycle"';
258
269
  var HOOK_REMEDY = `Run ${SIGN_IN_COMMAND} in the project folder to sign in.`;
259
270
 
260
- // ../events/dist/result.js
271
+ // ../core/dist/task-plan.js
261
272
  import { z as z2 } from "zod";
262
- var firedGuardSchema = z2.object({
263
- guardId: z2.string(),
264
- patternRef: z2.string(),
273
+ var TASK_PLAN_TASKS_MAX = 500;
274
+ var TASK_PLAN_ID_MAX = 64;
275
+ var TASK_PLAN_KINDS = ["build", "review", "spec", "walk", "check"];
276
+ var CONTROL_CHARACTER = /[\u0000-\u001f\u007f-\u009f]/;
277
+ var planText = z2.string().refine((text) => !CONTROL_CHARACTER.test(text), { message: "holds a control character" });
278
+ var taskIdSchema = z2.string().min(1).max(TASK_PLAN_ID_MAX).refine((text) => !CONTROL_CHARACTER.test(text), { message: "holds a control character" });
279
+ var taskPlanTaskSchema = z2.object({
280
+ id: taskIdSchema,
281
+ kind: z2.enum(TASK_PLAN_KINDS),
282
+ dependsOn: z2.array(taskIdSchema).max(TASK_PLAN_TASKS_MAX),
283
+ reviews: taskIdSchema.optional(),
284
+ heldBy: z2.literal("person").optional(),
285
+ touches: z2.object({
286
+ files: z2.array(planText).optional(),
287
+ interfaces: z2.array(planText).optional()
288
+ }).strict().optional(),
289
+ wholeProject: z2.literal(true).optional(),
290
+ ships: z2.enum(["deploy", "release"]).optional()
291
+ }).strict().superRefine((task, ctx) => {
292
+ if (task.reviews !== void 0 && task.kind !== "review") {
293
+ ctx.addIssue({
294
+ code: "custom",
295
+ path: ["reviews"],
296
+ message: "only a review task names the task it reviews"
297
+ });
298
+ }
299
+ if (task.wholeProject !== void 0 && task.kind !== "check") {
300
+ ctx.addIssue({
301
+ code: "custom",
302
+ path: ["wholeProject"],
303
+ message: "only a check task is a whole-project check"
304
+ });
305
+ }
306
+ if ((task.kind === "review" || task.kind === "check" || task.kind === "walk") && ((task.touches?.files?.length ?? 0) > 0 || (task.touches?.interfaces?.length ?? 0) > 0)) {
307
+ ctx.addIssue({
308
+ code: "custom",
309
+ path: ["touches"],
310
+ message: "a review, check or walk task touches nothing"
311
+ });
312
+ }
313
+ });
314
+ var taskPlanSchema = z2.object({
315
+ phase: z2.string().min(1).refine((text) => !CONTROL_CHARACTER.test(text), { message: "holds a control character" }),
316
+ tasks: z2.array(taskPlanTaskSchema).max(TASK_PLAN_TASKS_MAX)
317
+ }).strict();
318
+ var taskListRowSchema = z2.object({
319
+ id: planText,
320
+ dependsOn: z2.array(planText)
321
+ }).loose();
322
+ var taskListFileSchema = z2.union([
323
+ z2.object({ phase: planText, tasks: z2.array(taskListRowSchema) }).loose(),
324
+ z2.array(z2.unknown())
325
+ ]);
326
+
327
+ // ../events/dist/result.js
328
+ import { z as z3 } from "zod";
329
+ var firedGuardSchema = z3.object({
330
+ guardId: z3.string(),
331
+ patternRef: z3.string(),
265
332
  severity: severitySchema,
266
- explanation: z2.string(),
333
+ explanation: z3.string(),
267
334
  /**
268
335
  * The repo-relative path of the file this firing was decided on. The matcher
269
336
  * has the file live at the moment it decides to fire; this is the field that
@@ -287,66 +354,62 @@ var firedGuardSchema = z2.object({
287
354
  * safe-parse it and decide what to do with a refusal, rather than assume the
288
355
  * two shapes agree.
289
356
  */
290
- path: z2.string().optional()
357
+ path: z3.string().optional()
291
358
  }).strict();
292
- var notRunCheckSchema = z2.object({
359
+ var notRunCheckSchema = z3.object({
293
360
  /** Which check did not run, by the same reference a fired check carries. */
294
- patternRef: z2.string(),
361
+ patternRef: z3.string(),
295
362
  /**
296
363
  * Why it could not run, in plain words — what this client did not send, or
297
364
  * that the check's kind is not implemented yet. Never the check's own rule.
298
365
  */
299
- reason: z2.string()
366
+ reason: z3.string()
300
367
  }).strict();
301
- var resultEnvelopeSchema = z2.object({
302
- guardsFired: z2.array(firedGuardSchema),
368
+ var resultEnvelopeSchema = z3.object({
369
+ guardsFired: z3.array(firedGuardSchema),
303
370
  severity: severitySchema.nullable(),
304
- blocking: z2.boolean(),
305
- explanation: z2.string(),
371
+ blocking: z3.boolean(),
372
+ explanation: z3.string(),
306
373
  /**
307
374
  * The checks that could not run on this edit, each named with the reason.
308
375
  * Absent when every check ran — so an evaluation where nothing was skipped
309
376
  * looks exactly as it always has, and an absent list is never an empty one.
310
377
  */
311
- notRun: z2.array(notRunCheckSchema).optional()
378
+ notRun: z3.array(notRunCheckSchema).optional()
312
379
  }).strict();
313
- var wireErrorSchema = z2.object({
314
- statusCode: z2.number(),
315
- error: z2.string(),
316
- message: z2.string()
380
+ var wireErrorSchema = z3.object({
381
+ statusCode: z3.number(),
382
+ error: z3.string(),
383
+ message: z3.string()
317
384
  }).strict();
318
385
 
319
- // ../events/dist/lone-surrogates.js
320
- var LONE_SURROGATE = new RegExp("\\p{Cs}", "u");
321
- function hasLoneSurrogate(text) {
322
- return LONE_SURROGATE.test(text);
323
- }
324
- var LONE_SURROGATE_MESSAGE = "must be valid text: it holds half of a character pair with no other half";
386
+ // ../events/dist/build-record.js
387
+ import { z as z6 } from "zod";
325
388
 
326
389
  // ../events/dist/state.js
327
- import { z as z4 } from "zod";
390
+ import { z as z5 } from "zod";
328
391
 
329
392
  // ../events/dist/human-agent.js
330
- import { z as z3 } from "zod";
393
+ import { z as z4 } from "zod";
331
394
  var HUMAN_AGENT = "Human";
332
395
  var AGENT_NAME_MAX = 32;
333
- var agentNameSchema = z3.string().min(1).max(AGENT_NAME_MAX);
396
+ var agentNameSchema = z4.string().min(1).max(AGENT_NAME_MAX);
334
397
 
335
398
  // ../events/dist/state.js
336
- var utcIso8601 = z4.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/, "must be a UTC ISO-8601 timestamp ending in Z");
399
+ var utcIso8601 = z5.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/, "must be a UTC ISO-8601 timestamp ending in Z");
337
400
  var STATE_RECORD_FORMAT = "halfcycle-state-record/v1";
338
- var stepIdSchema = z4.string().regex(/^(l[0-5](-l[0-5])?|xl)\.[a-z0-9-]+$/);
339
- var methodVersionSchema = z4.string().regex(RELEASE_VERSION_PATTERN);
401
+ var stepIdSchema = z5.string().regex(/^(l[0-5](-l[0-5])?|xl)\.[a-z0-9-]+$/);
402
+ var methodVersionSchema = z5.string().regex(RELEASE_VERSION_PATTERN);
340
403
  var anchorPathRegex = /^(?!\/)(?!.*#)(?!method\/)(?!(.*\/)?docs\/method\/).+$/;
341
- var recordKindSchema = z4.enum(["artefact", "step-run", "gate", "intervention"]);
342
- var methodLayerSchema = z4.enum(["l0", "l1", "l2", "l3", "l3-l4", "l4", "l5", "xl"]);
404
+ var recordKindSchema = z5.enum(["artefact", "step-run", "gate", "intervention"]);
405
+ var methodLayerSchema = z5.enum(["l0", "l1", "l2", "l3", "l3-l4", "l4", "l5", "xl"]);
343
406
  var PHASE_SCOPED_LAYERS = ["l4", "l5"];
344
407
  function isPhaseScopedLayer(layer) {
345
408
  return PHASE_SCOPED_LAYERS.includes(layer);
346
409
  }
347
- var evidenceKindSchema = z4.enum(["anchor", "diff", "repro", "question-answer", "no-op"]);
348
- var declarableEvidenceKindSchema = z4.enum(["anchor", "diff", "repro", "question-answer"]);
349
- var dispositionSchema = z4.enum([
410
+ var evidenceKindSchema = z5.enum(["anchor", "diff", "repro", "question-answer", "no-op"]);
411
+ var declarableEvidenceKindSchema = z5.enum(["anchor", "diff", "repro", "question-answer"]);
412
+ var dispositionSchema = z5.enum([
350
413
  "prevented",
351
414
  "corrected-in-spec",
352
415
  "corrected-in-scope",
@@ -356,20 +419,20 @@ var dispositionSchema = z4.enum([
356
419
  "surfaced",
357
420
  "filed-forward"
358
421
  ]);
359
- var mechanismSchema = z4.enum(["guard", "verdict", "served-step", "method-step"]);
360
- var artefactStateSchema = z4.enum(["created", "updated"]);
422
+ var mechanismSchema = z5.enum(["guard", "verdict", "served-step", "method-step"]);
423
+ var artefactStateSchema = z5.enum(["created", "updated"]);
361
424
  var QUALIFIED_OUTCOMES = ["declined", "parked"];
362
- var qualifiedOutcomeSchema = z4.enum(QUALIFIED_OUTCOMES);
425
+ var qualifiedOutcomeSchema = z5.enum(QUALIFIED_OUTCOMES);
363
426
  function isQualifiedOutcome(outcome) {
364
427
  return QUALIFIED_OUTCOMES.includes(outcome);
365
428
  }
366
- var runOutcomeSchema = z4.enum([
429
+ var runOutcomeSchema = z5.enum([
367
430
  "completed",
368
431
  "attempted-failed",
369
432
  ...QUALIFIED_OUTCOMES
370
433
  ]);
371
- var gateKindSchema = z4.enum(["approval", "mechanical"]);
372
- var gateVerdictSchema = z4.enum(["pass", "fail", ...QUALIFIED_OUTCOMES]);
434
+ var gateKindSchema = z5.enum(["approval", "mechanical"]);
435
+ var gateVerdictSchema = z5.enum(["pass", "fail", ...QUALIFIED_OUTCOMES]);
373
436
  function outcomeReasonIssue(outcome, reason) {
374
437
  if (isQualifiedOutcome(outcome) && reason === void 0) {
375
438
  return { message: `outcomeReason is required when the outcome is '${outcome}'` };
@@ -381,47 +444,47 @@ function outcomeReasonIssue(outcome, reason) {
381
444
  }
382
445
  return null;
383
446
  }
384
- var anchorEvidenceSchema = z4.object({
385
- kind: z4.literal("anchor"),
386
- path: z4.string().regex(anchorPathRegex),
447
+ var anchorEvidenceSchema = z5.object({
448
+ kind: z5.literal("anchor"),
449
+ path: z5.string().regex(anchorPathRegex),
387
450
  // Optional 1-based line number within the file at `path`. Present when the
388
451
  // writer knows the exact line; omitted — never `0`, never `null` — when it
389
452
  // does not, because a line number is not always obtainable. Absence is a
390
453
  // valid, permanent state here, not a gap meant to be filled in later.
391
- line: z4.number().optional()
454
+ line: z5.number().optional()
392
455
  }).strict();
393
- var diffEvidenceSchema = z4.object({
394
- kind: z4.literal("diff"),
395
- ref: z4.string()
456
+ var diffEvidenceSchema = z5.object({
457
+ kind: z5.literal("diff"),
458
+ ref: z5.string()
396
459
  }).strict();
397
- var reproEvidenceSchema = z4.object({
398
- kind: z4.literal("repro"),
399
- steps: z4.string().max(2e3)
460
+ var reproEvidenceSchema = z5.object({
461
+ kind: z5.literal("repro"),
462
+ steps: z5.string().max(2e3)
400
463
  }).strict();
401
- var questionAnswerEvidenceSchema = z4.object({
402
- kind: z4.literal("question-answer"),
403
- question: z4.string().max(2e3),
404
- before: z4.string().max(2e3),
405
- after: z4.string().max(2e3)
464
+ var questionAnswerEvidenceSchema = z5.object({
465
+ kind: z5.literal("question-answer"),
466
+ question: z5.string().max(2e3),
467
+ before: z5.string().max(2e3),
468
+ after: z5.string().max(2e3)
406
469
  }).strict();
407
- var noOpEvidenceSchema = z4.object({
408
- kind: z4.literal("no-op"),
409
- inspected: z4.string().max(2e3),
410
- unchangedBecause: z4.string().max(2e3)
470
+ var noOpEvidenceSchema = z5.object({
471
+ kind: z5.literal("no-op"),
472
+ inspected: z5.string().max(2e3),
473
+ unchangedBecause: z5.string().max(2e3)
411
474
  }).strict();
412
- var evidenceSchema = z4.discriminatedUnion("kind", [
475
+ var evidenceSchema = z5.discriminatedUnion("kind", [
413
476
  anchorEvidenceSchema,
414
477
  diffEvidenceSchema,
415
478
  reproEvidenceSchema,
416
479
  questionAnswerEvidenceSchema,
417
480
  noOpEvidenceSchema
418
481
  ]);
419
- var stateRecordEnvelopeSchema = z4.object({
420
- format: z4.literal(STATE_RECORD_FORMAT),
482
+ var stateRecordEnvelopeSchema = z5.object({
483
+ format: z5.literal(STATE_RECORD_FORMAT),
421
484
  recordKind: recordKindSchema,
422
- recordId: z4.string().uuid(),
485
+ recordId: z5.string().uuid(),
423
486
  methodVersion: methodVersionSchema,
424
- engagementId: z4.string(),
487
+ engagementId: z5.string(),
425
488
  stepId: stepIdSchema,
426
489
  // Immutable: names when the thing happened, never when a later disposition
427
490
  // moved.
@@ -437,38 +500,38 @@ var stateRecordEnvelopeSchema = z4.object({
437
500
  // for the whole project rather than per phase. Absence is meaningful on its
438
501
  // own — it is never replaced with a placeholder value, and it is read as
439
502
  // "satisfies any phase", not as "unknown".
440
- phase: z4.string().optional()
503
+ phase: z5.string().optional()
441
504
  });
442
- var artefactRefSchema = z4.object({
443
- name: z4.string(),
444
- parent: z4.string()
505
+ var artefactRefSchema = z5.object({
506
+ name: z5.string(),
507
+ parent: z5.string()
445
508
  }).strict();
446
- var coverageSchema = z4.object({
447
- asked: z4.number().int().nonnegative(),
448
- answered: z4.number().int().nonnegative()
509
+ var coverageSchema = z5.object({
510
+ asked: z5.number().int().nonnegative(),
511
+ answered: z5.number().int().nonnegative()
449
512
  }).strict().superRefine((coverage, ctx) => {
450
513
  if (coverage.answered > coverage.asked) {
451
514
  ctx.addIssue({
452
- code: z4.ZodIssueCode.custom,
515
+ code: z5.ZodIssueCode.custom,
453
516
  path: ["answered"],
454
517
  message: "answered cannot be more than asked"
455
518
  });
456
519
  }
457
520
  });
458
- var projectTierSchema = z4.number().int().min(0).max(3);
521
+ var projectTierSchema = z5.number().int().min(0).max(3);
459
522
  var artefactRecordSchema = stateRecordEnvelopeSchema.extend({
460
- recordKind: z4.literal("artefact"),
523
+ recordKind: z5.literal("artefact"),
461
524
  artefactRef: artefactRefSchema,
462
525
  // Some layer wrote it, by construction — an empty array is not a legitimate
463
526
  // state, so the schema says so.
464
- writtenByLayers: z4.array(methodLayerSchema).min(1),
527
+ writtenByLayers: z5.array(methodLayerSchema).min(1),
465
528
  state: artefactStateSchema
466
529
  }).strict();
467
530
  var stepRunRecordSchema = stateRecordEnvelopeSchema.extend({
468
- recordKind: z4.literal("step-run"),
531
+ recordKind: z5.literal("step-run"),
469
532
  runOutcome: runOutcomeSchema,
470
- failureReason: z4.string().optional(),
471
- outcomeReason: z4.string().max(2e3).optional(),
533
+ failureReason: z5.string().optional(),
534
+ outcomeReason: z5.string().max(2e3).optional(),
472
535
  // Who did this step, as the session reported it. Absent when not reported.
473
536
  agent: agentNameSchema.optional(),
474
537
  // When the session began this step. Absent when not reported.
@@ -478,29 +541,29 @@ var stepRunRecordSchema = stateRecordEnvelopeSchema.extend({
478
541
  }).strict().superRefine((rec, ctx) => {
479
542
  if (rec.runOutcome === "attempted-failed" && rec.failureReason === void 0) {
480
543
  ctx.addIssue({
481
- code: z4.ZodIssueCode.custom,
544
+ code: z5.ZodIssueCode.custom,
482
545
  path: ["failureReason"],
483
546
  message: "failureReason is required when runOutcome is 'attempted-failed'"
484
547
  });
485
548
  }
486
549
  if (rec.runOutcome !== "attempted-failed" && rec.failureReason !== void 0) {
487
550
  ctx.addIssue({
488
- code: z4.ZodIssueCode.custom,
551
+ code: z5.ZodIssueCode.custom,
489
552
  path: ["failureReason"],
490
553
  message: "failureReason is only present when runOutcome is 'attempted-failed'"
491
554
  });
492
555
  }
493
556
  const issue = outcomeReasonIssue(rec.runOutcome, rec.outcomeReason);
494
557
  if (issue) {
495
- ctx.addIssue({ code: z4.ZodIssueCode.custom, path: ["outcomeReason"], ...issue });
558
+ ctx.addIssue({ code: z5.ZodIssueCode.custom, path: ["outcomeReason"], ...issue });
496
559
  }
497
560
  });
498
561
  var gateRecordSchema = stateRecordEnvelopeSchema.extend({
499
- recordKind: z4.literal("gate"),
562
+ recordKind: z5.literal("gate"),
500
563
  gateKind: gateKindSchema,
501
564
  verdict: gateVerdictSchema,
502
- actor: z4.string(),
503
- outcomeReason: z4.string().max(2e3).optional(),
565
+ actor: z5.string(),
566
+ outcomeReason: z5.string().max(2e3).optional(),
504
567
  coverage: coverageSchema.optional(),
505
568
  // Who did the step this checkpoint is on, as reported. Absent when not reported.
506
569
  agent: agentNameSchema.optional(),
@@ -510,35 +573,32 @@ var gateRecordSchema = stateRecordEnvelopeSchema.extend({
510
573
  }).strict().superRefine((rec, ctx) => {
511
574
  const issue = outcomeReasonIssue(rec.verdict, rec.outcomeReason);
512
575
  if (issue) {
513
- ctx.addIssue({ code: z4.ZodIssueCode.custom, path: ["outcomeReason"], ...issue });
576
+ ctx.addIssue({ code: z5.ZodIssueCode.custom, path: ["outcomeReason"], ...issue });
514
577
  }
515
578
  });
516
579
  var interventionRecordSchema = stateRecordEnvelopeSchema.extend({
517
- recordKind: z4.literal("intervention"),
580
+ recordKind: z5.literal("intervention"),
518
581
  layer: methodLayerSchema,
519
582
  mechanism: mechanismSchema,
520
583
  severity: severitySchema,
521
584
  disposition: dispositionSchema,
522
- summary: z4.string().max(2e3),
585
+ summary: z5.string().max(2e3),
523
586
  coverage: coverageSchema.optional(),
524
587
  // Who caught it, as the session reported. Absent when not reported.
525
588
  agent: agentNameSchema.optional()
526
589
  }).strict();
527
- var stateRecordSchema = z4.discriminatedUnion("recordKind", [
590
+ var stateRecordSchema = z5.discriminatedUnion("recordKind", [
528
591
  artefactRecordSchema,
529
592
  stepRunRecordSchema,
530
593
  gateRecordSchema,
531
594
  interventionRecordSchema
532
595
  ]);
533
596
 
534
- // ../events/dist/build-record.js
535
- import { z as z5 } from "zod";
536
-
537
597
  // ../events/dist/control-characters.js
538
598
  var CONTROL_CHARACTER_CLASS = "\\p{Cc}";
539
- var CONTROL_CHARACTER = new RegExp(CONTROL_CHARACTER_CLASS, "u");
599
+ var CONTROL_CHARACTER2 = new RegExp(CONTROL_CHARACTER_CLASS, "u");
540
600
  function hasControlCharacter(text) {
541
- return CONTROL_CHARACTER.test(text);
601
+ return CONTROL_CHARACTER2.test(text);
542
602
  }
543
603
 
544
604
  // ../events/dist/phase-identity.js
@@ -613,6 +673,13 @@ function describeIdentity(identity) {
613
673
  return `(a ${typeof identity})`;
614
674
  }
615
675
 
676
+ // ../events/dist/lone-surrogates.js
677
+ var LONE_SURROGATE = new RegExp("\\p{Cs}", "u");
678
+ function hasLoneSurrogate(text) {
679
+ return LONE_SURROGATE.test(text);
680
+ }
681
+ var LONE_SURROGATE_MESSAGE = "must be valid text: it holds half of a character pair with no other half";
682
+
616
683
  // ../events/dist/build-record.js
617
684
  var BUILD_RECORD_V2_FORMAT = "halfcycle-build-record/v2";
618
685
  var STATEMENT_MAX = 2e3;
@@ -622,16 +689,16 @@ var ACTOR_MAX = 200;
622
689
  var FINDING_MAX = 500;
623
690
  var FINDINGS_SHOWN_MAX = 50;
624
691
  var PHASE_CLOSE_REASON_MAX = 1900;
625
- var closeCommitSchema = z5.string().regex(/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/, "must be a full commit hash: 40 or 64 lowercase hex characters");
626
- var walkVerdictSchema = z5.enum(["clean", "defects"]);
627
- var acceptanceOutcomeSchema = z5.object({
692
+ var closeCommitSchema = z6.string().regex(/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/, "must be a full commit hash: 40 or 64 lowercase hex characters");
693
+ var walkVerdictSchema = z6.enum(["clean", "defects"]);
694
+ var acceptanceOutcomeSchema = z6.object({
628
695
  verdict: walkVerdictSchema,
629
- findings: z5.number().int().nonnegative(),
630
- findingsDetail: z5.array(z5.object({ summary: z5.string().refine((v) => !hasLoneSurrogate(v), { message: LONE_SURROGATE_MESSAGE }) }).strict())
696
+ findings: z6.number().int().nonnegative(),
697
+ findingsDetail: z6.array(z6.object({ summary: z6.string().refine((v) => !hasLoneSurrogate(v), { message: LONE_SURROGATE_MESSAGE }) }).strict())
631
698
  }).strict();
632
- var closeDecisionSchema = z5.enum(["done-when-met", "override"]);
699
+ var closeDecisionSchema = z6.enum(["done-when-met", "override"]);
633
700
  function plainText(max) {
634
- const base = max === void 0 ? z5.string() : z5.string().max(max);
701
+ const base = max === void 0 ? z6.string() : z6.string().max(max);
635
702
  return base.refine((v) => !hasControlCharacter(v), {
636
703
  message: "must be one line with no control characters (no line breaks, tabs or NUL)"
637
704
  }).refine((v) => !hasLoneSurrogate(v), { message: LONE_SURROGATE_MESSAGE });
@@ -643,81 +710,81 @@ function statementText(max) {
643
710
  }
644
711
  var closeActorSchema = plainText(ACTOR_MAX);
645
712
  var PHASE_NAME_RULE = `must be a phase name: not empty, no slash or backslash, not starting with a dot, no control characters, at most ${PHASE_NAME_MAX_BYTES} bytes`;
646
- var phaseNameSchema = z5.string().refine((v) => isValidPhaseIdentity(v), {
713
+ var phaseNameSchema = z6.string().refine((v) => isValidPhaseIdentity(v), {
647
714
  message: PHASE_NAME_RULE
648
715
  });
649
- var phaseIdentitySchema = z5.union([z5.string(), z5.number()], { error: "must be a phase name or a whole number" }).refine((v) => isValidPhaseIdentity(v), { message: PHASE_NAME_RULE });
650
- var closeStatementsSchema = z5.object({
716
+ var phaseIdentitySchema = z6.union([z6.string(), z6.number()], { error: "must be a phase name or a whole number" }).refine((v) => isValidPhaseIdentity(v), { message: PHASE_NAME_RULE });
717
+ var closeStatementsSchema = z6.object({
651
718
  thesis: statementText(STATEMENT_MAX).nullable(),
652
719
  outcome: statementText(STATEMENT_MAX).nullable(),
653
- deviations: z5.array(statementText(DEVIATION_MAX)).max(DEVIATIONS_MAX),
720
+ deviations: z6.array(statementText(DEVIATION_MAX)).max(DEVIATIONS_MAX),
654
721
  closeCommit: closeCommitSchema.nullable()
655
722
  }).strict();
656
- var closedBySchema = z5.discriminatedUnion("kind", [
657
- z5.object({ kind: z5.literal("account"), email: plainText().nullable() }).strict(),
658
- z5.object({ kind: z5.literal("no-account") }).strict(),
659
- z5.object({ kind: z5.literal("account-removed") }).strict(),
660
- z5.object({ kind: z5.literal("not-recorded") }).strict()
723
+ var closedBySchema = z6.discriminatedUnion("kind", [
724
+ z6.object({ kind: z6.literal("account"), email: plainText().nullable() }).strict(),
725
+ z6.object({ kind: z6.literal("no-account") }).strict(),
726
+ z6.object({ kind: z6.literal("account-removed") }).strict(),
727
+ z6.object({ kind: z6.literal("not-recorded") }).strict()
661
728
  ]);
662
729
  var STEP_NAME_MAX = 200;
663
730
  var stepNameSchema = plainText(STEP_NAME_MAX).refine((v) => v.trim().length > 0, {
664
731
  message: "must not be blank"
665
732
  });
666
- var unfinishedStepSchema = z5.object({
733
+ var unfinishedStepSchema = z6.object({
667
734
  stepId: plainText(),
668
- state: z5.enum(["parked", "failed"]),
735
+ state: z6.enum(["parked", "failed"]),
669
736
  name: stepNameSchema.optional()
670
737
  }).strict();
671
- var crossedSchema = z5.discriminatedUnion("kind", [
672
- z5.object({ kind: z5.literal("none") }).strict(),
673
- z5.object({ kind: z5.literal("unfinished-steps"), steps: z5.array(unfinishedStepSchema).min(1) }).strict(),
674
- z5.object({ kind: z5.literal("clean-over-findings"), findings: z5.number().int().min(1) }).strict(),
675
- z5.object({ kind: z5.literal("not-recorded") }).strict()
738
+ var crossedSchema = z6.discriminatedUnion("kind", [
739
+ z6.object({ kind: z6.literal("none") }).strict(),
740
+ z6.object({ kind: z6.literal("unfinished-steps"), steps: z6.array(unfinishedStepSchema).min(1) }).strict(),
741
+ z6.object({ kind: z6.literal("clean-over-findings"), findings: z6.number().int().min(1) }).strict(),
742
+ z6.object({ kind: z6.literal("not-recorded") }).strict()
676
743
  ]);
677
- var seatCoverageSchema = z5.object({
678
- enrolled: z5.number().int().nonnegative(),
679
- conformant: z5.number().int().nonnegative()
744
+ var seatCoverageSchema = z6.object({
745
+ enrolled: z6.number().int().nonnegative(),
746
+ conformant: z6.number().int().nonnegative()
680
747
  }).strict().refine((c) => c.conformant <= c.enrolled, {
681
748
  message: "conformant cannot exceed enrolled",
682
749
  path: ["conformant"]
683
750
  });
684
- var observedCloseSchema = z5.object({
685
- sequence: z5.number().int().min(1),
751
+ var observedCloseSchema = z6.object({
752
+ sequence: z6.number().int().min(1),
686
753
  closedAt: utcIso8601,
687
754
  closedBy: closedBySchema,
688
755
  crossed: crossedSchema,
689
756
  seatCoverage: seatCoverageSchema.nullable()
690
757
  }).strict();
691
- var couldNotRunCheckSchema = z5.object({
758
+ var couldNotRunCheckSchema = z6.object({
692
759
  check: plainText(),
693
760
  description: plainText().nullable(),
694
761
  reason: plainText()
695
762
  }).strict();
696
- var firedCheckSchema = z5.object({
763
+ var firedCheckSchema = z6.object({
697
764
  check: plainText(),
698
765
  description: plainText().nullable(),
699
766
  severity: severitySchema,
700
767
  explanation: plainText().nullable(),
701
- occurrences: z5.number().int().min(1)
768
+ occurrences: z6.number().int().min(1)
702
769
  }).strict();
703
- var phaseChecksSchema = z5.discriminatedUnion("state", [
704
- z5.object({ state: z5.literal("not-observed") }).strict(),
705
- z5.object({
706
- state: z5.literal("nothing-fired"),
707
- runs: z5.number().int().min(1),
708
- couldNotRun: z5.array(couldNotRunCheckSchema)
770
+ var phaseChecksSchema = z6.discriminatedUnion("state", [
771
+ z6.object({ state: z6.literal("not-observed") }).strict(),
772
+ z6.object({
773
+ state: z6.literal("nothing-fired"),
774
+ runs: z6.number().int().min(1),
775
+ couldNotRun: z6.array(couldNotRunCheckSchema)
709
776
  }).strict(),
710
- z5.object({
711
- state: z5.literal("fired"),
712
- runs: z5.number().int().min(1),
713
- couldNotRun: z5.array(couldNotRunCheckSchema),
714
- fired: z5.array(firedCheckSchema).min(1)
777
+ z6.object({
778
+ state: z6.literal("fired"),
779
+ runs: z6.number().int().min(1),
780
+ couldNotRun: z6.array(couldNotRunCheckSchema),
781
+ fired: z6.array(firedCheckSchema).min(1)
715
782
  }).strict()
716
783
  ]);
717
- var observedSchema = z5.object({
784
+ var observedSchema = z6.object({
718
785
  engagementId: plainText(),
719
786
  methodVersion: methodVersionSchema,
720
- closes: z5.array(observedCloseSchema).min(1),
787
+ closes: z6.array(observedCloseSchema).min(1),
721
788
  checks: phaseChecksSchema
722
789
  }).strict().superRefine((o, ctx) => {
723
790
  o.closes.forEach((close, i) => {
@@ -730,18 +797,18 @@ var observedSchema = z5.object({
730
797
  }
731
798
  });
732
799
  });
733
- var attestedWalkSchema = z5.object({
800
+ var attestedWalkSchema = z6.object({
734
801
  verdict: walkVerdictSchema,
735
- findings: z5.array(plainText(FINDING_MAX)).max(FINDINGS_SHOWN_MAX),
736
- findingsNotShown: z5.number().int().nonnegative()
802
+ findings: z6.array(plainText(FINDING_MAX)).max(FINDINGS_SHOWN_MAX),
803
+ findingsNotShown: z6.number().int().nonnegative()
737
804
  }).strict();
738
- var attestedStatementsSchema = z5.object({
805
+ var attestedStatementsSchema = z6.object({
739
806
  thesis: plainText(STATEMENT_MAX).nullable(),
740
807
  outcome: plainText(STATEMENT_MAX).nullable(),
741
- deviations: z5.array(plainText(DEVIATION_MAX)).max(DEVIATIONS_MAX)
808
+ deviations: z6.array(plainText(DEVIATION_MAX)).max(DEVIATIONS_MAX)
742
809
  }).strict();
743
- var attestedCloseSchema = z5.object({
744
- sequence: z5.number().int().min(1),
810
+ var attestedCloseSchema = z6.object({
811
+ sequence: z6.number().int().min(1),
745
812
  decision: closeDecisionSchema,
746
813
  actor: plainText(ACTOR_MAX).nullable(),
747
814
  reason: plainText(PHASE_CLOSE_REASON_MAX).nullable(),
@@ -749,8 +816,8 @@ var attestedCloseSchema = z5.object({
749
816
  closeCommit: closeCommitSchema.nullable(),
750
817
  statements: attestedStatementsSchema
751
818
  }).strict();
752
- var attestedSchema = z5.object({
753
- closes: z5.array(attestedCloseSchema)
819
+ var attestedSchema = z6.object({
820
+ closes: z6.array(attestedCloseSchema)
754
821
  }).strict();
755
822
  function checkRecordHalves(record2, ctx) {
756
823
  const observedBySequence = new Map(record2.observed.closes.map((c) => [c.sequence, c]));
@@ -781,362 +848,112 @@ function checkRecordHalves(record2, ctx) {
781
848
  }
782
849
  });
783
850
  }
784
- var buildRecordV2Schema = z5.object({
785
- format: z5.literal(BUILD_RECORD_V2_FORMAT),
851
+ var buildRecordV2Schema = z6.object({
852
+ format: z6.literal(BUILD_RECORD_V2_FORMAT),
786
853
  phase: phaseNameSchema,
787
854
  observed: observedSchema,
788
855
  attested: attestedSchema
789
856
  }).strict().superRefine(checkRecordHalves);
790
857
 
791
- // ../events/dist/crew.js
792
- import { z as z6 } from "zod";
793
- var crewGroupSchema = z6.enum(["analyst-architect", "builder", "reviewer", "lead"]);
794
- var crewTierSchema = z6.enum(["deep", "standard", "fast"]);
795
- var CREW_DISCIPLINE_MAX = 64;
796
- var crewFaceHeadSchema = z6.enum(["sq", "sqr", "rnd", "dome", "hex", "gable"]);
797
- var crewFaceAntennaSchema = z6.enum(["none", "stalk", "twin", "dish"]);
798
- var crewFaceEyesSchema = z6.enum(["dots", "bars", "visor", "cyclops"]);
799
- var crewFaceMouthSchema = z6.enum(["line", "grid", "dots", "wave"]);
800
- var crewFaceSchema = z6.object({
801
- // The outline of the head. Members who do the same kind of work share one, so a
802
- // team is recognisable before any name is read.
803
- head: crewFaceHeadSchema,
804
- // What sits on top of the head, if anything.
805
- antenna: crewFaceAntennaSchema,
806
- // The eyes.
807
- eyes: crewFaceEyesSchema,
808
- // The mouth.
809
- mouth: crewFaceMouthSchema,
810
- // Whether this member is drawn with ears.
811
- ears: z6.boolean()
858
+ // ../events/dist/build-snapshot.js
859
+ import { z as z8 } from "zod";
860
+
861
+ // ../events/dist/system-description.js
862
+ import { z as z7 } from "zod";
863
+ var SYSTEM_DESCRIPTION_FORMAT = 1;
864
+ var SYSTEM_PARTS_MAX = 60;
865
+ var SYSTEM_CONNECTIONS_MAX = 400;
866
+ var SYSTEM_SLOT_MAX = 11;
867
+ var SYSTEM_OWNS_MAX = 20;
868
+ var SYSTEM_OWNED_PATTERN_MAX = 200;
869
+ var SYSTEM_PART_NAME_MAX = 60;
870
+ var SYSTEM_PART_DESCRIPTION_MAX = 160;
871
+ var SYSTEM_CONNECTION_LABEL_MAX = 60;
872
+ var SYSTEM_PART_ID_PATTERN = /^[a-z][a-z0-9-]{0,39}$/;
873
+ var SYSTEM_LANES = ["clients", "edge", "services", "data", "external"];
874
+ var systemLaneSchema = z7.enum(SYSTEM_LANES, {
875
+ error: `must be one of ${SYSTEM_LANES.join(", ")}`
876
+ });
877
+ var SYSTEM_CONNECTION_KINDS = ["call", "event", "write", "external"];
878
+ var systemConnectionKindSchema = z7.enum(SYSTEM_CONNECTION_KINDS, {
879
+ error: `must be one of ${SYSTEM_CONNECTION_KINDS.join(", ")}`
880
+ });
881
+ var LINE_SEPARATOR = /[\u2028\u2029]/;
882
+ function descriptionText(max) {
883
+ return statementText(max).refine((v) => v.trim().length > 0, { message: "must not be empty or only spaces" }).refine((v) => !LINE_SEPARATOR.test(v), {
884
+ message: "must be one line: it holds a line or paragraph separator"
885
+ });
886
+ }
887
+ var systemPartIdSchema = z7.string().regex(SYSTEM_PART_ID_PATTERN, {
888
+ message: "must be a short id: a lowercase letter, then lowercase letters, digits or hyphens, 40 characters at most"
889
+ }).refine((v) => !looksLikeSecret(v), { message: "looks like a password or key; choose another id" });
890
+ function hasEmptySegment(pattern) {
891
+ return pattern.split("/").some((segment) => segment.length === 0);
892
+ }
893
+ function hasDotSegment(pattern) {
894
+ return pattern.split("/").some((segment) => segment === "." || segment === "..");
895
+ }
896
+ function hasPartialDoubleStar(pattern) {
897
+ return pattern.split("/").some((segment) => segment.includes("**") && segment !== "**");
898
+ }
899
+ var SEGMENT_CHARACTERS = /^[A-Za-z0-9._\-*]*$/;
900
+ function hasForeignCharacter(pattern) {
901
+ return pattern.split("/").some((segment) => !SEGMENT_CHARACTERS.test(segment));
902
+ }
903
+ var systemOwnedPatternSchema = z7.string().max(SYSTEM_OWNED_PATTERN_MAX, { message: `must be at most ${SYSTEM_OWNED_PATTERN_MAX} characters` }).refine((v) => !hasEmptySegment(v), {
904
+ message: "must not start or end with a slash, or have two slashes together"
905
+ }).refine((v) => !hasDotSegment(v), { message: 'must not have a "." or ".." folder in it' }).refine((v) => !hasPartialDoubleStar(v), { message: '"**" must stand alone between slashes' }).refine((v) => !hasForeignCharacter(v), {
906
+ message: 'may use only letters, digits, ".", "_", "-" and "*" between slashes'
907
+ }).refine((v) => !looksLikeSecret(v), {
908
+ message: "this path looks like a credential; list a shorter folder above it"
909
+ });
910
+ var systemPartSchema = z7.object({
911
+ id: systemPartIdSchema,
912
+ name: descriptionText(SYSTEM_PART_NAME_MAX),
913
+ description: descriptionText(SYSTEM_PART_DESCRIPTION_MAX),
914
+ lane: systemLaneSchema,
915
+ slot: z7.number({ error: `must be a whole number from 0 to ${SYSTEM_SLOT_MAX}` }).int({ error: `must be a whole number from 0 to ${SYSTEM_SLOT_MAX}` }).min(0, { error: `must be a whole number from 0 to ${SYSTEM_SLOT_MAX}` }).max(SYSTEM_SLOT_MAX, { error: `must be a whole number from 0 to ${SYSTEM_SLOT_MAX}` }),
916
+ outside: z7.boolean(),
917
+ owns: z7.array(systemOwnedPatternSchema).max(SYSTEM_OWNS_MAX, { message: `can list at most ${SYSTEM_OWNS_MAX} paths` })
812
918
  }).strict();
813
- var crewRoleSchema = z6.enum([
814
- "business-analyst",
815
- "architect",
816
- "spec-writer",
817
- "builder",
818
- "context-docs",
819
- "spec-reviewer",
820
- "code-test-reviewer",
821
- "delivery-lead",
822
- "quality-lead"
919
+ var systemConnectionSchema = z7.object({
920
+ from: systemPartIdSchema,
921
+ to: systemPartIdSchema,
922
+ kind: systemConnectionKindSchema,
923
+ label: descriptionText(SYSTEM_CONNECTION_LABEL_MAX)
924
+ }).strict();
925
+ var systemDescriptionSchema = z7.object({
926
+ format: z7.literal(SYSTEM_DESCRIPTION_FORMAT, { error: `must be ${SYSTEM_DESCRIPTION_FORMAT}` }),
927
+ parts: z7.array(systemPartSchema).min(1, { message: "must list at least one part" }).max(SYSTEM_PARTS_MAX, { message: `can hold at most ${SYSTEM_PARTS_MAX} parts (five lanes of twelve slots)` }),
928
+ connections: z7.array(systemConnectionSchema).max(SYSTEM_CONNECTIONS_MAX, { message: `can hold at most ${SYSTEM_CONNECTIONS_MAX} connections` })
929
+ }).strict();
930
+ var systemDescriptionProblemSchema = z7.object({
931
+ path: z7.string(),
932
+ message: z7.string()
933
+ }).strict();
934
+ var systemReportRecordedSchema = z7.object({
935
+ outcome: z7.literal("recorded"),
936
+ version: z7.number().int().min(1),
937
+ reportedAt: utcIso8601
938
+ }).strict();
939
+ var systemReportRefusedSchema = z7.object({
940
+ outcome: z7.literal("refused"),
941
+ problems: z7.array(systemDescriptionProblemSchema).min(1)
942
+ }).strict();
943
+ var systemReportUnavailableSchema = z7.object({
944
+ outcome: z7.literal("unavailable")
945
+ }).strict();
946
+ var systemReportResultSchema = z7.discriminatedUnion("outcome", [
947
+ systemReportRecordedSchema,
948
+ systemReportRefusedSchema,
949
+ systemReportUnavailableSchema
823
950
  ]);
824
- var crewMemberSchema = z6.object({
825
- // The agent's name. The same names on every project, so a name is a
826
- // vocabulary a returning client already knows.
827
- callsign: z6.string().min(1).max(32),
828
- // Which of the four teams this member belongs to.
829
- group: crewGroupSchema,
830
- // The part this member plays, which decides the stages it leads and helps with.
831
- role: crewRoleSchema,
832
- // The kind of work this member takes. A task names a discipline, and only
833
- // agents holding it can be dispatched to it.
834
- discipline: z6.string().min(1).max(CREW_DISCIPLINE_MAX),
835
- // How much thinking this member brings. There is no field naming the supplier
836
- // behind it, on this shape or on any other.
837
- tier: crewTierSchema,
838
- // How this member's face is drawn. The same face on every project, so a
839
- // returning client recognises a member before reading the name.
840
- face: crewFaceSchema
841
- }).strict();
842
- var CREW_ROSTER = [
843
- // ── analysts and architects ─────────────────────────────────────────────────
844
- {
845
- callsign: "SOFIA",
846
- group: "analyst-architect",
847
- role: "business-analyst",
848
- discipline: "business analyst",
849
- tier: "deep",
850
- face: { head: "gable", antenna: "stalk", eyes: "dots", mouth: "wave", ears: false }
851
- },
852
- {
853
- callsign: "BEA",
854
- group: "analyst-architect",
855
- role: "business-analyst",
856
- discipline: "business analyst",
857
- tier: "standard",
858
- face: { head: "gable", antenna: "none", eyes: "visor", mouth: "line", ears: false }
859
- },
860
- {
861
- callsign: "WREN",
862
- group: "analyst-architect",
863
- role: "architect",
864
- discipline: "solution architect",
865
- tier: "deep",
866
- face: { head: "gable", antenna: "dish", eyes: "bars", mouth: "line", ears: true }
867
- },
868
- {
869
- callsign: "PEI",
870
- group: "analyst-architect",
871
- role: "architect",
872
- discipline: "technical architect",
873
- tier: "standard",
874
- face: { head: "gable", antenna: "twin", eyes: "cyclops", mouth: "dots", ears: false }
875
- },
876
- // ── builders: code ──────────────────────────────────────────────────────────
877
- {
878
- callsign: "ADA",
879
- group: "builder",
880
- role: "builder",
881
- discipline: "implementation",
882
- tier: "deep",
883
- face: { head: "sq", antenna: "stalk", eyes: "dots", mouth: "line", ears: false }
884
- },
885
- {
886
- callsign: "OTTO",
887
- group: "builder",
888
- role: "builder",
889
- discipline: "implementation",
890
- tier: "deep",
891
- face: { head: "sq", antenna: "twin", eyes: "bars", mouth: "grid", ears: true }
892
- },
893
- {
894
- callsign: "MILO",
895
- group: "builder",
896
- role: "builder",
897
- discipline: "implementation",
898
- tier: "standard",
899
- face: { head: "sqr", antenna: "stalk", eyes: "dots", mouth: "dots", ears: false }
900
- },
901
- {
902
- callsign: "NOVA",
903
- group: "builder",
904
- role: "builder",
905
- discipline: "implementation",
906
- tier: "standard",
907
- face: { head: "sq", antenna: "dish", eyes: "cyclops", mouth: "line", ears: false }
908
- },
909
- {
910
- callsign: "HUGO",
911
- group: "builder",
912
- role: "builder",
913
- discipline: "infrastructure",
914
- tier: "standard",
915
- face: { head: "sq", antenna: "twin", eyes: "dots", mouth: "grid", ears: true }
916
- },
917
- // ── builders: specs and docs ────────────────────────────────────────────────
918
- {
919
- callsign: "IRIS",
920
- group: "builder",
921
- role: "spec-writer",
922
- discipline: "feature spec",
923
- tier: "deep",
924
- face: { head: "dome", antenna: "stalk", eyes: "visor", mouth: "line", ears: false }
925
- },
926
- {
927
- callsign: "JUNO",
928
- group: "builder",
929
- role: "spec-writer",
930
- discipline: "feature spec",
931
- tier: "standard",
932
- face: { head: "dome", antenna: "none", eyes: "dots", mouth: "wave", ears: false }
933
- },
934
- {
935
- callsign: "LEX",
936
- group: "builder",
937
- role: "context-docs",
938
- discipline: "context & docs",
939
- tier: "fast",
940
- face: { head: "dome", antenna: "twin", eyes: "bars", mouth: "dots", ears: false }
941
- },
942
- // ── reviewers ───────────────────────────────────────────────────────────────
943
- {
944
- callsign: "VERA",
945
- group: "reviewer",
946
- role: "spec-reviewer",
947
- discipline: "spec consistency",
948
- tier: "deep",
949
- face: { head: "rnd", antenna: "stalk", eyes: "visor", mouth: "line", ears: false }
950
- },
951
- {
952
- callsign: "ODIN",
953
- group: "reviewer",
954
- role: "spec-reviewer",
955
- discipline: "cross-spec audit",
956
- tier: "deep",
957
- face: { head: "rnd", antenna: "dish", eyes: "cyclops", mouth: "line", ears: false }
958
- },
959
- {
960
- callsign: "CASS",
961
- group: "reviewer",
962
- role: "code-test-reviewer",
963
- discipline: "code review",
964
- tier: "deep",
965
- face: { head: "rnd", antenna: "none", eyes: "visor", mouth: "grid", ears: true }
966
- },
967
- {
968
- callsign: "THEO",
969
- group: "reviewer",
970
- role: "code-test-reviewer",
971
- discipline: "code review",
972
- tier: "standard",
973
- face: { head: "rnd", antenna: "twin", eyes: "bars", mouth: "line", ears: false }
974
- },
975
- {
976
- callsign: "KAI",
977
- group: "reviewer",
978
- role: "code-test-reviewer",
979
- discipline: "code review",
980
- tier: "standard",
981
- face: { head: "rnd", antenna: "none", eyes: "bars", mouth: "line", ears: true }
982
- },
983
- {
984
- callsign: "ECHO",
985
- group: "reviewer",
986
- role: "code-test-reviewer",
987
- discipline: "test quality",
988
- tier: "standard",
989
- face: { head: "rnd", antenna: "stalk", eyes: "dots", mouth: "wave", ears: false }
990
- },
991
- {
992
- callsign: "MAVIS",
993
- group: "reviewer",
994
- role: "code-test-reviewer",
995
- discipline: "suites & coverage",
996
- tier: "standard",
997
- face: { head: "rnd", antenna: "none", eyes: "visor", mouth: "dots", ears: false }
998
- },
999
- {
1000
- callsign: "ZARA",
1001
- group: "reviewer",
1002
- role: "code-test-reviewer",
1003
- discipline: "smoke & walk aid",
1004
- tier: "standard",
1005
- face: { head: "rnd", antenna: "dish", eyes: "bars", mouth: "wave", ears: false }
1006
- },
1007
- {
1008
- callsign: "NORA",
1009
- group: "reviewer",
1010
- role: "code-test-reviewer",
1011
- discipline: "evidence & provenance",
1012
- tier: "fast",
1013
- face: { head: "rnd", antenna: "twin", eyes: "dots", mouth: "line", ears: false }
1014
- },
1015
- // ── leads ───────────────────────────────────────────────────────────────────
1016
- {
1017
- callsign: "ARGUS",
1018
- group: "lead",
1019
- role: "quality-lead",
1020
- discipline: "quality lead \xB7 every diff",
1021
- tier: "standard",
1022
- face: { head: "hex", antenna: "dish", eyes: "visor", mouth: "grid", ears: true }
1023
- },
1024
- {
1025
- callsign: "ATLAS",
1026
- group: "lead",
1027
- role: "delivery-lead",
1028
- discipline: "delivery lead",
1029
- tier: "deep",
1030
- face: { head: "hex", antenna: "twin", eyes: "cyclops", mouth: "line", ears: true }
1031
- }
1032
- ];
1033
- var CREW_ROSTER_SIZE = CREW_ROSTER.length;
1034
- var CREW_CALLSIGNS = CREW_ROSTER.map((member) => member.callsign);
1035
- var crewLayerSchema = methodLayerSchema.extract(["l0", "l1", "l2", "l3", "l4", "l5"]);
1036
- var crewRoleLayersSchema = z6.object({
1037
- leads: z6.array(crewLayerSchema),
1038
- helps: z6.array(crewLayerSchema)
1039
- }).strict();
1040
-
1041
- // ../events/dist/build-snapshot.js
1042
- import { z as z8 } from "zod";
1043
-
1044
- // ../events/dist/system-description.js
1045
- import { z as z7 } from "zod";
1046
- var SYSTEM_DESCRIPTION_FORMAT = 1;
1047
- var SYSTEM_PARTS_MAX = 60;
1048
- var SYSTEM_CONNECTIONS_MAX = 400;
1049
- var SYSTEM_SLOT_MAX = 11;
1050
- var SYSTEM_OWNS_MAX = 20;
1051
- var SYSTEM_OWNED_PATTERN_MAX = 200;
1052
- var SYSTEM_PART_NAME_MAX = 60;
1053
- var SYSTEM_PART_DESCRIPTION_MAX = 160;
1054
- var SYSTEM_CONNECTION_LABEL_MAX = 60;
1055
- var SYSTEM_PART_ID_PATTERN = /^[a-z][a-z0-9-]{0,39}$/;
1056
- var SYSTEM_LANES = ["clients", "edge", "services", "data", "external"];
1057
- var systemLaneSchema = z7.enum(SYSTEM_LANES, {
1058
- error: `must be one of ${SYSTEM_LANES.join(", ")}`
1059
- });
1060
- var SYSTEM_CONNECTION_KINDS = ["call", "event", "write", "external"];
1061
- var systemConnectionKindSchema = z7.enum(SYSTEM_CONNECTION_KINDS, {
1062
- error: `must be one of ${SYSTEM_CONNECTION_KINDS.join(", ")}`
1063
- });
1064
- var LINE_SEPARATOR = /[\u2028\u2029]/;
1065
- function descriptionText(max) {
1066
- return statementText(max).refine((v) => v.trim().length > 0, { message: "must not be empty or only spaces" }).refine((v) => !LINE_SEPARATOR.test(v), {
1067
- message: "must be one line: it holds a line or paragraph separator"
1068
- });
1069
- }
1070
- var systemPartIdSchema = z7.string().regex(SYSTEM_PART_ID_PATTERN, {
1071
- message: "must be a short id: a lowercase letter, then lowercase letters, digits or hyphens, 40 characters at most"
1072
- }).refine((v) => !looksLikeSecret(v), { message: "looks like a password or key; choose another id" });
1073
- function hasEmptySegment(pattern) {
1074
- return pattern.split("/").some((segment) => segment.length === 0);
1075
- }
1076
- function hasDotSegment(pattern) {
1077
- return pattern.split("/").some((segment) => segment === "." || segment === "..");
1078
- }
1079
- function hasPartialDoubleStar(pattern) {
1080
- return pattern.split("/").some((segment) => segment.includes("**") && segment !== "**");
1081
- }
1082
- var SEGMENT_CHARACTERS = /^[A-Za-z0-9._\-*]*$/;
1083
- function hasForeignCharacter(pattern) {
1084
- return pattern.split("/").some((segment) => !SEGMENT_CHARACTERS.test(segment));
1085
- }
1086
- var systemOwnedPatternSchema = z7.string().max(SYSTEM_OWNED_PATTERN_MAX, { message: `must be at most ${SYSTEM_OWNED_PATTERN_MAX} characters` }).refine((v) => !hasEmptySegment(v), {
1087
- message: "must not start or end with a slash, or have two slashes together"
1088
- }).refine((v) => !hasDotSegment(v), { message: 'must not have a "." or ".." folder in it' }).refine((v) => !hasPartialDoubleStar(v), { message: '"**" must stand alone between slashes' }).refine((v) => !hasForeignCharacter(v), {
1089
- message: 'may use only letters, digits, ".", "_", "-" and "*" between slashes'
1090
- }).refine((v) => !looksLikeSecret(v), {
1091
- message: "this path looks like a credential; list a shorter folder above it"
1092
- });
1093
- var systemPartSchema = z7.object({
1094
- id: systemPartIdSchema,
1095
- name: descriptionText(SYSTEM_PART_NAME_MAX),
1096
- description: descriptionText(SYSTEM_PART_DESCRIPTION_MAX),
1097
- lane: systemLaneSchema,
1098
- slot: z7.number({ error: `must be a whole number from 0 to ${SYSTEM_SLOT_MAX}` }).int({ error: `must be a whole number from 0 to ${SYSTEM_SLOT_MAX}` }).min(0, { error: `must be a whole number from 0 to ${SYSTEM_SLOT_MAX}` }).max(SYSTEM_SLOT_MAX, { error: `must be a whole number from 0 to ${SYSTEM_SLOT_MAX}` }),
1099
- outside: z7.boolean(),
1100
- owns: z7.array(systemOwnedPatternSchema).max(SYSTEM_OWNS_MAX, { message: `can list at most ${SYSTEM_OWNS_MAX} paths` })
1101
- }).strict();
1102
- var systemConnectionSchema = z7.object({
1103
- from: systemPartIdSchema,
1104
- to: systemPartIdSchema,
1105
- kind: systemConnectionKindSchema,
1106
- label: descriptionText(SYSTEM_CONNECTION_LABEL_MAX)
1107
- }).strict();
1108
- var systemDescriptionSchema = z7.object({
1109
- format: z7.literal(SYSTEM_DESCRIPTION_FORMAT, { error: `must be ${SYSTEM_DESCRIPTION_FORMAT}` }),
1110
- parts: z7.array(systemPartSchema).min(1, { message: "must list at least one part" }).max(SYSTEM_PARTS_MAX, { message: `can hold at most ${SYSTEM_PARTS_MAX} parts (five lanes of twelve slots)` }),
1111
- connections: z7.array(systemConnectionSchema).max(SYSTEM_CONNECTIONS_MAX, { message: `can hold at most ${SYSTEM_CONNECTIONS_MAX} connections` })
1112
- }).strict();
1113
- var systemDescriptionProblemSchema = z7.object({
1114
- path: z7.string(),
1115
- message: z7.string()
1116
- }).strict();
1117
- var systemReportRecordedSchema = z7.object({
1118
- outcome: z7.literal("recorded"),
1119
- version: z7.number().int().min(1),
1120
- reportedAt: utcIso8601
1121
- }).strict();
1122
- var systemReportRefusedSchema = z7.object({
1123
- outcome: z7.literal("refused"),
1124
- problems: z7.array(systemDescriptionProblemSchema).min(1)
1125
- }).strict();
1126
- var systemReportUnavailableSchema = z7.object({
1127
- outcome: z7.literal("unavailable")
1128
- }).strict();
1129
- var systemReportResultSchema = z7.discriminatedUnion("outcome", [
1130
- systemReportRecordedSchema,
1131
- systemReportRefusedSchema,
1132
- systemReportUnavailableSchema
1133
- ]);
1134
- var SYSTEM_DRAW_SVG_MAX_BYTES = 2 * 1024 * 1024;
1135
- var SYSTEM_DRAW_MERMAID_MAX_BYTES = 256 * 1024;
1136
- var SYSTEM_DRAW_PNG_MAX_BYTES = 8 * 1024 * 1024;
1137
- var systemDrawArchitectureRequestSchema = z7.object({
1138
- picture: z7.literal("architecture"),
1139
- version: z7.number().int().min(1)
951
+ var SYSTEM_DRAW_SVG_MAX_BYTES = 2 * 1024 * 1024;
952
+ var SYSTEM_DRAW_MERMAID_MAX_BYTES = 256 * 1024;
953
+ var SYSTEM_DRAW_PNG_MAX_BYTES = 8 * 1024 * 1024;
954
+ var systemDrawArchitectureRequestSchema = z7.object({
955
+ picture: z7.literal("architecture"),
956
+ version: z7.number().int().min(1)
1140
957
  }).strict();
1141
958
  var systemDrawPhaseRequestSchema = z7.object({
1142
959
  picture: z7.literal("phase"),
@@ -1207,7 +1024,7 @@ var BUILD_TASK_WHY_OUTSIDE_PLAN_MAX = 160;
1207
1024
  var BUILD_TASK_BUDGET_MAX_MINUTES = 10080;
1208
1025
  var BUILD_TASK_NOTE_MAX = 240;
1209
1026
  var BUILD_TASK_CLEAR_COMMAND_MAX = 240;
1210
- var buildTaskKindSchema = z8.enum(["build", "review", "spec", "walk"]);
1027
+ var buildTaskKindSchema = z8.enum(["build", "review", "spec", "walk", "check"]);
1211
1028
  var buildTaskIdSchema = z8.string().min(1).max(64);
1212
1029
  var buildTaskSchema = z8.object({
1213
1030
  // The task's own id, as the coordinator's graph names it.
@@ -1249,7 +1066,8 @@ var buildTaskSchema = z8.object({
1249
1066
  // week. A plan, not a measurement; absent when no budget was set.
1250
1067
  budgetMinutes: z8.number().int().min(1).max(BUILD_TASK_BUDGET_MAX_MINUTES).optional(),
1251
1068
  // What kind of work the task is: building, checking other work, writing what to
1252
- // build, or a person trying the product. Absent when the graph does not say.
1069
+ // build, a person trying the product, or a run of the project's own checks.
1070
+ // Absent when the graph does not say.
1253
1071
  kind: buildTaskKindSchema.optional(),
1254
1072
  // One line from whoever holds the task about where it stands. Absent when there
1255
1073
  // is nothing to say.
@@ -1359,27 +1177,55 @@ var buildSnapshotReportResultSchema = z8.object({
1359
1177
  }).strict();
1360
1178
 
1361
1179
  // ../events/dist/phase-plan.js
1180
+ import { z as z10 } from "zod";
1181
+
1182
+ // ../events/dist/change-path-order.js
1362
1183
  import { z as z9 } from "zod";
1184
+ var PATH_ORDER = Object.freeze(["small", "mini", "full"]);
1185
+ var changePathSchema = z9.enum(PATH_ORDER);
1186
+ var PHASE_PATHS = Object.freeze(["mini", "full"]);
1187
+ var phasePathSchema = z9.enum(PHASE_PATHS);
1188
+ var changeUpPathSchema = z9.enum(["mini", "full"]);
1189
+ var CHANGE_PATH_NEXT = Object.freeze({
1190
+ small: Object.freeze({ say: "make the small change", section: "small" }),
1191
+ mini: Object.freeze({ say: "scope the mini phase", section: "mini" }),
1192
+ full: Object.freeze({ say: "plan the next phase", section: "full" })
1193
+ });
1194
+ var changePathNextSchema = z9.object({
1195
+ say: z9.enum([CHANGE_PATH_NEXT.small.say, CHANGE_PATH_NEXT.mini.say, CHANGE_PATH_NEXT.full.say]),
1196
+ section: changePathSchema
1197
+ }).strict();
1198
+ var CHANGE_SENTENCE_MAX = 4e3;
1199
+ var planMovedUpSchema = z9.object({
1200
+ to: z9.literal("full"),
1201
+ sentence: z9.string().min(1).max(CHANGE_SENTENCE_MAX),
1202
+ next: changePathNextSchema
1203
+ }).strict().refine((m) => m.next.say === CHANGE_PATH_NEXT.full.say && m.next.section === CHANGE_PATH_NEXT.full.section, {
1204
+ message: "names the full path's next phrase and section",
1205
+ path: ["next"]
1206
+ });
1207
+
1208
+ // ../events/dist/phase-plan.js
1363
1209
  var PHASE_PLAN_FORMAT = 1;
1364
1210
  var PHASE_PLAN_CHANGES_MAX = 60;
1365
1211
  var PHASE_PLAN_SUMMARY_MAX = 160;
1366
1212
  var SCOPE_NOTE_MAX = 500;
1367
1213
  var PHASE_PLAN_PHASE_MAX = buildPhaseSchema.maxLength ?? 0;
1368
1214
  var PHASE_PLAN_CHANGE_KINDS = ["add", "change", "remove"];
1369
- var phasePlanChangeSchema = z9.enum(PHASE_PLAN_CHANGE_KINDS, {
1215
+ var phasePlanChangeSchema = z10.enum(PHASE_PLAN_CHANGE_KINDS, {
1370
1216
  error: `must be one of ${PHASE_PLAN_CHANGE_KINDS.join(", ")}`
1371
1217
  });
1372
1218
  var phasePlanPhaseSchema = phaseIdentitySchema.refine((v) => !isValidPhaseIdentity(v) || buildPhaseSchema.safeParse(String(v)).success, { message: `must be at most ${PHASE_PLAN_PHASE_MAX} characters` });
1373
1219
  var phasePlanSummarySchema = descriptionText(PHASE_PLAN_SUMMARY_MAX);
1374
- var phasePlanEntrySchema = z9.object({
1220
+ var phasePlanEntrySchema = z10.object({
1375
1221
  part: systemPartIdSchema,
1376
1222
  change: phasePlanChangeSchema,
1377
1223
  summary: phasePlanSummarySchema
1378
1224
  }).strict();
1379
- var phasePlanSchema = z9.object({
1380
- format: z9.literal(PHASE_PLAN_FORMAT, { error: `must be ${PHASE_PLAN_FORMAT}` }),
1225
+ var phasePlanSchema = z10.object({
1226
+ format: z10.literal(PHASE_PLAN_FORMAT, { error: `must be ${PHASE_PLAN_FORMAT}` }),
1381
1227
  phase: phasePlanPhaseSchema,
1382
- changes: z9.array(phasePlanEntrySchema).min(1, { message: "must list at least one part" }).max(PHASE_PLAN_CHANGES_MAX, { message: `can list at most ${PHASE_PLAN_CHANGES_MAX} parts` })
1228
+ changes: z10.array(phasePlanEntrySchema).min(1, { message: "must list at least one part" }).max(PHASE_PLAN_CHANGES_MAX, { message: `can list at most ${PHASE_PLAN_CHANGES_MAX} parts` })
1383
1229
  }).strict();
1384
1230
  var scopePhaseSchema = buildPhaseSchema.refine((v) => isValidPhaseIdentity(v), {
1385
1231
  message: "must be a phase name"
@@ -1391,109 +1237,110 @@ var SCOPE_STATE_NAMES = [
1391
1237
  "approved",
1392
1238
  "partly-stale"
1393
1239
  ];
1394
- var scopeStateNameSchema = z9.enum(SCOPE_STATE_NAMES);
1240
+ var scopeStateNameSchema = z10.enum(SCOPE_STATE_NAMES);
1395
1241
  var plannedScopeStateSchema = scopeStateNameSchema.exclude(["not-planned"]);
1396
1242
  var SCOPE_PART_STATUSES = ["approved", "waiting"];
1397
- var scopePartStatusSchema = z9.enum(SCOPE_PART_STATUSES);
1243
+ var scopePartStatusSchema = z10.enum(SCOPE_PART_STATUSES);
1398
1244
  var SCOPE_ACTS = ["approve", "send-back"];
1399
- var scopeActSchema = z9.enum(SCOPE_ACTS);
1245
+ var scopeActSchema = z10.enum(SCOPE_ACTS);
1400
1246
  var SCOPE_GIVEN_IN = ["console", "chat"];
1401
- var scopeGivenInSchema = z9.enum(SCOPE_GIVEN_IN);
1247
+ var scopeGivenInSchema = z10.enum(SCOPE_GIVEN_IN);
1402
1248
  var scopeNoteSchema = descriptionText(SCOPE_NOTE_MAX);
1403
- var scopeNoteWireSchema = z9.string().max(SCOPE_NOTE_MAX);
1249
+ var scopeNoteWireSchema = z10.string().max(SCOPE_NOTE_MAX);
1404
1250
  var SCOPE_PART_VERDICTS = ["ok", "change"];
1405
- var scopePartVerdictSchema = z9.enum(SCOPE_PART_VERDICTS);
1406
- var scopeDecisionPartSchema = z9.object({
1251
+ var scopePartVerdictSchema = z10.enum(SCOPE_PART_VERDICTS);
1252
+ var scopeDecisionPartSchema = z10.object({
1407
1253
  part: systemPartIdSchema,
1408
1254
  verdict: scopePartVerdictSchema,
1409
1255
  note: scopeNoteWireSchema.optional()
1410
1256
  }).strict();
1411
- var planVersionSchema = z9.number().int().min(1);
1412
- var carriedPhaseSchema = z9.object({
1257
+ var planVersionSchema = z10.number().int().min(1);
1258
+ var carriedPhaseSchema = z10.object({
1413
1259
  phase: scopePhaseSchema,
1414
- number: z9.number().int().min(1)
1260
+ number: z10.number().int().min(1)
1415
1261
  }).strict();
1416
1262
  var carriedFromEntrySchema = carriedPhaseSchema.extend({
1417
1263
  part: systemPartIdSchema
1418
1264
  }).strict();
1419
- var phasePlanRequestSchema = z9.object({
1420
- descriptionVersion: z9.number().int().min(1),
1421
- plan: z9.unknown()
1265
+ var phasePlanRequestSchema = z10.object({
1266
+ descriptionVersion: z10.number().int().min(1),
1267
+ plan: z10.unknown()
1422
1268
  }).strict();
1423
- var phasePlanRecordedSchema = z9.object({
1424
- outcome: z9.literal("recorded"),
1269
+ var phasePlanRecordedSchema = z10.object({
1270
+ outcome: z10.literal("recorded"),
1425
1271
  version: planVersionSchema,
1426
- phaseNumber: z9.number().int().min(1),
1272
+ phaseNumber: z10.number().int().min(1),
1427
1273
  state: plannedScopeStateSchema,
1428
- changedParts: z9.array(systemPartIdSchema).max(PHASE_PLAN_CHANGES_MAX),
1429
- typedApprovalOffered: z9.boolean(),
1274
+ changedParts: z10.array(systemPartIdSchema).max(PHASE_PLAN_CHANGES_MAX),
1275
+ typedApprovalOffered: z10.boolean(),
1430
1276
  reportedAt: utcIso8601,
1431
- carriedFrom: z9.array(carriedFromEntrySchema).min(1).max(PHASE_PLAN_CHANGES_MAX).optional()
1277
+ carriedFrom: z10.array(carriedFromEntrySchema).min(1).max(PHASE_PLAN_CHANGES_MAX).optional(),
1278
+ movedUp: planMovedUpSchema.optional()
1432
1279
  }).strict();
1433
- var phasePlanRefusedSchema = z9.object({
1434
- outcome: z9.literal("refused"),
1435
- problems: z9.array(systemDescriptionProblemSchema).min(1)
1280
+ var phasePlanRefusedSchema = z10.object({
1281
+ outcome: z10.literal("refused"),
1282
+ problems: z10.array(systemDescriptionProblemSchema).min(1)
1436
1283
  }).strict();
1437
- var phasePlanUnavailableSchema = z9.object({
1438
- outcome: z9.literal("unavailable")
1284
+ var phasePlanUnavailableSchema = z10.object({
1285
+ outcome: z10.literal("unavailable")
1439
1286
  }).strict();
1440
1287
  var REMOVAL_OWED_ACTS = ["dropped", "removed"];
1441
- var removalOwedPartSchema = z9.object({
1288
+ var removalOwedPartSchema = z10.object({
1442
1289
  part: systemPartIdSchema,
1443
1290
  name: systemPartSchema.shape.name,
1444
1291
  phase: scopePhaseSchema,
1445
- number: z9.number().int().min(1),
1446
- act: z9.enum(REMOVAL_OWED_ACTS)
1292
+ number: z10.number().int().min(1),
1293
+ act: z10.enum(REMOVAL_OWED_ACTS)
1447
1294
  }).strict();
1448
- var phasePlanRemovalOwedSchema = z9.object({
1449
- outcome: z9.literal("removal-owed"),
1450
- parts: z9.array(removalOwedPartSchema).min(1).max(SYSTEM_PARTS_MAX)
1295
+ var phasePlanRemovalOwedSchema = z10.object({
1296
+ outcome: z10.literal("removal-owed"),
1297
+ parts: z10.array(removalOwedPartSchema).min(1).max(SYSTEM_PARTS_MAX)
1451
1298
  }).strict();
1452
- var phasePlanResultSchema = z9.discriminatedUnion("outcome", [
1299
+ var phasePlanResultSchema = z10.discriminatedUnion("outcome", [
1453
1300
  phasePlanRecordedSchema,
1454
1301
  phasePlanRefusedSchema,
1455
1302
  phasePlanUnavailableSchema,
1456
1303
  phasePlanRemovalOwedSchema
1457
1304
  ]);
1458
- var scopeApproveRequestSchema = z9.object({
1459
- act: z9.literal("approve"),
1305
+ var scopeApproveRequestSchema = z10.object({
1306
+ act: z10.literal("approve"),
1460
1307
  planVersion: planVersionSchema,
1461
- parts: z9.array(scopeDecisionPartSchema).max(PHASE_PLAN_CHANGES_MAX)
1308
+ parts: z10.array(scopeDecisionPartSchema).max(PHASE_PLAN_CHANGES_MAX)
1462
1309
  }).strict();
1463
- var scopeSendBackRequestSchema = z9.object({
1464
- act: z9.literal("send-back"),
1310
+ var scopeSendBackRequestSchema = z10.object({
1311
+ act: z10.literal("send-back"),
1465
1312
  planVersion: planVersionSchema,
1466
- parts: z9.array(scopeDecisionPartSchema).max(PHASE_PLAN_CHANGES_MAX),
1313
+ parts: z10.array(scopeDecisionPartSchema).max(PHASE_PLAN_CHANGES_MAX),
1467
1314
  note: scopeNoteWireSchema.optional()
1468
1315
  }).strict();
1469
- var scopeDecisionRequestSchema = z9.discriminatedUnion("act", [
1316
+ var scopeDecisionRequestSchema = z10.discriminatedUnion("act", [
1470
1317
  scopeApproveRequestSchema,
1471
1318
  scopeSendBackRequestSchema
1472
1319
  ]);
1473
- var scopeDecisionRecordedSchema = z9.object({
1474
- outcome: z9.literal("recorded"),
1320
+ var scopeDecisionRecordedSchema = z10.object({
1321
+ outcome: z10.literal("recorded"),
1475
1322
  act: scopeActSchema,
1476
1323
  phase: scopePhaseSchema,
1477
1324
  planVersion: planVersionSchema,
1478
1325
  state: plannedScopeStateSchema,
1479
1326
  decidedAt: utcIso8601
1480
1327
  }).strict();
1481
- var scopeDecisionAlreadyApprovedSchema = z9.object({
1482
- outcome: z9.literal("already-approved"),
1483
- replanNeeded: z9.boolean(),
1484
- reason: z9.string().min(1)
1328
+ var scopeDecisionAlreadyApprovedSchema = z10.object({
1329
+ outcome: z10.literal("already-approved"),
1330
+ replanNeeded: z10.boolean(),
1331
+ reason: z10.string().min(1)
1485
1332
  }).strict();
1486
- var scopeDecisionRefusedSchema = z9.object({
1487
- outcome: z9.literal("refused"),
1488
- problems: z9.array(systemDescriptionProblemSchema).min(1)
1333
+ var scopeDecisionRefusedSchema = z10.object({
1334
+ outcome: z10.literal("refused"),
1335
+ problems: z10.array(systemDescriptionProblemSchema).min(1)
1489
1336
  }).strict();
1490
- var scopeDecisionUnavailableSchema = z9.object({
1491
- outcome: z9.literal("unavailable")
1337
+ var scopeDecisionUnavailableSchema = z10.object({
1338
+ outcome: z10.literal("unavailable")
1492
1339
  }).strict();
1493
1340
  function reasonOutcome(outcome) {
1494
- return z9.object({ outcome: z9.literal(outcome), reason: z9.string().min(1) }).strict();
1341
+ return z10.object({ outcome: z10.literal(outcome), reason: z10.string().min(1) }).strict();
1495
1342
  }
1496
- var scopeDecisionResultSchema = z9.discriminatedUnion("outcome", [
1343
+ var scopeDecisionResultSchema = z10.discriminatedUnion("outcome", [
1497
1344
  scopeDecisionRecordedSchema,
1498
1345
  scopeDecisionAlreadyApprovedSchema,
1499
1346
  scopeDecisionRefusedSchema,
@@ -1503,60 +1350,42 @@ var scopeDecisionResultSchema = z9.discriminatedUnion("outcome", [
1503
1350
  reasonOutcome("plan-changed"),
1504
1351
  reasonOutcome("build-started")
1505
1352
  ]);
1506
- var scopeReadPartSchema = z9.object({
1353
+ var scopeReadPartSchema = z10.object({
1507
1354
  part: systemPartIdSchema,
1508
1355
  name: systemPartSchema.shape.name,
1509
1356
  change: phasePlanChangeSchema,
1510
1357
  summary: phasePlanSummarySchema,
1511
1358
  status: scopePartStatusSchema,
1512
- changed: z9.boolean(),
1359
+ changed: z10.boolean(),
1513
1360
  carriedFrom: carriedPhaseSchema.optional()
1514
1361
  }).strict();
1515
- var scopeReadDecisionSchema = z9.object({
1362
+ var scopeReadDecisionSchema = z10.object({
1516
1363
  act: scopeActSchema,
1517
1364
  planVersion: planVersionSchema,
1518
1365
  givenIn: scopeGivenInSchema,
1519
1366
  at: utcIso8601,
1520
- parts: z9.array(scopeDecisionPartSchema).max(PHASE_PLAN_CHANGES_MAX),
1367
+ parts: z10.array(scopeDecisionPartSchema).max(PHASE_PLAN_CHANGES_MAX),
1521
1368
  note: scopeNoteWireSchema.optional()
1522
1369
  }).strict();
1523
- var scopeReadNotPlannedSchema = z9.object({
1370
+ var scopeReadNotPlannedSchema = z10.object({
1524
1371
  phase: scopePhaseSchema,
1525
- state: z9.literal("not-planned"),
1526
- canDecide: z9.boolean()
1372
+ state: z10.literal("not-planned"),
1373
+ canDecide: z10.boolean()
1527
1374
  }).strict();
1528
- var scopeReadPlannedSchema = z9.object({
1375
+ var scopeReadPlannedSchema = z10.object({
1529
1376
  phase: scopePhaseSchema,
1530
1377
  state: plannedScopeStateSchema,
1531
- phaseNumber: z9.number().int().min(1),
1378
+ phaseNumber: z10.number().int().min(1),
1532
1379
  planVersion: planVersionSchema,
1533
- parts: z9.array(scopeReadPartSchema).min(1).max(PHASE_PLAN_CHANGES_MAX),
1534
- decisions: z9.array(scopeReadDecisionSchema),
1535
- canDecide: z9.boolean(),
1536
- replanLine: z9.string().min(1).optional()
1380
+ parts: z10.array(scopeReadPartSchema).min(1).max(PHASE_PLAN_CHANGES_MAX),
1381
+ decisions: z10.array(scopeReadDecisionSchema),
1382
+ canDecide: z10.boolean(),
1383
+ replanLine: z10.string().min(1).optional()
1537
1384
  }).strict();
1538
- var scopeReadSchema = z9.union([scopeReadNotPlannedSchema, scopeReadPlannedSchema]);
1539
-
1540
- // ../events/dist/build-record-v3.js
1541
- import { z as z14 } from "zod";
1385
+ var scopeReadSchema = z10.union([scopeReadNotPlannedSchema, scopeReadPlannedSchema]);
1542
1386
 
1543
- // ../events/dist/board-vocabulary.js
1544
- import { z as z10 } from "zod";
1545
- var boardStepStateSchema = z10.enum([
1546
- "not-instrumented",
1547
- "done",
1548
- "did-not-pass",
1549
- "could-not-complete",
1550
- "not-yet-available",
1551
- "qualified"
1552
- ]);
1553
- var boardGateNameSchema = z10.enum([
1554
- "scope",
1555
- "architecture",
1556
- "specs",
1557
- "taskgraph",
1558
- "phase-close"
1559
- ]);
1387
+ // ../events/dist/observed-changes.js
1388
+ import { z as z12 } from "zod";
1560
1389
 
1561
1390
  // ../events/dist/code-link.js
1562
1391
  import { z as z11 } from "zod";
@@ -1592,7 +1421,6 @@ var partCodeLinkSchema = z11.discriminatedUnion("kind", [
1592
1421
  ]);
1593
1422
 
1594
1423
  // ../events/dist/observed-changes.js
1595
- import { z as z12 } from "zod";
1596
1424
  var OBSERVED_PATHS_LISTED_MAX = 50;
1597
1425
  var OBSERVED_PATH_MAX = 300;
1598
1426
  var OBSERVED_COUNT_MAX = 1e6;
@@ -1801,36 +1629,659 @@ var observedChangesSchema = z12.object({
1801
1629
  if (counted2 && v.descriptionVersion === null) {
1802
1630
  ctx.addIssue({ code: "custom", path: ["descriptionVersion"], message: "must name the version the counts used" });
1803
1631
  }
1804
- });
1805
- var partOutsideThePlanSchema = z12.object({
1806
- part: systemPartIdSchema,
1807
- name: systemPartSchema.shape.name,
1808
- seen: observedFilesSchema.nullable(),
1809
- tasks: z12.array(observedSaidSchema.extend({ id: buildTaskSchema.shape.id }).strict()).max(BUILD_SNAPSHOT_TASK_LIMIT)
1632
+ });
1633
+ var partOutsideThePlanSchema = z12.object({
1634
+ part: systemPartIdSchema,
1635
+ name: systemPartSchema.shape.name,
1636
+ seen: observedFilesSchema.nullable(),
1637
+ tasks: z12.array(observedSaidSchema.extend({ id: buildTaskSchema.shape.id }).strict()).max(BUILD_SNAPSHOT_TASK_LIMIT)
1638
+ }).strict();
1639
+ function shortCommit(commit) {
1640
+ return commit.slice(0, 7);
1641
+ }
1642
+
1643
+ // ../events/dist/work-record.js
1644
+ import { z as z14 } from "zod";
1645
+
1646
+ // ../events/dist/crew-tier.js
1647
+ import { z as z13 } from "zod";
1648
+ var crewTierSchema = z13.enum(["deep", "standard", "fast"]);
1649
+
1650
+ // ../events/dist/work-record.js
1651
+ var agentWorkText = statementText(STATEMENT_MAX);
1652
+ var WORK_FINDINGS_MAX = 100;
1653
+ var WORK_ANSWERS_MAX = 100;
1654
+ var WORK_ATTEMPT_LIMIT = 4;
1655
+ var WORK_CLAIM_CHECKS_MAX = 20;
1656
+ var findingIdSchema = z14.string().regex(/^F[1-9][0-9]?\.[AQ][1-9][0-9]{0,2}$/, { message: "must be a finding id such as F1.A1" });
1657
+ var WORK_STAGES = Object.freeze(["asked", "quality"]);
1658
+ var workStageSchema = z14.enum(WORK_STAGES);
1659
+ var workVerdictSchema = z14.enum(["pass", "fail"]);
1660
+ var WORK_FINDING_KINDS = Object.freeze([
1661
+ "asked",
1662
+ "quality",
1663
+ "unrecorded-call",
1664
+ "undeclared-listed-decision"
1665
+ ]);
1666
+ var workFindingKindSchema = z14.enum(WORK_FINDING_KINDS);
1667
+ var WORK_ASKED_ONLY_FINDING_KINDS = Object.freeze([
1668
+ "unrecorded-call",
1669
+ "undeclared-listed-decision"
1670
+ ]);
1671
+ var workOutcomeSchema = z14.enum(["fixed", "declined", "deferred"]);
1672
+ var DIAGNOSIS_CAUSES = Object.freeze([
1673
+ "spec",
1674
+ "ordering",
1675
+ "approach",
1676
+ "capability",
1677
+ "environment",
1678
+ "review"
1679
+ ]);
1680
+ var diagnosisCauseSchema = z14.enum(DIAGNOSIS_CAUSES);
1681
+ var diagnosisNextSchema = z14.enum(["different-way", "stop", "ask-person"]);
1682
+ var callUndoableSchema = z14.enum(["yes", "no"]);
1683
+ var callKindSchema = z14.enum(["default", "listed"]);
1684
+ var commitShaSchema = closeCommitSchema;
1685
+ var workChangeIdSchema = z14.string().uuid({ message: "must be the change id Halfcycle gave" });
1686
+ var workTaskSubjectSchema = z14.object({
1687
+ kind: z14.literal("task"),
1688
+ phase: buildPhaseSchema,
1689
+ task: buildTaskIdSchema
1690
+ }).strict();
1691
+ var workChangeSubjectSchema = z14.object({
1692
+ kind: z14.literal("change"),
1693
+ change: workChangeIdSchema
1694
+ }).strict();
1695
+ var workPhaseSubjectSchema = z14.object({
1696
+ kind: z14.literal("phase"),
1697
+ phase: buildPhaseSchema
1698
+ }).strict();
1699
+ var workSubjectSchema = z14.discriminatedUnion("kind", [
1700
+ workTaskSubjectSchema,
1701
+ workChangeSubjectSchema,
1702
+ workPhaseSubjectSchema
1703
+ ]);
1704
+ var workItemSubjectSchema = z14.discriminatedUnion("kind", [workTaskSubjectSchema, workChangeSubjectSchema]);
1705
+ var workFindingInputSchema = z14.object({
1706
+ kind: workFindingKindSchema,
1707
+ says: agentWorkText
1708
+ }).strict();
1709
+ var workReviewRecordSchema = z14.object({
1710
+ record: z14.literal("review"),
1711
+ subject: workItemSubjectSchema,
1712
+ stage: workStageSchema,
1713
+ verdict: workVerdictSchema,
1714
+ reviewer: agentNameSchema,
1715
+ builder: agentNameSchema,
1716
+ builderTier: crewTierSchema,
1717
+ head: commitShaSchema,
1718
+ findings: z14.array(workFindingInputSchema).max(WORK_FINDINGS_MAX, { message: `can carry at most ${WORK_FINDINGS_MAX} findings` }),
1719
+ retryReason: agentWorkText.optional(),
1720
+ diagnosis: z14.string().uuid({ message: "must be the diagnosis id Halfcycle gave" }).optional(),
1721
+ followsDiagnosis: z14.boolean().optional()
1722
+ }).strict().refine((r) => r.stage === "asked" || r.findings.every((f) => !WORK_ASKED_ONLY_FINDING_KINDS.includes(f.kind)), {
1723
+ message: "a review for quality cannot raise an unrecorded call or an undeclared listed decision",
1724
+ path: ["findings"]
1725
+ });
1726
+ var workAnswerSchema = z14.object({
1727
+ finding: findingIdSchema,
1728
+ outcome: workOutcomeSchema,
1729
+ reason: agentWorkText.optional()
1730
+ }).strict().refine((a) => a.outcome === "fixed" === (a.reason === void 0), {
1731
+ message: "a reason is given exactly when a finding is declined or deferred",
1732
+ path: ["reason"]
1733
+ });
1734
+ var workOutcomesRecordSchema = z14.object({
1735
+ record: z14.literal("outcomes"),
1736
+ subject: workItemSubjectSchema,
1737
+ by: agentNameSchema,
1738
+ answers: z14.array(workAnswerSchema).min(1, { message: "must answer at least one finding" }).max(WORK_ANSWERS_MAX, { message: `can carry at most ${WORK_ANSWERS_MAX} answers` })
1739
+ }).strict().refine((r) => new Set(r.answers.map((a) => a.finding)).size === r.answers.length, {
1740
+ message: "answers each finding once",
1741
+ path: ["answers"]
1742
+ });
1743
+ var workClaimsRecordSchema = z14.object({
1744
+ record: z14.literal("claims"),
1745
+ subject: workItemSubjectSchema,
1746
+ head: commitShaSchema,
1747
+ by: agentNameSchema,
1748
+ checks: z14.array(z14.object({ label: agentWorkText }).strict()).min(1, { message: "must name at least one check" }).max(WORK_CLAIM_CHECKS_MAX, { message: `can name at most ${WORK_CLAIM_CHECKS_MAX} checks` })
1749
+ }).strict().refine((r) => new Set(r.checks.map((c) => c.label)).size === r.checks.length, {
1750
+ message: "names each check once",
1751
+ path: ["checks"]
1752
+ });
1753
+ var workDiagnosisRecordSchema = z14.object({
1754
+ record: z14.literal("diagnosis"),
1755
+ subject: workItemSubjectSchema,
1756
+ afterAttempt: z14.number().int().min(2, { message: "must be attempt 2 or later" }).max(WORK_ATTEMPT_LIMIT - 1, { message: `must be attempt ${WORK_ATTEMPT_LIMIT - 1} or earlier` }),
1757
+ by: agentNameSchema,
1758
+ cause: diagnosisCauseSchema,
1759
+ says: agentWorkText,
1760
+ next: diagnosisNextSchema,
1761
+ change: agentWorkText.optional(),
1762
+ question: agentWorkText.optional(),
1763
+ answer: agentWorkText.optional()
1764
+ }).strict().refine((r) => r.next === "different-way" === (r.change !== void 0), {
1765
+ message: "says what will change exactly when the next step is to try a different way",
1766
+ path: ["change"]
1767
+ }).refine((r) => r.next === "ask-person" === (r.question !== void 0), {
1768
+ message: "carries a question exactly when the next step is to ask the person",
1769
+ path: ["question"]
1770
+ });
1771
+ var workCallRecordSchema = z14.object({
1772
+ record: z14.literal("call"),
1773
+ subject: workSubjectSchema,
1774
+ by: agentNameSchema,
1775
+ question: agentWorkText,
1776
+ chose: agentWorkText,
1777
+ statedDefault: agentWorkText,
1778
+ undoable: callUndoableSchema,
1779
+ how: agentWorkText.optional()
1780
+ }).strict().refine((r) => r.undoable === "yes" === (r.how !== void 0), {
1781
+ message: "says how to undo it exactly when it can be undone",
1782
+ path: ["how"]
1783
+ });
1784
+ var workRecordSchema = z14.discriminatedUnion("record", [
1785
+ workReviewRecordSchema,
1786
+ workOutcomesRecordSchema,
1787
+ workClaimsRecordSchema,
1788
+ workDiagnosisRecordSchema,
1789
+ workCallRecordSchema
1790
+ ]);
1791
+ var WORK_STANDINGS = Object.freeze([
1792
+ "not-reviewed",
1793
+ "stopped",
1794
+ "held",
1795
+ "needs-diagnosis",
1796
+ "failed",
1797
+ "in-review",
1798
+ "findings-open",
1799
+ "passed"
1800
+ ]);
1801
+ var workStandingSchema = z14.enum(WORK_STANDINGS);
1802
+ var workRecordResultSchema = z14.object({
1803
+ outcome: z14.enum(["recorded", "kept"]),
1804
+ attempt: z14.number().int().min(1).optional(),
1805
+ findings: z14.array(findingIdSchema).max(WORK_FINDINGS_MAX).optional(),
1806
+ diagnosis: z14.string().uuid().optional(),
1807
+ standing: workStandingSchema,
1808
+ says: z14.string().min(1)
1809
+ }).strict();
1810
+ var WORK_REFUSAL_STATUS = Object.freeze({
1811
+ "phase-not-open": 409,
1812
+ "unknown-task": 400,
1813
+ "unknown-change": 404,
1814
+ "change-finished": 409,
1815
+ "out-of-order": 409,
1816
+ "same-agent": 409,
1817
+ "already-recorded": 409,
1818
+ "already-answered": 409,
1819
+ "unknown-finding": 400,
1820
+ "findings-unanswered": 409,
1821
+ "listed-decision-passed": 400,
1822
+ "needs-diagnosis": 409,
1823
+ "no-diagnosis-due": 409,
1824
+ "not-fresh": 409,
1825
+ "graph-unchanged": 409,
1826
+ "no-graph": 400,
1827
+ "wrong-diagnosis": 409,
1828
+ "diagnosis-not-followed": 400,
1829
+ stopped: 409,
1830
+ held: 409,
1831
+ "attempt-limit": 409,
1832
+ "same-attempt": 409,
1833
+ "call-not-recorded": 409
1834
+ });
1835
+ var WORK_REFUSALS = Object.freeze(Object.keys(WORK_REFUSAL_STATUS));
1836
+ var workRefusalSchema = z14.enum(WORK_REFUSALS);
1837
+ var workRefusalErrorSchema = wireErrorSchema.extend({ error: workRefusalSchema }).strict().refine((e) => e.statusCode === WORK_REFUSAL_STATUS[e.error], {
1838
+ message: "carries the status its refusal is answered with",
1839
+ path: ["statusCode"]
1840
+ });
1841
+ var testCommandIdSchema = z14.string().regex(/^[0-9a-f]{16}$/, { message: "must be 16 lower-case hex characters" });
1842
+ var workCountSchema = z14.number().int().min(0);
1843
+ var testRunReportSchema = z14.object({
1844
+ runId: z14.string().uuid(),
1845
+ subject: workItemSubjectSchema,
1846
+ label: agentWorkText,
1847
+ when: z14.enum(["before", "after"]),
1848
+ commandId: testCommandIdSchema,
1849
+ head: commitShaSchema,
1850
+ changedOutsideTests: workCountSchema,
1851
+ uncommitted: workCountSchema,
1852
+ beforeRunId: z14.string().uuid().nullable(),
1853
+ changedSinceBefore: workCountSchema.nullable(),
1854
+ exitCode: z14.number().int().nullable(),
1855
+ timedOut: z14.boolean(),
1856
+ passed: workCountSchema.nullable(),
1857
+ failed: workCountSchema.nullable(),
1858
+ durationMs: workCountSchema,
1859
+ startedAt: utcIso8601,
1860
+ reason: agentWorkText.optional()
1861
+ }).strict().refine((r) => r.passed === null === (r.failed === null), {
1862
+ message: "counts both passed and failed, or neither",
1863
+ path: ["failed"]
1864
+ }).refine((r) => r.when === "after" || r.beforeRunId === null && r.changedSinceBefore === null, {
1865
+ message: "a run before the change is measured against no earlier run",
1866
+ path: ["beforeRunId"]
1867
+ }).refine((r) => !r.timedOut || r.exitCode === null, {
1868
+ message: "a run that timed out has no exit code",
1869
+ path: ["exitCode"]
1870
+ });
1871
+ var testRunResultSchema = z14.object({ outcome: z14.literal("recorded") }).strict();
1872
+ var TEST_PROOF_REASONS = Object.freeze([
1873
+ "no-run-before",
1874
+ "before-after-change",
1875
+ "not-counted",
1876
+ "passed-before",
1877
+ "no-run-after",
1878
+ "command-changed",
1879
+ "not-after-before",
1880
+ "nothing-changed",
1881
+ "failed-after",
1882
+ "after-not-at-claimed-head"
1883
+ ]);
1884
+ var testProofReasonSchema = z14.enum(TEST_PROOF_REASONS);
1885
+ var testProofStateSchema = z14.enum(["proven", "not-proven", "unchecked"]);
1886
+ var testRunCountsSchema = z14.object({
1887
+ passed: workCountSchema.nullable(),
1888
+ failed: workCountSchema.nullable(),
1889
+ durationMs: workCountSchema,
1890
+ timedOut: z14.boolean()
1891
+ }).strict();
1892
+ var workTestSchema = z14.object({
1893
+ label: agentWorkText,
1894
+ state: testProofStateSchema,
1895
+ why: testProofReasonSchema.optional(),
1896
+ claimedBy: agentNameSchema,
1897
+ before: testRunCountsSchema.optional(),
1898
+ after: testRunCountsSchema.optional()
1899
+ }).strict().refine((t) => t.state === "not-proven" === (t.why !== void 0), {
1900
+ message: "gives a reason exactly when a check is not proven",
1901
+ path: ["why"]
1902
+ });
1903
+ var workFindingOutcomeSchema = z14.object({
1904
+ outcome: workOutcomeSchema,
1905
+ reason: agentWorkText.optional(),
1906
+ by: agentNameSchema
1907
+ }).strict().refine((o) => o.outcome === "fixed" === (o.reason === void 0), {
1908
+ message: "a reason is given exactly when a finding is declined or deferred",
1909
+ path: ["reason"]
1910
+ });
1911
+ var workFindingSchema = z14.object({
1912
+ id: findingIdSchema,
1913
+ kind: workFindingKindSchema,
1914
+ says: agentWorkText,
1915
+ outcome: workFindingOutcomeSchema.optional()
1916
+ }).strict();
1917
+ var workStageViewSchema = z14.object({
1918
+ reviewer: agentNameSchema,
1919
+ verdict: workVerdictSchema,
1920
+ findings: z14.array(workFindingSchema).max(WORK_FINDINGS_MAX)
1921
+ }).strict();
1922
+ var workAttemptSchema = z14.object({
1923
+ attempt: z14.number().int().min(1),
1924
+ builder: agentNameSchema,
1925
+ builderTier: crewTierSchema,
1926
+ head: commitShaSchema,
1927
+ retryReason: agentWorkText.optional(),
1928
+ diagnosis: z14.string().uuid().optional(),
1929
+ followsDiagnosis: z14.boolean().optional(),
1930
+ asked: workStageViewSchema.optional(),
1931
+ quality: workStageViewSchema.optional()
1932
+ }).strict();
1933
+ var workDiagnosisSchema = z14.object({
1934
+ id: z14.string().uuid(),
1935
+ afterAttempt: z14.number().int().min(2).max(WORK_ATTEMPT_LIMIT - 1),
1936
+ by: agentNameSchema,
1937
+ cause: diagnosisCauseSchema,
1938
+ says: agentWorkText,
1939
+ next: diagnosisNextSchema,
1940
+ change: agentWorkText.optional(),
1941
+ question: agentWorkText.optional(),
1942
+ answer: agentWorkText.optional()
1943
+ }).strict().refine((d) => d.next === "different-way" === (d.change !== void 0), {
1944
+ message: "says what will change exactly when the next step is to try a different way",
1945
+ path: ["change"]
1946
+ }).refine((d) => d.next === "ask-person" === (d.question !== void 0), {
1947
+ message: "carries a question exactly when the next step is to ask the person",
1948
+ path: ["question"]
1949
+ });
1950
+ var workCallSchema = z14.object({
1951
+ subject: workSubjectSchema,
1952
+ by: agentNameSchema,
1953
+ kind: callKindSchema,
1954
+ question: agentWorkText,
1955
+ chose: agentWorkText,
1956
+ statedDefault: agentWorkText,
1957
+ undoable: callUndoableSchema,
1958
+ how: agentWorkText.nullable(),
1959
+ at: utcIso8601
1960
+ }).strict().superRefine((c, ctx) => checkReadCallHow(c, ctx));
1961
+ function checkReadCallHow(c, ctx) {
1962
+ if (c.undoable === "yes" !== (c.how !== null)) {
1963
+ ctx.addIssue({ code: "custom", path: ["how"], message: "says how to undo it exactly when it can be undone" });
1964
+ }
1965
+ }
1966
+ var workTaskViewSchema = z14.object({
1967
+ task: buildTaskIdSchema,
1968
+ standing: workStandingSchema,
1969
+ attempts: z14.array(workAttemptSchema),
1970
+ diagnoses: z14.array(workDiagnosisSchema),
1971
+ tests: z14.array(workTestSchema)
1972
+ }).strict();
1973
+ var WORK_CLAIM_ACTS = Object.freeze(["accept", "reject"]);
1974
+ var workClaimActSchema = z14.enum(WORK_CLAIM_ACTS);
1975
+ var workClaimAnswerSchema = z14.object({
1976
+ act: workClaimActSchema,
1977
+ note: z14.string().min(1).max(SCOPE_NOTE_MAX).nullable()
1978
+ }).strict().refine((a) => a.act === "reject" === (a.note !== null), {
1979
+ message: "carries the owner's note exactly on a reject",
1980
+ path: ["note"]
1981
+ });
1982
+ var workUncheckedClaimSchema = z14.object({
1983
+ task: buildTaskIdSchema,
1984
+ label: agentWorkText,
1985
+ head: commitShaSchema,
1986
+ claimedBy: agentNameSchema,
1987
+ answer: workClaimAnswerSchema.nullable()
1988
+ }).strict();
1989
+ var phaseWorkSchema = z14.object({
1990
+ phase: buildPhaseSchema,
1991
+ tasks: z14.array(workTaskViewSchema),
1992
+ calls: z14.array(workCallSchema),
1993
+ uncheckedClaims: z14.array(workUncheckedClaimSchema)
1994
+ }).strict();
1995
+
1996
+ // ../events/dist/crew.js
1997
+ import { z as z15 } from "zod";
1998
+ var crewGroupSchema = z15.enum(["analyst-architect", "builder", "reviewer", "lead"]);
1999
+ var CREW_DISCIPLINE_MAX = 64;
2000
+ var crewFaceHeadSchema = z15.enum(["sq", "sqr", "rnd", "dome", "hex", "gable"]);
2001
+ var crewFaceAntennaSchema = z15.enum(["none", "stalk", "twin", "dish"]);
2002
+ var crewFaceEyesSchema = z15.enum(["dots", "bars", "visor", "cyclops"]);
2003
+ var crewFaceMouthSchema = z15.enum(["line", "grid", "dots", "wave"]);
2004
+ var crewFaceSchema = z15.object({
2005
+ // The outline of the head. Members who do the same kind of work share one, so a
2006
+ // team is recognisable before any name is read.
2007
+ head: crewFaceHeadSchema,
2008
+ // What sits on top of the head, if anything.
2009
+ antenna: crewFaceAntennaSchema,
2010
+ // The eyes.
2011
+ eyes: crewFaceEyesSchema,
2012
+ // The mouth.
2013
+ mouth: crewFaceMouthSchema,
2014
+ // Whether this member is drawn with ears.
2015
+ ears: z15.boolean()
2016
+ }).strict();
2017
+ var crewRoleSchema = z15.enum([
2018
+ "business-analyst",
2019
+ "architect",
2020
+ "spec-writer",
2021
+ "builder",
2022
+ "context-docs",
2023
+ "spec-reviewer",
2024
+ "code-test-reviewer",
2025
+ "delivery-lead",
2026
+ "quality-lead"
2027
+ ]);
2028
+ var crewMemberSchema = z15.object({
2029
+ // The agent's name. The same names on every project, so a name is a
2030
+ // vocabulary a returning client already knows.
2031
+ callsign: z15.string().min(1).max(32),
2032
+ // Which of the four teams this member belongs to.
2033
+ group: crewGroupSchema,
2034
+ // The part this member plays, which decides the stages it leads and helps with.
2035
+ role: crewRoleSchema,
2036
+ // The kind of work this member takes. A task names a discipline, and only
2037
+ // agents holding it can be dispatched to it.
2038
+ discipline: z15.string().min(1).max(CREW_DISCIPLINE_MAX),
2039
+ // How much thinking this member brings. There is no field naming the supplier
2040
+ // behind it, on this shape or on any other.
2041
+ tier: crewTierSchema,
2042
+ // How this member's face is drawn. The same face on every project, so a
2043
+ // returning client recognises a member before reading the name.
2044
+ face: crewFaceSchema
2045
+ }).strict();
2046
+ var CREW_ROSTER = [
2047
+ // ── analysts and architects ─────────────────────────────────────────────────
2048
+ {
2049
+ callsign: "SOFIA",
2050
+ group: "analyst-architect",
2051
+ role: "business-analyst",
2052
+ discipline: "business analyst",
2053
+ tier: "deep",
2054
+ face: { head: "gable", antenna: "stalk", eyes: "dots", mouth: "wave", ears: false }
2055
+ },
2056
+ {
2057
+ callsign: "BEA",
2058
+ group: "analyst-architect",
2059
+ role: "business-analyst",
2060
+ discipline: "business analyst",
2061
+ tier: "standard",
2062
+ face: { head: "gable", antenna: "none", eyes: "visor", mouth: "line", ears: false }
2063
+ },
2064
+ {
2065
+ callsign: "WREN",
2066
+ group: "analyst-architect",
2067
+ role: "architect",
2068
+ discipline: "solution architect",
2069
+ tier: "deep",
2070
+ face: { head: "gable", antenna: "dish", eyes: "bars", mouth: "line", ears: true }
2071
+ },
2072
+ {
2073
+ callsign: "PEI",
2074
+ group: "analyst-architect",
2075
+ role: "architect",
2076
+ discipline: "technical architect",
2077
+ tier: "standard",
2078
+ face: { head: "gable", antenna: "twin", eyes: "cyclops", mouth: "dots", ears: false }
2079
+ },
2080
+ // ── builders: code ──────────────────────────────────────────────────────────
2081
+ {
2082
+ callsign: "ADA",
2083
+ group: "builder",
2084
+ role: "builder",
2085
+ discipline: "implementation",
2086
+ tier: "deep",
2087
+ face: { head: "sq", antenna: "stalk", eyes: "dots", mouth: "line", ears: false }
2088
+ },
2089
+ {
2090
+ callsign: "OTTO",
2091
+ group: "builder",
2092
+ role: "builder",
2093
+ discipline: "implementation",
2094
+ tier: "deep",
2095
+ face: { head: "sq", antenna: "twin", eyes: "bars", mouth: "grid", ears: true }
2096
+ },
2097
+ {
2098
+ callsign: "MILO",
2099
+ group: "builder",
2100
+ role: "builder",
2101
+ discipline: "implementation",
2102
+ tier: "standard",
2103
+ face: { head: "sqr", antenna: "stalk", eyes: "dots", mouth: "dots", ears: false }
2104
+ },
2105
+ {
2106
+ callsign: "NOVA",
2107
+ group: "builder",
2108
+ role: "builder",
2109
+ discipline: "implementation",
2110
+ tier: "standard",
2111
+ face: { head: "sq", antenna: "dish", eyes: "cyclops", mouth: "line", ears: false }
2112
+ },
2113
+ {
2114
+ callsign: "HUGO",
2115
+ group: "builder",
2116
+ role: "builder",
2117
+ discipline: "infrastructure",
2118
+ tier: "standard",
2119
+ face: { head: "sq", antenna: "twin", eyes: "dots", mouth: "grid", ears: true }
2120
+ },
2121
+ // ── builders: specs and docs ────────────────────────────────────────────────
2122
+ {
2123
+ callsign: "IRIS",
2124
+ group: "builder",
2125
+ role: "spec-writer",
2126
+ discipline: "feature spec",
2127
+ tier: "deep",
2128
+ face: { head: "dome", antenna: "stalk", eyes: "visor", mouth: "line", ears: false }
2129
+ },
2130
+ {
2131
+ callsign: "JUNO",
2132
+ group: "builder",
2133
+ role: "spec-writer",
2134
+ discipline: "feature spec",
2135
+ tier: "standard",
2136
+ face: { head: "dome", antenna: "none", eyes: "dots", mouth: "wave", ears: false }
2137
+ },
2138
+ {
2139
+ callsign: "LEX",
2140
+ group: "builder",
2141
+ role: "context-docs",
2142
+ discipline: "context & docs",
2143
+ tier: "fast",
2144
+ face: { head: "dome", antenna: "twin", eyes: "bars", mouth: "dots", ears: false }
2145
+ },
2146
+ // ── reviewers ───────────────────────────────────────────────────────────────
2147
+ {
2148
+ callsign: "VERA",
2149
+ group: "reviewer",
2150
+ role: "spec-reviewer",
2151
+ discipline: "spec consistency",
2152
+ tier: "deep",
2153
+ face: { head: "rnd", antenna: "stalk", eyes: "visor", mouth: "line", ears: false }
2154
+ },
2155
+ {
2156
+ callsign: "ODIN",
2157
+ group: "reviewer",
2158
+ role: "spec-reviewer",
2159
+ discipline: "cross-spec audit",
2160
+ tier: "deep",
2161
+ face: { head: "rnd", antenna: "dish", eyes: "cyclops", mouth: "line", ears: false }
2162
+ },
2163
+ {
2164
+ callsign: "CASS",
2165
+ group: "reviewer",
2166
+ role: "code-test-reviewer",
2167
+ discipline: "code review",
2168
+ tier: "deep",
2169
+ face: { head: "rnd", antenna: "none", eyes: "visor", mouth: "grid", ears: true }
2170
+ },
2171
+ {
2172
+ callsign: "THEO",
2173
+ group: "reviewer",
2174
+ role: "code-test-reviewer",
2175
+ discipline: "code review",
2176
+ tier: "standard",
2177
+ face: { head: "rnd", antenna: "twin", eyes: "bars", mouth: "line", ears: false }
2178
+ },
2179
+ {
2180
+ callsign: "KAI",
2181
+ group: "reviewer",
2182
+ role: "code-test-reviewer",
2183
+ discipline: "code review",
2184
+ tier: "standard",
2185
+ face: { head: "rnd", antenna: "none", eyes: "bars", mouth: "line", ears: true }
2186
+ },
2187
+ {
2188
+ callsign: "ECHO",
2189
+ group: "reviewer",
2190
+ role: "code-test-reviewer",
2191
+ discipline: "test quality",
2192
+ tier: "standard",
2193
+ face: { head: "rnd", antenna: "stalk", eyes: "dots", mouth: "wave", ears: false }
2194
+ },
2195
+ {
2196
+ callsign: "MAVIS",
2197
+ group: "reviewer",
2198
+ role: "code-test-reviewer",
2199
+ discipline: "suites & coverage",
2200
+ tier: "standard",
2201
+ face: { head: "rnd", antenna: "none", eyes: "visor", mouth: "dots", ears: false }
2202
+ },
2203
+ {
2204
+ callsign: "ZARA",
2205
+ group: "reviewer",
2206
+ role: "code-test-reviewer",
2207
+ discipline: "smoke & walk aid",
2208
+ tier: "standard",
2209
+ face: { head: "rnd", antenna: "dish", eyes: "bars", mouth: "wave", ears: false }
2210
+ },
2211
+ {
2212
+ callsign: "NORA",
2213
+ group: "reviewer",
2214
+ role: "code-test-reviewer",
2215
+ discipline: "evidence & provenance",
2216
+ tier: "fast",
2217
+ face: { head: "rnd", antenna: "twin", eyes: "dots", mouth: "line", ears: false }
2218
+ },
2219
+ // ── leads ───────────────────────────────────────────────────────────────────
2220
+ {
2221
+ callsign: "ARGUS",
2222
+ group: "lead",
2223
+ role: "quality-lead",
2224
+ discipline: "quality lead \xB7 every diff",
2225
+ tier: "standard",
2226
+ face: { head: "hex", antenna: "dish", eyes: "visor", mouth: "grid", ears: true }
2227
+ },
2228
+ {
2229
+ callsign: "ATLAS",
2230
+ group: "lead",
2231
+ role: "delivery-lead",
2232
+ discipline: "delivery lead",
2233
+ tier: "deep",
2234
+ face: { head: "hex", antenna: "twin", eyes: "cyclops", mouth: "line", ears: true }
2235
+ }
2236
+ ];
2237
+ var CREW_ROSTER_SIZE = CREW_ROSTER.length;
2238
+ var CREW_CALLSIGNS = CREW_ROSTER.map((member) => member.callsign);
2239
+ var crewLayerSchema = methodLayerSchema.extract(["l0", "l1", "l2", "l3", "l4", "l5"]);
2240
+ var crewRoleLayersSchema = z15.object({
2241
+ leads: z15.array(crewLayerSchema),
2242
+ helps: z15.array(crewLayerSchema)
1810
2243
  }).strict();
1811
- function shortCommit(commit) {
1812
- return commit.slice(0, 7);
1813
- }
2244
+
2245
+ // ../events/dist/build-record-v3.js
2246
+ import { z as z18 } from "zod";
2247
+
2248
+ // ../events/dist/board-vocabulary.js
2249
+ import { z as z16 } from "zod";
2250
+ var boardStepStateSchema = z16.enum([
2251
+ "not-instrumented",
2252
+ "done",
2253
+ "did-not-pass",
2254
+ "could-not-complete",
2255
+ "not-yet-available",
2256
+ "qualified"
2257
+ ]);
2258
+ var boardGateNameSchema = z16.enum([
2259
+ "scope",
2260
+ "architecture",
2261
+ "specs",
2262
+ "taskgraph",
2263
+ "phase-close"
2264
+ ]);
1814
2265
 
1815
2266
  // ../events/dist/phase-review.js
1816
- import { z as z13 } from "zod";
2267
+ import { z as z17 } from "zod";
1817
2268
  var BUILT_PART_ACTS = Object.freeze(["accept", "question", "send-back"]);
1818
2269
  var NOT_BUILT_PART_ACTS = Object.freeze(["carry", "drop"]);
1819
2270
  var REVIEW_ACTS = Object.freeze([...BUILT_PART_ACTS, ...NOT_BUILT_PART_ACTS]);
1820
- var reviewActSchema = z13.enum(REVIEW_ACTS);
2271
+ var reviewActSchema = z17.enum(REVIEW_ACTS);
1821
2272
  function reviewActsFor(built) {
1822
2273
  return built ? BUILT_PART_ACTS : NOT_BUILT_PART_ACTS;
1823
2274
  }
1824
2275
  var REVIEW_NOTE_MAX = SCOPE_NOTE_MAX;
1825
- var reviewNoteWireSchema = z13.string().max(REVIEW_NOTE_MAX);
1826
- var reviewNoteSchema = z13.string().min(1).max(REVIEW_NOTE_MAX);
2276
+ var reviewNoteWireSchema = z17.string().max(REVIEW_NOTE_MAX);
2277
+ var reviewNoteSchema = z17.string().min(1).max(REVIEW_NOTE_MAX);
1827
2278
  var REVIEW_ANSWER_MAX = 1e3;
1828
- var reviewAgentAnswerSchema = z13.string().min(1).max(REVIEW_ANSWER_MAX);
2279
+ var reviewAgentAnswerSchema = z17.string().min(1).max(REVIEW_ANSWER_MAX);
1829
2280
  var REVIEW_PARTS_MAX = PHASE_PLAN_CHANGES_MAX * 2;
1830
2281
  var REVIEW_PART_CHANGES = Object.freeze([...PHASE_PLAN_CHANGE_KINDS, "not-in-plan"]);
1831
- var reviewPartChangeSchema = z13.enum(REVIEW_PART_CHANGES);
2282
+ var reviewPartChangeSchema = z17.enum(REVIEW_PART_CHANGES);
1832
2283
  var REVIEW_DECISIONS = Object.freeze(["accepted", "carried", "dropped"]);
1833
- var reviewDecisionSchema = z13.enum(REVIEW_DECISIONS);
2284
+ var reviewDecisionSchema = z17.enum(REVIEW_DECISIONS);
1834
2285
  function actOf(decision) {
1835
2286
  if (decision === "accepted")
1836
2287
  return "accept";
@@ -1842,10 +2293,10 @@ var reviewCarriedToSchema = carriedPhaseSchema.nullable();
1842
2293
  var reviewStepNameSchema = stepNameSchema.refine((v) => !stepIdSchema.safeParse(v.trim()).success, {
1843
2294
  message: "must be the step's plain name"
1844
2295
  });
1845
- var reviewStepSchema = z13.object({
2296
+ var reviewStepSchema = z17.object({
1846
2297
  name: reviewStepNameSchema.nullable(),
1847
2298
  state: unfinishedStepSchema.shape.state,
1848
- judged: z13.boolean()
2299
+ judged: z17.boolean()
1849
2300
  }).strict();
1850
2301
  function codeLinkLine(link) {
1851
2302
  switch (link.kind) {
@@ -1862,41 +2313,41 @@ function codeLinkLine(link) {
1862
2313
  function assertNever(value) {
1863
2314
  throw new Error(`unexpected value: ${JSON.stringify(value)}`);
1864
2315
  }
1865
- var openReviewRequestSchema = z13.object({
2316
+ var openReviewRequestSchema = z17.object({
1866
2317
  actor: closeActorSchema,
1867
2318
  acceptanceOutcome: acceptanceOutcomeSchema,
1868
2319
  statements: closeStatementsSchema
1869
2320
  }).strict();
1870
2321
  var REVIEW_OPEN_TASKS_SHOWN_MAX = 10;
1871
- var reviewOpenedSchema = z13.object({
1872
- outcome: z13.literal("opened"),
1873
- round: z13.number().int().min(1),
1874
- parts: z13.number().int().min(0).max(REVIEW_PARTS_MAX),
1875
- notBuilt: z13.number().int().min(0).max(REVIEW_PARTS_MAX),
1876
- counted: z13.boolean(),
1877
- typedReviewOffered: z13.boolean()
2322
+ var reviewOpenedSchema = z17.object({
2323
+ outcome: z17.literal("opened"),
2324
+ round: z17.number().int().min(1),
2325
+ parts: z17.number().int().min(0).max(REVIEW_PARTS_MAX),
2326
+ notBuilt: z17.number().int().min(0).max(REVIEW_PARTS_MAX),
2327
+ counted: z17.boolean(),
2328
+ typedReviewOffered: z17.boolean()
1878
2329
  }).strict().refine((v) => v.notBuilt <= v.parts, { message: "cannot exceed the parts in the round", path: ["notBuilt"] });
1879
- var reviewTasksOpenSchema = z13.object({
1880
- outcome: z13.literal("tasks-open"),
1881
- open: z13.array(buildTaskSchema.shape.title).min(1).max(REVIEW_OPEN_TASKS_SHOWN_MAX),
1882
- more: z13.number().int().min(0).max(BUILD_SNAPSHOT_TASK_LIMIT)
2330
+ var reviewTasksOpenSchema = z17.object({
2331
+ outcome: z17.literal("tasks-open"),
2332
+ open: z17.array(buildTaskSchema.shape.title).min(1).max(REVIEW_OPEN_TASKS_SHOWN_MAX),
2333
+ more: z17.number().int().min(0).max(BUILD_SNAPSHOT_TASK_LIMIT)
1883
2334
  }).strict();
1884
- var reviewOutstandingSchema = z13.object({
1885
- outcome: z13.literal("outstanding"),
1886
- steps: z13.array(reviewStepSchema).min(1)
2335
+ var reviewOutstandingSchema = z17.object({
2336
+ outcome: z17.literal("outstanding"),
2337
+ steps: z17.array(reviewStepSchema).min(1)
1887
2338
  }).strict();
1888
- var reviewCleanOverFindingsSchema = z13.object({
1889
- outcome: z13.literal("clean-over-findings"),
1890
- findings: z13.number().int().min(1)
2339
+ var reviewCleanOverFindingsSchema = z17.object({
2340
+ outcome: z17.literal("clean-over-findings"),
2341
+ findings: z17.number().int().min(1)
1891
2342
  }).strict();
1892
- var reviewRefusedSchema = z13.object({
1893
- outcome: z13.literal("refused"),
1894
- problems: z13.array(systemDescriptionProblemSchema).min(1)
2343
+ var reviewRefusedSchema = z17.object({
2344
+ outcome: z17.literal("refused"),
2345
+ problems: z17.array(systemDescriptionProblemSchema).min(1)
1895
2346
  }).strict();
1896
2347
  function bareReviewOutcome(outcome) {
1897
- return z13.object({ outcome: z13.literal(outcome) }).strict();
2348
+ return z17.object({ outcome: z17.literal(outcome) }).strict();
1898
2349
  }
1899
- var openReviewResultSchema = z13.discriminatedUnion("outcome", [
2350
+ var openReviewResultSchema = z17.discriminatedUnion("outcome", [
1900
2351
  reviewOpenedSchema,
1901
2352
  reviewTasksOpenSchema,
1902
2353
  bareReviewOutcome("no-build"),
@@ -1906,23 +2357,33 @@ var openReviewResultSchema = z13.discriminatedUnion("outcome", [
1906
2357
  reviewRefusedSchema,
1907
2358
  bareReviewOutcome("unavailable")
1908
2359
  ]);
1909
- var reviewPhaseNotOpenSchema = z13.object({
1910
- statusCode: z13.literal(409),
1911
- error: z13.literal("PhaseNotOpen"),
1912
- message: z13.string().min(1),
1913
- openPhase: z13.string().nullable()
2360
+ var reviewPhaseNotOpenSchema = z17.object({
2361
+ statusCode: z17.literal(409),
2362
+ error: z17.literal("PhaseNotOpen"),
2363
+ message: z17.string().min(1),
2364
+ openPhase: z17.string().nullable()
1914
2365
  }).strict();
1915
- var openReviewAnswerSchema = z13.union([openReviewResultSchema, reviewPhaseNotOpenSchema]);
1916
- var reviewDecisionEntrySchema = z13.object({
2366
+ var openReviewAnswerSchema = z17.union([openReviewResultSchema, reviewPhaseNotOpenSchema]);
2367
+ var reviewDecisionEntrySchema = z17.object({
1917
2368
  part: systemPartIdSchema,
1918
2369
  act: reviewActSchema,
1919
2370
  note: reviewNoteWireSchema.optional()
1920
2371
  }).strict();
1921
- var reviewDecisionRequestSchema = z13.object({
1922
- reviewVersion: z13.number().int().min(0),
1923
- decisions: z13.array(reviewDecisionEntrySchema).max(REVIEW_PARTS_MAX)
2372
+ var reviewClaimActSchema = workClaimActSchema;
2373
+ var REVIEW_CLAIMS_MAX = BUILD_SNAPSHOT_TASK_LIMIT * WORK_CLAIM_CHECKS_MAX;
2374
+ var reviewClaimAnswerSchema = z17.object({
2375
+ task: buildTaskIdSchema,
2376
+ label: agentWorkText,
2377
+ head: commitShaSchema,
2378
+ act: reviewClaimActSchema,
2379
+ note: reviewNoteWireSchema.optional()
2380
+ }).strict();
2381
+ var reviewDecisionRequestSchema = z17.object({
2382
+ reviewVersion: z17.number().int().min(0),
2383
+ decisions: z17.array(reviewDecisionEntrySchema).max(REVIEW_PARTS_MAX),
2384
+ claims: z17.array(reviewClaimAnswerSchema).max(REVIEW_CLAIMS_MAX)
1924
2385
  }).strict();
1925
- var reviewDecidedSchema = z13.object({
2386
+ var reviewDecidedSchema = z17.object({
1926
2387
  part: systemPartIdSchema,
1927
2388
  name: systemPartSchema.shape.name,
1928
2389
  act: reviewActSchema,
@@ -1931,39 +2392,51 @@ var reviewDecidedSchema = z13.object({
1931
2392
  message: "is present exactly on a carry",
1932
2393
  path: ["carriedTo"]
1933
2394
  });
1934
- var reviewRecordedSchema = z13.object({
1935
- outcome: z13.literal("recorded"),
1936
- decided: z13.array(reviewDecidedSchema).max(REVIEW_PARTS_MAX),
1937
- waiting: z13.number().int().min(0).max(REVIEW_PARTS_MAX),
1938
- closed: z13.boolean(),
1939
- reviewVersion: z13.number().int().min(1)
2395
+ var reviewRecordedSchema = z17.object({
2396
+ outcome: z17.literal("recorded"),
2397
+ decided: z17.array(reviewDecidedSchema).max(REVIEW_PARTS_MAX),
2398
+ waiting: z17.number().int().min(0).max(REVIEW_PARTS_MAX),
2399
+ closed: z17.boolean(),
2400
+ reviewVersion: z17.number().int().min(1)
1940
2401
  }).strict().superRefine((v, ctx) => {
1941
2402
  if (v.closed && v.waiting !== 0) {
1942
2403
  ctx.addIssue({ code: "custom", path: ["waiting"], message: "must be 0 once the phase is closed" });
1943
2404
  }
1944
- if (v.decided.length === 0 && !v.closed) {
1945
- ctx.addIssue({ code: "custom", path: ["decided"], message: "may be empty only when the phase closed" });
1946
- }
1947
2405
  });
1948
2406
  var REVIEW_NOT_DECIDABLE_WHY = Object.freeze(["settled", "question-waiting", "sent-back"]);
1949
- var reviewNotDecidablePartSchema = z13.object({
2407
+ var reviewNotDecidablePartSchema = z17.object({
1950
2408
  part: systemPartIdSchema,
1951
2409
  name: systemPartSchema.shape.name,
1952
- why: z13.enum(REVIEW_NOT_DECIDABLE_WHY)
2410
+ why: z17.enum(REVIEW_NOT_DECIDABLE_WHY)
2411
+ }).strict();
2412
+ var REVIEW_CLAIM_NOT_DECIDABLE_WHY = Object.freeze(["not-unchecked", "answered", "claim-changed"]);
2413
+ var reviewClaimNotDecidableWhySchema = z17.enum(REVIEW_CLAIM_NOT_DECIDABLE_WHY);
2414
+ var reviewNotDecidableClaimSchema = z17.object({
2415
+ task: buildTaskIdSchema,
2416
+ label: agentWorkText,
2417
+ why: reviewClaimNotDecidableWhySchema
1953
2418
  }).strict();
1954
- var reviewNotDecidableSchema = z13.object({
1955
- outcome: z13.literal("not-decidable"),
1956
- parts: z13.array(reviewNotDecidablePartSchema).min(1).max(REVIEW_PARTS_MAX)
2419
+ var reviewNotDecidableSchema = z17.object({
2420
+ outcome: z17.literal("not-decidable"),
2421
+ parts: z17.array(reviewNotDecidablePartSchema).max(REVIEW_PARTS_MAX),
2422
+ claims: z17.array(reviewNotDecidableClaimSchema).max(REVIEW_CLAIMS_MAX)
2423
+ }).strict().refine((v) => v.parts.length + v.claims.length > 0, {
2424
+ message: "names at least one part or claim",
2425
+ path: ["parts"]
2426
+ });
2427
+ var reviewPartsNotDecidableSchema = z17.object({
2428
+ outcome: z17.literal("not-decidable"),
2429
+ parts: z17.array(reviewNotDecidablePartSchema).min(1).max(REVIEW_PARTS_MAX)
1957
2430
  }).strict();
1958
- var reviewCloseRefusedSchema = z13.object({
1959
- outcome: z13.literal("close-refused"),
1960
- steps: z13.array(reviewStepSchema)
2431
+ var reviewCloseRefusedSchema = z17.object({
2432
+ outcome: z17.literal("close-refused"),
2433
+ steps: z17.array(reviewStepSchema)
1961
2434
  }).strict();
1962
- var reviewWalkChangedSchema = z13.object({
1963
- outcome: z13.literal("walk-changed"),
1964
- findings: z13.number().int().min(1)
2435
+ var reviewWalkChangedSchema = z17.object({
2436
+ outcome: z17.literal("walk-changed"),
2437
+ findings: z17.number().int().min(1)
1965
2438
  }).strict();
1966
- var reviewDecisionResultSchema = z13.discriminatedUnion("outcome", [
2439
+ var reviewDecisionResultSchema = z17.discriminatedUnion("outcome", [
1967
2440
  reviewRecordedSchema,
1968
2441
  bareReviewOutcome("review-changed"),
1969
2442
  bareReviewOutcome("no-review"),
@@ -1975,33 +2448,33 @@ var reviewDecisionResultSchema = z13.discriminatedUnion("outcome", [
1975
2448
  reviewRefusedSchema,
1976
2449
  bareReviewOutcome("unavailable")
1977
2450
  ]);
1978
- var reviewSentBackSchema = z13.object({
1979
- round: z13.number().int().min(1),
2451
+ var reviewSentBackSchema = z17.object({
2452
+ round: z17.number().int().min(1),
1980
2453
  note: reviewNoteSchema
1981
2454
  }).strict();
1982
- var reviewQuestionSchema = z13.object({
2455
+ var reviewQuestionSchema = z17.object({
1983
2456
  question: reviewNoteSchema,
1984
2457
  agentAnswer: reviewAgentAnswerSchema.optional()
1985
2458
  }).strict();
1986
- var reviewTaskSchema = z13.object({
2459
+ var reviewTaskSchema = z17.object({
1987
2460
  title: buildTaskSchema.shape.title,
1988
2461
  state: buildTaskStateSchema
1989
2462
  }).strict();
1990
- var reviewViewPartSchema = z13.object({
2463
+ var reviewViewPartSchema = z17.object({
1991
2464
  part: systemPartIdSchema,
1992
2465
  name: systemPartSchema.shape.name,
1993
2466
  change: reviewPartChangeSchema,
1994
- built: z13.boolean(),
1995
- builtWithoutApproval: z13.boolean(),
1996
- decidable: z13.boolean(),
1997
- waiting: z13.boolean(),
2467
+ built: z17.boolean(),
2468
+ builtWithoutApproval: z17.boolean(),
2469
+ decidable: z17.boolean(),
2470
+ waiting: z17.boolean(),
1998
2471
  decision: reviewDecisionSchema.optional(),
1999
2472
  reason: reviewNoteSchema.optional(),
2000
2473
  carriedTo: reviewCarriedToSchema.optional(),
2001
- sentBack: z13.array(reviewSentBackSchema),
2002
- questions: z13.array(reviewQuestionSchema),
2003
- tasks: z13.array(reviewTaskSchema).max(BUILD_SNAPSHOT_TASK_LIMIT),
2004
- said: z13.array(observedSaidSchema).max(BUILD_SNAPSHOT_TASK_LIMIT),
2474
+ sentBack: z17.array(reviewSentBackSchema),
2475
+ questions: z17.array(reviewQuestionSchema),
2476
+ tasks: z17.array(reviewTaskSchema).max(BUILD_SNAPSHOT_TASK_LIMIT),
2477
+ said: z17.array(observedSaidSchema).max(BUILD_SNAPSHOT_TASK_LIMIT),
2005
2478
  codeLink: partCodeLinkSchema
2006
2479
  }).strict().superRefine((v, ctx) => {
2007
2480
  if (v.waiting !== (v.decision === void 0)) {
@@ -2024,29 +2497,29 @@ var reviewViewPartSchema = z13.object({
2024
2497
  ctx.addIssue({ code: "custom", path: ["built"], message: "is always true for a part outside the plan" });
2025
2498
  }
2026
2499
  });
2027
- var reviewPhaseNumberSchema = z13.number().int().min(1).nullable();
2028
- var phaseReviewNoneSchema = z13.object({
2029
- state: z13.literal("none"),
2500
+ var reviewPhaseNumberSchema = z17.number().int().min(1).nullable();
2501
+ var phaseReviewNoneSchema = z17.object({
2502
+ state: z17.literal("none"),
2030
2503
  phase: scopePhaseSchema,
2031
2504
  number: reviewPhaseNumberSchema,
2032
- closedWithoutReview: z13.boolean()
2505
+ closedWithoutReview: z17.boolean()
2033
2506
  }).strict();
2034
- var reviewWalkSchema = z13.object({
2507
+ var reviewWalkSchema = z17.object({
2035
2508
  verdict: walkVerdictSchema,
2036
- findings: z13.number().int().min(0)
2509
+ findings: z17.number().int().min(0)
2037
2510
  }).strict();
2038
2511
  function reviewedViewSchema(state) {
2039
- return z13.object({
2040
- state: z13.literal(state),
2512
+ return z17.object({
2513
+ state: z17.literal(state),
2041
2514
  phase: scopePhaseSchema,
2042
2515
  number: reviewPhaseNumberSchema,
2043
- cycle: z13.number().int().min(0),
2044
- round: z13.number().int().min(1),
2045
- reviewVersion: z13.number().int().min(1),
2046
- typedReviewOffered: z13.boolean(),
2047
- counted: z13.boolean(),
2516
+ cycle: z17.number().int().min(0),
2517
+ round: z17.number().int().min(1),
2518
+ reviewVersion: z17.number().int().min(1),
2519
+ typedReviewOffered: z17.boolean(),
2520
+ counted: z17.boolean(),
2048
2521
  walk: reviewWalkSchema,
2049
- parts: z13.array(reviewViewPartSchema).max(REVIEW_PARTS_MAX),
2522
+ parts: z17.array(reviewViewPartSchema).max(REVIEW_PARTS_MAX),
2050
2523
  observed: observedChangesSchema.nullable(),
2051
2524
  description: systemDescriptionSchema
2052
2525
  }).strict().superRefine((v, ctx) => {
@@ -2074,7 +2547,7 @@ function reviewedViewSchema(state) {
2074
2547
  }
2075
2548
  var phaseReviewOpenSchema = reviewedViewSchema("open");
2076
2549
  var phaseReviewClosedSchema = reviewedViewSchema("closed");
2077
- var phaseReviewViewSchema = z13.discriminatedUnion("state", [
2550
+ var phaseReviewViewSchema = z17.discriminatedUnion("state", [
2078
2551
  phaseReviewNoneSchema,
2079
2552
  phaseReviewOpenSchema,
2080
2553
  phaseReviewClosedSchema
@@ -2082,45 +2555,45 @@ var phaseReviewViewSchema = z13.discriminatedUnion("state", [
2082
2555
 
2083
2556
  // ../events/dist/build-record-v3.js
2084
2557
  var BUILD_RECORD_V3_FORMAT = "halfcycle-build-record/v3";
2085
- var recordRoundStatedSchema = z14.object({
2558
+ var recordRoundStatedSchema = z18.object({
2086
2559
  actor: attestedCloseSchema.shape.actor,
2087
2560
  statements: attestedStatementsSchema
2088
2561
  }).strict();
2089
- var recordReviewRoundSchema = z14.object({
2090
- round: z14.number().int().min(1),
2562
+ var recordReviewRoundSchema = z18.object({
2563
+ round: z18.number().int().min(1),
2091
2564
  openedAt: utcIso8601,
2092
2565
  verdict: walkVerdictSchema,
2093
- findings: z14.number().int().min(0),
2566
+ findings: z18.number().int().min(0),
2094
2567
  stated: recordRoundStatedSchema.optional()
2095
2568
  }).strict();
2096
- var recordReviewApprovalSchema = z14.discriminatedUnion("kind", [
2097
- z14.object({
2098
- kind: z14.literal("approved"),
2569
+ var recordReviewApprovalSchema = z18.discriminatedUnion("kind", [
2570
+ z18.object({
2571
+ kind: z18.literal("approved"),
2099
2572
  planVersion: planVersionSchema,
2100
2573
  givenIn: scopeGivenInSchema,
2101
2574
  approvedAt: utcIso8601
2102
2575
  }).strict(),
2103
- z14.object({ kind: z14.literal("none") }).strict()
2576
+ z18.object({ kind: z18.literal("none") }).strict()
2104
2577
  ]);
2105
- var recordReviewGateSchema = z14.object({
2578
+ var recordReviewGateSchema = z18.object({
2106
2579
  gate: boardGateNameSchema,
2107
2580
  state: boardStepStateSchema
2108
2581
  }).strict();
2109
- var recordReviewQuestionSchema = z14.object({
2582
+ var recordReviewQuestionSchema = z18.object({
2110
2583
  question: reviewNoteSchema,
2111
2584
  agentAnswer: reviewAgentAnswerSchema
2112
2585
  }).strict();
2113
- var recordReviewPartSchema = z14.object({
2586
+ var recordReviewPartSchema = z18.object({
2114
2587
  part: systemPartIdSchema,
2115
2588
  name: systemPartSchema.shape.name,
2116
2589
  change: reviewPartChangeSchema,
2117
- built: z14.boolean(),
2118
- builtWithoutApproval: z14.boolean(),
2590
+ built: z18.boolean(),
2591
+ builtWithoutApproval: z18.boolean(),
2119
2592
  decision: reviewDecisionSchema,
2120
2593
  reason: reviewNoteSchema.optional(),
2121
2594
  carriedTo: reviewCarriedToSchema.optional(),
2122
- sentBack: z14.array(reviewSentBackSchema),
2123
- questions: z14.array(recordReviewQuestionSchema),
2595
+ sentBack: z18.array(reviewSentBackSchema),
2596
+ questions: z18.array(recordReviewQuestionSchema),
2124
2597
  codeLink: partCodeLinkSchema
2125
2598
  }).strict().superRefine((v, ctx) => {
2126
2599
  const carriedOrDropped = v.decision === "carried" || v.decision === "dropped";
@@ -2132,23 +2605,24 @@ var recordReviewPartSchema = z14.object({
2132
2605
  }
2133
2606
  });
2134
2607
  var SPINE_ORDER = boardGateNameSchema.options;
2135
- var recordReviewGatesSchema = z14.array(recordReviewGateSchema).superRefine((gates, ctx) => {
2608
+ var recordReviewGatesSchema = z18.array(recordReviewGateSchema).superRefine((gates, ctx) => {
2136
2609
  const inOrder = gates.length === SPINE_ORDER.length && gates.every((g, i) => g.gate === SPINE_ORDER[i]);
2137
2610
  if (!inOrder) {
2138
2611
  ctx.addIssue({ code: "custom", message: `must be exactly the five checkpoints in order: ${SPINE_ORDER.join(", ")}` });
2139
2612
  }
2140
2613
  });
2141
- var recordReviewSchema = z14.object({
2142
- sequence: z14.number().int().min(1),
2143
- cycle: z14.number().int().min(0),
2144
- rounds: z14.array(recordReviewRoundSchema).min(1),
2614
+ var recordReviewSchema = z18.object({
2615
+ sequence: z18.number().int().min(1),
2616
+ cycle: z18.number().int().min(0),
2617
+ rounds: z18.array(recordReviewRoundSchema).min(1),
2145
2618
  approval: recordReviewApprovalSchema,
2146
2619
  planVersion: planVersionSchema.nullable(),
2147
2620
  descriptionVersion: observedDescriptionVersionSchema,
2148
2621
  gates: recordReviewGatesSchema,
2149
2622
  observed: observedChangesSchema.nullable(),
2150
- parts: z14.array(recordReviewPartSchema).max(REVIEW_PARTS_MAX)
2151
- }).strict().superRefine((v, ctx) => {
2623
+ parts: z18.array(recordReviewPartSchema).max(REVIEW_PARTS_MAX)
2624
+ }).strict().superRefine((v, ctx) => checkRecordReview(v, ctx));
2625
+ function checkRecordReview(v, ctx) {
2152
2626
  v.rounds.forEach((r, i) => {
2153
2627
  if (r.round !== i + 1) {
2154
2628
  ctx.addIssue({ code: "custom", path: ["rounds", i, "round"], message: "rounds must be numbered 1, 2, 3 \u2026 in order with no gap" });
@@ -2166,12 +2640,13 @@ var recordReviewSchema = z14.object({
2166
2640
  ctx.addIssue({ code: "custom", path: ["parts", i, "codeLink"], message: "must be none when nothing was observed" });
2167
2641
  }
2168
2642
  });
2169
- });
2170
- var buildRecordV3Schema = z14.object({
2643
+ }
2644
+ var buildRecordV3Schema = z18.object({
2171
2645
  ...buildRecordV2Schema.shape,
2172
- format: z14.literal(BUILD_RECORD_V3_FORMAT),
2646
+ format: z18.literal(BUILD_RECORD_V3_FORMAT),
2173
2647
  review: recordReviewSchema
2174
- }).strict().superRefine((record2, ctx) => {
2648
+ }).strict().superRefine((record2, ctx) => checkReviewedRecord(record2, ctx));
2649
+ function checkReviewedRecord(record2, ctx) {
2175
2650
  checkRecordHalves(record2, ctx);
2176
2651
  const closes = record2.observed.closes;
2177
2652
  const newest = closes[closes.length - 1].sequence;
@@ -2197,24 +2672,11 @@ var buildRecordV3Schema = z14.object({
2197
2672
  if (record2.review.observed !== null && record2.review.observed.phase !== record2.phase) {
2198
2673
  ctx.addIssue({ code: "custom", path: ["review", "observed", "phase"], message: "must be this record's phase" });
2199
2674
  }
2200
- });
2201
- var buildRecordSchema = z14.discriminatedUnion("format", [buildRecordV2Schema, buildRecordV3Schema]);
2202
- function removalsOf(record2) {
2203
- if (record2.format !== BUILD_RECORD_V3_FORMAT)
2204
- return [];
2205
- const out = [];
2206
- for (const p of record2.review.parts) {
2207
- if (p.change === "add" && p.decision === "dropped")
2208
- out.push({ part: p.part, name: p.name, act: "dropped" });
2209
- else if (p.change === "remove" && p.decision === "accepted")
2210
- out.push({ part: p.part, name: p.name, act: "removed" });
2211
- }
2212
- return out;
2213
2675
  }
2214
2676
 
2215
2677
  // ../events/dist/device-auth.js
2216
- import { z as z15 } from "zod";
2217
- var utcIso86012 = z15.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/, "must be a UTC ISO-8601 timestamp ending in Z");
2678
+ import { z as z19 } from "zod";
2679
+ var utcIso86012 = z19.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/, "must be a UTC ISO-8601 timestamp ending in Z");
2218
2680
  var DEVICE_AUTH_STATUS = {
2219
2681
  /** The poll: minted, nobody has acted yet. Keep polling. 200. */
2220
2682
  PENDING: "pending",
@@ -2255,25 +2717,25 @@ var DEVICE_POLL_REFUSALS = [
2255
2717
  DEVICE_AUTH_STATUS.EXPIRED,
2256
2718
  DEVICE_AUTH_STATUS.UNKNOWN
2257
2719
  ];
2258
- var deviceAuthStartResponseSchema = z15.object({
2259
- deviceCode: z15.string().min(1),
2260
- userCode: z15.string().min(1),
2261
- verificationUrl: z15.string().min(1),
2720
+ var deviceAuthStartResponseSchema = z19.object({
2721
+ deviceCode: z19.string().min(1),
2722
+ userCode: z19.string().min(1),
2723
+ verificationUrl: z19.string().min(1),
2262
2724
  expiresAt: utcIso86012,
2263
- pollIntervalMs: z15.number().int().positive()
2725
+ pollIntervalMs: z19.number().int().positive()
2264
2726
  }).strict();
2265
- var devicePollPendingSchema = z15.object({
2266
- status: z15.literal(DEVICE_AUTH_STATUS.PENDING),
2267
- pollIntervalMs: z15.number().int().positive()
2727
+ var devicePollPendingSchema = z19.object({
2728
+ status: z19.literal(DEVICE_AUTH_STATUS.PENDING),
2729
+ pollIntervalMs: z19.number().int().positive()
2268
2730
  }).strict();
2269
- var devicePollApprovedSchema = z15.object({
2270
- status: z15.literal(DEVICE_AUTH_STATUS.APPROVED),
2271
- accountId: z15.string().min(1),
2272
- credential: z15.string().min(1),
2731
+ var devicePollApprovedSchema = z19.object({
2732
+ status: z19.literal(DEVICE_AUTH_STATUS.APPROVED),
2733
+ accountId: z19.string().min(1),
2734
+ credential: z19.string().min(1),
2273
2735
  expiresAt: utcIso86012
2274
2736
  }).strict();
2275
- var deviceAuthRefusalSchema = z15.object({
2276
- status: z15.enum([
2737
+ var deviceAuthRefusalSchema = z19.object({
2738
+ status: z19.enum([
2277
2739
  DEVICE_AUTH_STATUS.DECLINED,
2278
2740
  DEVICE_AUTH_STATUS.EXPIRED,
2279
2741
  DEVICE_AUTH_STATUS.UNKNOWN,
@@ -2284,25 +2746,25 @@ var deviceAuthRefusalSchema = z15.object({
2284
2746
  DEVICE_AUTH_STATUS.ALREADY_DECIDED,
2285
2747
  DEVICE_AUTH_STATUS.TERMS_REQUIRED
2286
2748
  ]),
2287
- message: z15.string().min(1)
2749
+ message: z19.string().min(1)
2288
2750
  }).strict();
2289
- var devicePollResponseSchema = z15.union([
2751
+ var devicePollResponseSchema = z19.union([
2290
2752
  devicePollPendingSchema,
2291
2753
  devicePollApprovedSchema,
2292
2754
  deviceAuthRefusalSchema
2293
2755
  ]);
2294
- var deviceConfirmRequestSchema = z15.object({
2295
- userCode: z15.string().min(1),
2296
- decision: z15.enum(["approve", "decline"]),
2297
- externalAuthId: z15.string().min(1).optional(),
2298
- email: z15.string().optional(),
2299
- acceptTerms: z15.boolean().optional()
2756
+ var deviceConfirmRequestSchema = z19.object({
2757
+ userCode: z19.string().min(1),
2758
+ decision: z19.enum(["approve", "decline"]),
2759
+ externalAuthId: z19.string().min(1).optional(),
2760
+ email: z19.string().optional(),
2761
+ acceptTerms: z19.boolean().optional()
2300
2762
  }).strict();
2301
- var deviceConfirmRecordedSchema = z15.object({
2302
- status: z15.literal(DEVICE_AUTH_STATUS.RECORDED),
2303
- accountId: z15.string().min(1).optional()
2763
+ var deviceConfirmRecordedSchema = z19.object({
2764
+ status: z19.literal(DEVICE_AUTH_STATUS.RECORDED),
2765
+ accountId: z19.string().min(1).optional()
2304
2766
  }).strict();
2305
- var deviceConfirmResponseSchema = z15.union([
2767
+ var deviceConfirmResponseSchema = z19.union([
2306
2768
  deviceConfirmRecordedSchema,
2307
2769
  deviceAuthRefusalSchema
2308
2770
  ]);
@@ -2314,20 +2776,20 @@ function projectPageUrl(engagementId) {
2314
2776
  }
2315
2777
 
2316
2778
  // ../events/dist/credential-wire.js
2317
- import { z as z16 } from "zod";
2318
- var engagementCredentialResponseSchema = z16.object({
2319
- engagementId: z16.string(),
2320
- sessionToken: z16.string(),
2321
- mcpUrl: z16.string(),
2322
- guardUrl: z16.string(),
2323
- controlTelemetryUrl: z16.string(),
2324
- methodVersion: z16.string().optional(),
2325
- consoleUrl: z16.string().optional()
2779
+ import { z as z20 } from "zod";
2780
+ var engagementCredentialResponseSchema = z20.object({
2781
+ engagementId: z20.string(),
2782
+ sessionToken: z20.string(),
2783
+ mcpUrl: z20.string(),
2784
+ guardUrl: z20.string(),
2785
+ controlTelemetryUrl: z20.string(),
2786
+ methodVersion: z20.string().optional(),
2787
+ consoleUrl: z20.string().optional()
2326
2788
  }).strict();
2327
- var ciTokenExchangeResponseSchema = z16.object({
2328
- token: z16.string(),
2789
+ var ciTokenExchangeResponseSchema = z20.object({
2790
+ token: z20.string(),
2329
2791
  expiresAt: utcIso8601,
2330
- engagementId: z16.string()
2792
+ engagementId: z20.string()
2331
2793
  }).strict();
2332
2794
  var NO_CREDENTIAL_PLACEHOLDER = "halfcycle:no-credential";
2333
2795
  var NOT_THIS_ACCOUNT_PLACEHOLDER = "halfcycle:not-this-account";
@@ -2344,42 +2806,118 @@ var PLACEHOLDER_SENTENCES = Object.freeze({
2344
2806
  });
2345
2807
 
2346
2808
  // ../events/dist/phase-record.js
2347
- import { z as z17 } from "zod";
2348
- var phaseRecordRefusalReasonSchema = z17.enum([
2809
+ import { z as z22 } from "zod";
2810
+
2811
+ // ../events/dist/build-record-v4.js
2812
+ import { z as z21 } from "zod";
2813
+ var BUILD_RECORD_V4_FORMAT = "halfcycle-build-record/v4";
2814
+ var recordReviewClaimSchema = z21.object({
2815
+ ...reviewClaimAnswerSchema.shape,
2816
+ note: reviewNoteSchema.nullable()
2817
+ }).strict().superRefine((c, ctx) => {
2818
+ if (c.act === "accept" !== (c.note === null)) {
2819
+ ctx.addIssue({ code: "custom", path: ["note"], message: "carries the owner's note exactly on a reject" });
2820
+ }
2821
+ });
2822
+ var recordCallSchema = z21.object(workCallSchema.shape).omit({ at: true }).strict().superRefine((c, ctx) => checkReadCallHow(c, ctx));
2823
+ var recordReviewV4Schema = z21.object({
2824
+ ...recordReviewSchema.shape,
2825
+ claims: z21.array(recordReviewClaimSchema).max(REVIEW_CLAIMS_MAX),
2826
+ calls: z21.array(recordCallSchema)
2827
+ }).strict().superRefine((v, ctx) => checkRecordReview(v, ctx));
2828
+ var buildRecordV4Schema = z21.object({
2829
+ ...buildRecordV3Schema.shape,
2830
+ format: z21.literal(BUILD_RECORD_V4_FORMAT),
2831
+ path: phasePathSchema,
2832
+ review: recordReviewV4Schema
2833
+ }).strict().superRefine((record2, ctx) => checkReviewedRecord(record2, ctx));
2834
+ var buildRecordSchema = z21.discriminatedUnion("format", [
2835
+ buildRecordV2Schema,
2836
+ buildRecordV3Schema,
2837
+ buildRecordV4Schema
2838
+ ]);
2839
+ var OWNER_REVIEW_FORMATS = Object.freeze([BUILD_RECORD_V3_FORMAT, BUILD_RECORD_V4_FORMAT]);
2840
+ function hasOwnerReview(record2) {
2841
+ return OWNER_REVIEW_FORMATS.includes(record2.format);
2842
+ }
2843
+ function keptClaimsOf(record2) {
2844
+ return record2.format === BUILD_RECORD_V4_FORMAT ? record2.review.claims : null;
2845
+ }
2846
+ function keptCallsOf(record2) {
2847
+ return record2.format === BUILD_RECORD_V4_FORMAT ? record2.review.calls : null;
2848
+ }
2849
+ function keptPathOf(record2) {
2850
+ return record2.format === BUILD_RECORD_V4_FORMAT ? record2.path : null;
2851
+ }
2852
+ function removalsOf(record2) {
2853
+ if (!hasOwnerReview(record2))
2854
+ return [];
2855
+ const out = [];
2856
+ for (const p of record2.review.parts) {
2857
+ if (p.change === "add" && p.decision === "dropped")
2858
+ out.push({ part: p.part, name: p.name, act: "dropped" });
2859
+ else if (p.change === "remove" && p.decision === "accepted")
2860
+ out.push({ part: p.part, name: p.name, act: "removed" });
2861
+ }
2862
+ return out;
2863
+ }
2864
+
2865
+ // ../events/dist/phase-record.js
2866
+ var phaseRecordRefusalReasonSchema = z22.enum([
2349
2867
  "still-open",
2350
2868
  "reopened",
2351
2869
  "never-opened",
2352
2870
  "never-closed",
2353
2871
  "closed-before-records"
2354
2872
  ]);
2355
- var phaseRecordRecordSchema = z17.object({
2356
- outcome: z17.literal("record"),
2873
+ var phaseRecordRecordSchema = z22.object({
2874
+ outcome: z22.literal("record"),
2357
2875
  phase: phaseNameSchema,
2358
2876
  record: buildRecordSchema
2359
2877
  }).strict().refine((body) => body.record.phase === body.phase, {
2360
2878
  message: "the record is for a different phase than the one asked for",
2361
2879
  path: ["record", "phase"]
2362
2880
  });
2363
- var phaseRecordWithheldSchema = z17.object({
2364
- outcome: z17.literal("withheld"),
2881
+ var phaseRecordWithheldSchema = z22.object({
2882
+ outcome: z22.literal("withheld"),
2365
2883
  phase: phaseNameSchema,
2366
- sequence: z17.number().int().min(1),
2884
+ sequence: z22.number().int().min(1),
2367
2885
  closedAt: utcIso8601,
2368
- message: z17.string()
2886
+ message: z22.string()
2369
2887
  }).strict();
2370
2888
  var phaseRecordRefusedSchema = wireErrorSchema.extend({
2371
- outcome: z17.literal("refused"),
2889
+ outcome: z22.literal("refused"),
2372
2890
  phase: phaseNameSchema,
2373
- statusCode: z17.literal(409),
2374
- error: z17.literal("PhaseRecordRefused"),
2891
+ statusCode: z22.literal(409),
2892
+ error: z22.literal("PhaseRecordRefused"),
2375
2893
  reason: phaseRecordRefusalReasonSchema
2376
2894
  }).strict();
2377
- var phaseRecordResponseSchema = z17.discriminatedUnion("outcome", [
2895
+ var phaseRecordResponseSchema = z22.discriminatedUnion("outcome", [
2378
2896
  phaseRecordRecordSchema,
2379
2897
  phaseRecordWithheldSchema,
2380
2898
  phaseRecordRefusedSchema
2381
2899
  ]);
2382
2900
 
2901
+ // ../events/dist/work-words.js
2902
+ var CALLS_HEADING = "Calls made without asking you";
2903
+ var CALLS_EXPLAINED = "These are the decisions the agents recorded taking on their own, each against a stated default. Halfcycle lists what they recorded; it cannot see a call an agent did not record.";
2904
+ function callMadeLine(call) {
2905
+ return `${call.by} decided "${call.question}": chose "${call.chose}". The stated default was "${call.statedDefault}".`;
2906
+ }
2907
+ function callUndoLine(call) {
2908
+ return call.undoable === "yes" ? `${call.by} says this can be undone: "${call.how ?? ""}"` : `${call.by} says this cannot be undone.`;
2909
+ }
2910
+ var NO_CALLS_LINE = "No agent recorded a call made without asking in this phase.";
2911
+ var UNCHECKED_CLAIMS_HEADING = "Checks nobody ran";
2912
+ function claimSaidLine(claim) {
2913
+ return `Task ${claim.task}: an agent said "${claim.label}" passes at commit ${claim.head}, and Halfcycle never ran it.`;
2914
+ }
2915
+ function recordedClaimLine(claim) {
2916
+ const said = claimSaidLine(claim);
2917
+ return claim.act === "accept" ? `${said} Accepted by the project's owner.` : `${said} Rejected by the project's owner: "${claim.note ?? ""}"`;
2918
+ }
2919
+ var NO_UNCHECKED_CLAIMS_LINE = "No agent said a check passes that Halfcycle never ran.";
2920
+
2383
2921
  // dist/engagement-credential.js
2384
2922
  import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2385
2923
  import { platform as platform2 } from "node:os";
@@ -2849,8 +3387,12 @@ function parseManifest(text) {
2849
3387
  "eventsCreated",
2850
3388
  "hooksCreated",
2851
3389
  "permissionsCreated",
2852
- "denyCreated"
3390
+ "denyCreated",
3391
+ "statusLineSha256"
2853
3392
  ], '"settings"');
3393
+ if (s["statusLineSha256"] !== void 0 && (typeof s["statusLineSha256"] !== "string" || !HEX64.test(s["statusLineSha256"]))) {
3394
+ throw new UnreadableManifestError('"settings.statusLineSha256" is not a hash');
3395
+ }
2854
3396
  const settings = {
2855
3397
  created: bool(s["created"], "settings.created"),
2856
3398
  adopted: bool(s["adopted"], "settings.adopted"),
@@ -2860,7 +3402,8 @@ function parseManifest(text) {
2860
3402
  eventsCreated: stringArray(s["eventsCreated"], "settings.eventsCreated"),
2861
3403
  hooksCreated: bool(s["hooksCreated"], "settings.hooksCreated"),
2862
3404
  permissionsCreated: bool(s["permissionsCreated"], "settings.permissionsCreated"),
2863
- denyCreated: bool(s["denyCreated"], "settings.denyCreated")
3405
+ denyCreated: bool(s["denyCreated"], "settings.denyCreated"),
3406
+ ...typeof s["statusLineSha256"] === "string" ? { statusLineSha256: s["statusLineSha256"] } : {}
2864
3407
  };
2865
3408
  let mcp;
2866
3409
  if (raw["mcp"] !== void 0) {
@@ -2938,6 +3481,7 @@ function recordSettings(draft, observed) {
2938
3481
  draft.settings = observed;
2939
3482
  return;
2940
3483
  }
3484
+ const statusLineSha256 = observed.statusLineSha256 ?? prev.statusLineSha256;
2941
3485
  draft.settings = {
2942
3486
  created: prev.created,
2943
3487
  adopted: prev.adopted,
@@ -2947,9 +3491,17 @@ function recordSettings(draft, observed) {
2947
3491
  eventsCreated: union(prev.eventsCreated, observed.eventsCreated),
2948
3492
  hooksCreated: prev.hooksCreated,
2949
3493
  permissionsCreated: prev.permissionsCreated,
2950
- denyCreated: prev.denyCreated
3494
+ denyCreated: prev.denyCreated,
3495
+ ...statusLineSha256 !== void 0 ? { statusLineSha256 } : {}
2951
3496
  };
2952
3497
  }
3498
+ function recordDenyRetired(draft, hashes) {
3499
+ const settings = draft.settings;
3500
+ if (settings === void 0 || hashes.length === 0)
3501
+ return;
3502
+ const gone = new Set(hashes);
3503
+ draft.settings = { ...settings, denyAddedSha256: settings.denyAddedSha256.filter((h) => !gone.has(h)) };
3504
+ }
2953
3505
  function recordMcp(draft, observed) {
2954
3506
  const prev = draft.mcp;
2955
3507
  if (prev === void 0) {
@@ -3034,7 +3586,7 @@ function withoutHalfcycleCommands(entry, halfcycleCommands) {
3034
3586
  const remaining = entry.hooks.filter((h) => !isHalfcycleCommand(h, halfcycleCommands));
3035
3587
  return remaining.length > 0 ? { ...entry, hooks: remaining } : null;
3036
3588
  }
3037
- function mergeSettings(existing, generated) {
3589
+ function mergeSettings(existing, generated, retiredOurs, statusLine) {
3038
3590
  const merged = { ...existing };
3039
3591
  if (generated.$schema !== void 0) {
3040
3592
  merged.$schema = generated.$schema;
@@ -3056,18 +3608,30 @@ function mergeSettings(existing, generated) {
3056
3608
  mergedHooks[event] = [...developerEntries, ...halfcycleEntries];
3057
3609
  }
3058
3610
  merged.hooks = mergedHooks;
3059
- merged.permissions = mergePermissions(existing.permissions, generated.permissions);
3611
+ merged.permissions = mergePermissions(existing.permissions, generated.permissions, retiredOurs);
3612
+ if (statusLine !== void 0 && !("statusLine" in existing)) {
3613
+ merged.statusLine = { ...statusLine };
3614
+ }
3060
3615
  return merged;
3061
3616
  }
3062
- function mergePermissions(existing, generated) {
3617
+ function mergePermissions(existing, generated, retiredOurs) {
3063
3618
  if (existing === void 0 && generated === void 0)
3064
3619
  return void 0;
3065
3620
  const result = { ...existing ?? {} };
3621
+ const theirs = Array.isArray(existing?.deny) ? [...existing.deny] : void 0;
3622
+ if (theirs !== void 0) {
3623
+ for (const rule of retiredOurs) {
3624
+ const at = theirs.lastIndexOf(rule);
3625
+ if (at >= 0)
3626
+ theirs.splice(at, 1);
3627
+ }
3628
+ result.deny = theirs;
3629
+ }
3066
3630
  const generatedDeny = generated?.deny ?? [];
3067
3631
  if (generatedDeny.length > 0) {
3068
- const theirs = existing?.deny ?? [];
3069
- const seen = new Set(theirs);
3070
- result.deny = [...theirs, ...generatedDeny.filter((p) => !seen.has(p))];
3632
+ const kept = theirs ?? [];
3633
+ const seen = new Set(kept);
3634
+ result.deny = [...kept, ...generatedDeny.filter((p) => !seen.has(p))];
3071
3635
  }
3072
3636
  return result;
3073
3637
  }
@@ -3166,6 +3730,17 @@ function resolveVendoredBinary() {
3166
3730
  }
3167
3731
  throw new Error(`[halfcycle] Cannot find the self-contained guard binary bin.bundle.mjs. This copy of the halfcycle package is incomplete \u2014 reinstall it, or rebuild it if you are working on it. Looked in: ${candidates.join(", ")}.`);
3168
3732
  }
3733
+ function resolvePreFilterShell() {
3734
+ const candidates = [
3735
+ join5(BUNDLE_ROOT, "bin", PRE_FILTER_SHELL_FILENAME),
3736
+ join5(BUNDLE_ROOT, "..", "runner", "dist", PRE_FILTER_SHELL_FILENAME)
3737
+ ];
3738
+ for (const candidate of candidates) {
3739
+ if (existsSync3(candidate))
3740
+ return readFileSync4(candidate, "utf-8");
3741
+ }
3742
+ throw new Error(`[halfcycle] Cannot find ${PRE_FILTER_SHELL_FILENAME}. This copy of the halfcycle package is incomplete \u2014 reinstall it, or rebuild it if you are working on it. Looked in: ${candidates.join(", ")}.`);
3743
+ }
3169
3744
  var WRITE_ALLOWLIST = [
3170
3745
  ".claude/commands",
3171
3746
  ".claude/hooks",
@@ -3200,7 +3775,18 @@ function commandStubRel(name) {
3200
3775
  }
3201
3776
  var RETIRED_MCP_HEADERS_HELPER_REL = ".halfcycle/mcp-headers.sh";
3202
3777
  var RETIRED_WRITE_SET_FILES = [RETIRED_MCP_HEADERS_HELPER_REL];
3203
- var RETIRED_DENY_PATTERNS = [];
3778
+ var RETIRED_DENY_PATTERNS = [
3779
+ "Bash(rm -rf:*)",
3780
+ "Bash(git push --force:*)",
3781
+ "Bash(git push -f:*)",
3782
+ "Bash(git reset --hard:*)",
3783
+ "Bash(pnpm publish:*)",
3784
+ "Bash(npm publish:*)"
3785
+ ];
3786
+ function retiredRulesThisInstallAdded(previous) {
3787
+ const added = new Set(previous?.settings.denyAddedSha256 ?? []);
3788
+ return RETIRED_DENY_PATTERNS.filter((rule) => added.has(sha256Hex(rule)));
3789
+ }
3204
3790
  function closedWriteSet() {
3205
3791
  const commands = readPluginManifest().commands ?? [];
3206
3792
  const files = /* @__PURE__ */ new Set([
@@ -3235,7 +3821,8 @@ function closedWriteSet() {
3235
3821
  GITATTRIBUTES_HEADER,
3236
3822
  ...lfRules([...Object.keys(OWNED_GENERATED_HEADERS), ...RETIRED_WRITE_SET_FILES])
3237
3823
  ]),
3238
- generatedSha256: sha256OfWhatThisVersionWrites(commands)
3824
+ generatedSha256: sha256OfWhatThisVersionWrites(commands),
3825
+ statusLineSha256: /* @__PURE__ */ new Set([canonicalSha256(HALFCYCLE_STATUS_LINE)])
3239
3826
  };
3240
3827
  }
3241
3828
  function sha256OfWhatThisVersionWrites(commands) {
@@ -3270,7 +3857,7 @@ function noteFile(draft, targetRepo, rel, outcome) {
3270
3857
  }
3271
3858
  recordInstalledFile(draft, rel, readFileSync4(join5(targetRepo, rel)));
3272
3859
  }
3273
- function recordSettingsMerge(draft, existing, generated, preexisted) {
3860
+ function recordSettingsMerge(draft, existing, generated, preexisted, merged) {
3274
3861
  const generatedHooks = generated.hooks ?? {};
3275
3862
  const hookCommands = Object.values(generatedHooks).flatMap((entries) => entries.flatMap((entry) => entry.hooks.map((h) => h.command)));
3276
3863
  const ours = /* @__PURE__ */ new Set([...hookCommands, ...HALFCYCLE_HOOK_COMMANDS]);
@@ -3287,9 +3874,16 @@ function recordSettingsMerge(draft, existing, generated, preexisted) {
3287
3874
  eventsCreated: Object.keys(generatedHooks).filter((event) => !(existingHooks && event in existingHooks)),
3288
3875
  hooksCreated: existing.hooks === void 0,
3289
3876
  permissionsCreated: existing.permissions === void 0,
3290
- denyCreated: existing.permissions?.deny === void 0
3877
+ denyCreated: existing.permissions?.deny === void 0,
3878
+ ...ourStatusLineSha256(merged) !== void 0 ? { statusLineSha256: ourStatusLineSha256(merged) } : {}
3291
3879
  });
3292
3880
  }
3881
+ function ourStatusLineSha256(settings) {
3882
+ if (!("statusLine" in settings))
3883
+ return void 0;
3884
+ const hash = canonicalSha256(settings["statusLine"]);
3885
+ return hash === canonicalSha256(HALFCYCLE_STATUS_LINE) ? hash : void 0;
3886
+ }
3293
3887
  function recordMcpMerge(draft, existingText, writtenText) {
3294
3888
  const before = existingText === null ? void 0 : JSON.parse(existingText);
3295
3889
  const servers = isPlainObject(before) ? before["mcpServers"] : void 0;
@@ -3514,12 +4108,6 @@ function record(report, outcome, rel) {
3514
4108
  bucket[outcome].push(rel);
3515
4109
  }
3516
4110
  var DENY_PATTERNS = [
3517
- "Bash(rm -rf:*)",
3518
- "Bash(git push --force:*)",
3519
- "Bash(git push -f:*)",
3520
- "Bash(git reset --hard:*)",
3521
- "Bash(pnpm publish:*)",
3522
- "Bash(npm publish:*)",
3523
4111
  "Bash(gh repo delete:*)",
3524
4112
  "Bash(git push --no-verify:*)",
3525
4113
  "Bash(git commit --no-verify:*)"
@@ -3546,6 +4134,29 @@ function settingsParseRefusal(targetRepo, err) {
3546
4134
  return `${file} could not be read as JSON${where} \u2014 fix it and run "npx halfcycle" again.`;
3547
4135
  }
3548
4136
  var SETTINGS_SCHEMA = "https://json.schemastore.org/claude-code-settings.json";
4137
+ var HALFCYCLE_STATUS_LINE = Object.freeze({
4138
+ type: "command",
4139
+ command: 'bash "$CLAUDE_PROJECT_DIR"/.claude/hooks/guard-runner.sh statusline'
4140
+ });
4141
+ function statusLineSetElsewhere(targetRepo, home = homedir2(), env = process.env) {
4142
+ const places = [join5(targetRepo, ".claude", "settings.local.json"), join5(home, ".claude", "settings.json")];
4143
+ const configDir = env["CLAUDE_CONFIG_DIR"];
4144
+ if (configDir !== void 0 && configDir !== "") {
4145
+ const expanded = configDir === "~" ? home : configDir.startsWith("~/") ? join5(home, configDir.slice(2)) : configDir;
4146
+ places.push(join5(isAbsolute2(expanded) ? expanded : resolve(expanded), "settings.json"));
4147
+ }
4148
+ return places.some((path) => {
4149
+ if (!existsSync3(path))
4150
+ return false;
4151
+ let value;
4152
+ try {
4153
+ value = JSON.parse(readFileSync4(path, "utf-8"));
4154
+ } catch {
4155
+ return true;
4156
+ }
4157
+ return !isPlainObject(value) || "statusLine" in value;
4158
+ });
4159
+ }
3549
4160
  function generateSettingsJson() {
3550
4161
  const hooks = {};
3551
4162
  for (const { event, matcher, timeout, command } of HALFCYCLE_HOOK_WIRING) {
@@ -3566,7 +4177,7 @@ function generateSettingsJson() {
3566
4177
  return JSON.stringify(settings, null, 2) + "\n";
3567
4178
  }
3568
4179
  var GENERATED_GUARD_RUNNER_HEADER = "# Governance hook wrapper \u2014 generated by";
3569
- function generateGuardRunnerWrapper() {
4180
+ function generateGuardRunnerWrapper(preFilter = resolvePreFilterShell()) {
3570
4181
  return `#!/usr/bin/env bash
3571
4182
  ${GENERATED_GUARD_RUNNER_HEADER} the Halfcycle installer.
3572
4183
  #
@@ -3610,13 +4221,119 @@ if [ "$HALFCYCLE_ENV_NOT_THIS_ACCOUNT" = "1" ]; then
3610
4221
  export HALFCYCLE_NOT_THIS_ACCOUNT=1
3611
4222
  fi
3612
4223
 
4224
+ ${preToolUseFallbackShell()}
4225
+
4226
+ ${preFilter.trimEnd()}
4227
+
4228
+ # Before a tool runs, on a bash new enough for the cheap pass: read the call
4229
+ # once; when no risky action can be in it, answer here (the pass writes the
4230
+ # time line) and do not start Node. Otherwise hand the same input on below.
4231
+ if [ "$1" = hook ] && [ "$2" = pre-tool-use ] && [ "\${BASH_VERSINFO[0]:-0}" -ge 5 ]; then
4232
+ hc_input=$(cat 2>/dev/null) || IFS= read -r -d '' hc_input
4233
+ ${PRE_FILTER_FUNCTION} && exit 0
4234
+ node -e "${nodeFloorProgram("")}" || {
4235
+ printf '%s' "$hc_input" | halfcycle_pre_tool_use_fallback
4236
+ exit 0
4237
+ }
4238
+ exec node "$PROJECT_DIR/${VENDORED_BIN_REL}" "$@" <<< "$hc_input"
4239
+ fi
4240
+
3613
4241
  # On a Node older than Halfcycle supports, the runner cannot even be read, so
3614
- # say that in one line instead of handing it a stack trace.
3615
- node -e "${nodeFloorProgram("")}" || exit
4242
+ # say that in one line instead of handing it a stack trace. Before a tool runs,
4243
+ # the check itself is what cannot start, so the commands it would have stopped
4244
+ # are stopped here instead and everything else goes ahead.
4245
+ node -e "${nodeFloorProgram("")}" || {
4246
+ hc_node_status=$?
4247
+ if [ "$1" = hook ] && [ "$2" = pre-tool-use ]; then
4248
+ halfcycle_pre_tool_use_fallback
4249
+ exit 0
4250
+ fi
4251
+ exit "$hc_node_status"
4252
+ }
3616
4253
 
3617
4254
  exec node "$PROJECT_DIR/${VENDORED_BIN_REL}" "$@"
3618
4255
  `;
3619
4256
  }
4257
+ var PRE_TOOL_USE_FALLBACK_SENTENCE = `Halfcycle: This command was stopped. Halfcycle's check is not running, because Node is not installed or is older than version ${NODE_FLOOR_MAJOR}, so Halfcycle cannot tell what the command would delete, overwrite or publish. To fix it, install Node ${NODE_FLOOR_MAJOR} or newer and open a new session. Nothing was changed.`;
4258
+ function retiredDenyPrefixes() {
4259
+ return RETIRED_DENY_PATTERNS.map((rule) => {
4260
+ const m = /^Bash\((.+):\*\)$/.exec(rule);
4261
+ if (m === null || m[1].includes("'")) {
4262
+ throw new Error(`[halfcycle] retired deny rule ${rule} is not a plain Bash(<prefix>:*) rule`);
4263
+ }
4264
+ return m[1];
4265
+ });
4266
+ }
4267
+ function preToolUseFallbackShell() {
4268
+ const reply = JSON.stringify({
4269
+ systemMessage: PRE_TOOL_USE_FALLBACK_SENTENCE,
4270
+ hookSpecificOutput: {
4271
+ hookEventName: "PreToolUse",
4272
+ permissionDecision: "deny",
4273
+ permissionDecisionReason: PRE_TOOL_USE_FALLBACK_SENTENCE
4274
+ }
4275
+ });
4276
+ const arms = retiredDenyPrefixes().map((prefix) => ` ${quoteForShell(prefix)}*) ;;`).join("\n");
4277
+ return `# --- Halfcycle fallback when the check cannot start (generated; do not edit) ---
4278
+ # Runs only before a tool runs, and only when Node is missing or too old. It
4279
+ # reads the shell command the agent is about to run and stops the few commands
4280
+ # that can lose work for good; everything else goes ahead.
4281
+ halfcycle_pre_tool_use_fallback() {
4282
+ local LC_ALL=C hc_in= hc_rest
4283
+ IFS= read -r -d '' hc_in
4284
+ case $hc_in in *'"tool_name":'*) ;; *) return 0 ;; esac
4285
+ hc_rest=\${hc_in#*'"tool_name":'}
4286
+ hc_rest=\${hc_rest#"\${hc_rest%%[! ]*}"}
4287
+ case $hc_rest in '"Bash"'*) ;; *) return 0 ;; esac
4288
+ case $hc_in in *'"tool_input"'*) ;; *) return 0 ;; esac
4289
+ hc_rest=\${hc_in#*'"tool_input"'}
4290
+ case $hc_rest in *'"command":'*) ;; *) return 0 ;; esac
4291
+ hc_rest=\${hc_rest#*'"command":'}
4292
+ hc_rest=\${hc_rest#"\${hc_rest%%[! ]*}"}
4293
+ case $hc_rest in '"'*) hc_rest=\${hc_rest#'"'} ;; *) return 0 ;; esac
4294
+ printf '%s' "$hc_rest" 2>/dev/null | {
4295
+ hc_piece= hc_esc= hc_prev= hc_end=
4296
+ while IFS= read -r -n 1 hc_c; do
4297
+ if [ -n "$hc_esc" ]; then
4298
+ hc_esc=
4299
+ case $hc_c in
4300
+ n) hc_c=';' ;;
4301
+ '\\'|'"') ;;
4302
+ *) hc_c="\\\\$hc_c" ;;
4303
+ esac
4304
+ else
4305
+ case $hc_c in
4306
+ '\\') hc_esc=1; continue ;;
4307
+ '"') hc_end=1; break ;;
4308
+ esac
4309
+ fi
4310
+ case $hc_prev$hc_c in
4311
+ '&&'|*';'|*'|')
4312
+ halfcycle_retired_spelling "$hc_piece" && return 0
4313
+ hc_piece= hc_prev=
4314
+ continue
4315
+ ;;
4316
+ esac
4317
+ if [ -z "$hc_piece" ]; then
4318
+ case $hc_c in ' '|' ') continue ;; esac
4319
+ fi
4320
+ [ \${#hc_piece} -lt 64 ] && hc_piece=$hc_piece$hc_c
4321
+ hc_prev=$hc_c
4322
+ done
4323
+ [ -n "$hc_end" ] && halfcycle_retired_spelling "$hc_piece"
4324
+ return 0
4325
+ }
4326
+ return 0
4327
+ }
4328
+ halfcycle_retired_spelling() {
4329
+ case $1 in
4330
+ ${arms}
4331
+ *) return 1 ;;
4332
+ esac
4333
+ printf '%s\\n' ${quoteForShell(reply)}
4334
+ }
4335
+ # --- end Halfcycle fallback ---------------------------------------------------`;
4336
+ }
3620
4337
  var GENERATED_SESSION_START_HEADER = "# Session-start marker \u2014 generated by";
3621
4338
  var SESSION_ID_FROM_HOOK_INPUT_SH = `SESSION_ID="$(printf '%s' "$INPUT" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{const j=JSON.parse(s);process.stdout.write(typeof j.session_id==="string"?j.session_id:"")}catch{process.stdout.write("")}})' 2>/dev/null || true)"`;
3622
4339
  var REPO_HASH_FROM_ROOT_SH = `HASH="$(printf '%s' "$REPO_ROOT" | node -e 'const c=[];process.stdin.on("data",d=>c.push(d)).on("end",()=>{process.stdout.write(require("node:crypto").createHash("sha256").update(Buffer.concat(c)).digest("hex").slice(0,12))})' 2>/dev/null || true)"`;
@@ -3901,7 +4618,7 @@ function generateMcpRegistration(existing, mcpOrigin) {
3901
4618
  };
3902
4619
  return JSON.stringify(base, null, 2) + "\n";
3903
4620
  }
3904
- var REQUIRED_GITIGNORE_ENTRIES = [".halfcycle/state.json", `${ZONE_B_DIR}/`];
4621
+ var REQUIRED_GITIGNORE_ENTRIES = [".halfcycle/state.json", `${ZONE_B_DIR}/`, ".claude/worktrees/"];
3905
4622
  var GITIGNORE_HEADER = "# Halfcycle \u2014 machine-local secrets/state and Zone-B (never push to client remote)";
3906
4623
  function adoptBlockUnderHeader(before, header, claimable) {
3907
4624
  const claimed = [];
@@ -4024,13 +4741,16 @@ function writeBundlePin(targetRepoRoot, version, engagementId, engagementType, c
4024
4741
  const pinText = inLineEndingOf(readFileOrNull(pinPath), JSON.stringify(pin, null, 2) + "\n");
4025
4742
  writeAllowlisted(pinPath, targetRepoRoot, pinText, writtenPaths);
4026
4743
  }
4027
- function writeCrewRoster(targetRepoRoot, report) {
4744
+ function renderCrewRoster() {
4028
4745
  const doc = {
4029
4746
  $comment: "GENERATED by the Halfcycle installer \u2014 do not edit by hand. Re-run the installer to pick up a roster change.",
4030
4747
  crew: CREW_ROSTER
4031
4748
  };
4032
- const rendered = `${JSON.stringify(doc, null, 2)}
4749
+ return `${JSON.stringify(doc, null, 2)}
4033
4750
  `;
4751
+ }
4752
+ function writeCrewRoster(targetRepoRoot, report) {
4753
+ const rendered = renderCrewRoster();
4034
4754
  const crewPath = join5(targetRepoRoot, CREW_ROSTER_REL);
4035
4755
  return recordOwned(report, crewPath, targetRepoRoot, rendered, CREW_ROSTER_REL);
4036
4756
  }
@@ -4170,8 +4890,11 @@ async function install(options) {
4170
4890
  } catch (err) {
4171
4891
  throw new SettingsParseRefused(settingsParseRefusal(targetRepo, err));
4172
4892
  }
4173
- recordSettingsMerge(draft, existingSettings, generatedSettings, settingsPreexisted);
4174
- const mergedSettings = mergeSettings(existingSettings, generatedSettings);
4893
+ const retiredOurs = retiredRulesThisInstallAdded(draft.previous);
4894
+ const statusLine = statusLineSetElsewhere(targetRepo, home, options.env) ? void 0 : HALFCYCLE_STATUS_LINE;
4895
+ const mergedSettings = mergeSettings(existingSettings, generatedSettings, retiredOurs, statusLine);
4896
+ recordSettingsMerge(draft, existingSettings, generatedSettings, settingsPreexisted, mergedSettings);
4897
+ recordDenyRetired(draft, retiredOurs.map((rule) => sha256Hex(rule)));
4175
4898
  const mergedSettingsText = inLineEndingOf(readFileOrNull(settingsPath), JSON.stringify(mergedSettings, null, 2) + "\n");
4176
4899
  mkdirSync4(dirname3(settingsPath), { recursive: true });
4177
4900
  writeFileSync4(settingsPath, mergedSettingsText, "utf-8");
@@ -5094,6 +5817,10 @@ var HEADER_CHECKS = "Halfcycle's checks are automatic rules drawn from the ways
5094
5817
  var HEADER_KEPT = "Halfcycle derived this record and kept a copy of it when the phase closed. It changes that copy afterwards in one way only. If an account that closed this phase is later deleted, its email is removed, and so is the name given at any close Halfcycle recorded as made by that account. The client's Halfcycle command-line tool wrote this file from the kept copy, when the phase closed or when the record was fetched again. So a later deletion reaches this file the next time the record is fetched. The same record, as data, is in the .json file of the same name beside this one. If the phase is closed again, this file is replaced by a new record that describes every close, this one included.";
5095
5818
  var HEADER_HIDDEN = "Some text in this record contains characters that do not print. Each is shown as U+ and its code of four or more hex digits; the .json file holds the text exactly.";
5096
5819
  var OBSERVED_LABEL = "**Observed by Halfcycle**";
5820
+ var PATH_LINE = Object.freeze({
5821
+ mini: "Path: mini phase",
5822
+ full: "Path: full phase"
5823
+ });
5097
5824
  var STATED_LABEL = "**Stated by the client** \u2014 Halfcycle did not check these.";
5098
5825
  var MISMATCHED_HALVES = "This Build Record could not be written: its two parts describe different closes.";
5099
5826
  function renderChecks(checks, p) {
@@ -5341,6 +6068,30 @@ function renderReview(record2, p) {
5341
6068
  blocks.push(removals);
5342
6069
  return blocks;
5343
6070
  }
6071
+ function renderCalls(calls, p) {
6072
+ if (calls.length === 0)
6073
+ return [`## ${CALLS_HEADING}`, NO_CALLS_LINE];
6074
+ const lines = calls.flatMap((c) => {
6075
+ const by = p.itemStart(c.by);
6076
+ return [
6077
+ `- ${callMadeLine({ by, question: p.text(c.question), chose: p.text(c.chose), statedDefault: p.text(c.statedDefault) })}`,
6078
+ ` - ${callUndoLine({ by: p.text(c.by), undoable: c.undoable, how: c.how === null ? null : p.text(c.how) })}`
6079
+ ];
6080
+ });
6081
+ return [`## ${CALLS_HEADING}`, CALLS_EXPLAINED, lines.join("\n")];
6082
+ }
6083
+ function renderClaims(claims, p) {
6084
+ if (claims.length === 0)
6085
+ return [`## ${UNCHECKED_CLAIMS_HEADING}`, NO_UNCHECKED_CLAIMS_LINE];
6086
+ const lines = claims.map((c) => `- ${recordedClaimLine({
6087
+ task: p.text(c.task),
6088
+ label: p.text(c.label),
6089
+ head: shortCommit(c.head),
6090
+ act: c.act,
6091
+ note: c.note === null ? null : p.text(c.note)
6092
+ })}`);
6093
+ return [`## ${UNCHECKED_CLAIMS_HEADING}`, lines.join("\n")];
6094
+ }
5344
6095
  var EARLIER_ROUNDS_LABEL = `${STATED_LABEL} What was said each time the owner's review was asked for, before the round that closed the phase. What was said for that last round is under the close above. If the account that asked for a round is later deleted, the name given for that round is removed.`;
5345
6096
  function renderRoundStated(round, stated, p) {
5346
6097
  const label = "> - Acceptance walk (the finished work tried as a user would, by someone other than its builder): ";
@@ -5385,7 +6136,8 @@ function renderBuildRecordV2Markdown(record2) {
5385
6136
  const closes = [...record2.observed.closes].sort((a, b) => a.sequence - b.sequence);
5386
6137
  const total = closes.length;
5387
6138
  const latest = closes[total - 1];
5388
- const rounds = record2.format === "halfcycle-build-record/v3" ? record2.review.rounds : [];
6139
+ const rounds = hasOwnerReview(record2) ? record2.review.rounds : [];
6140
+ const path = keptPathOf(record2);
5389
6141
  const roundsAfter = rounds.length <= 1 ? null : `after ${String(rounds.length)} rounds of the owner's review.${rounds.some((r) => r.stated !== void 0) ? ' What was said for each earlier round is under "Earlier rounds of the review".' : ""}`;
5390
6142
  const body = [
5391
6143
  `Phase: ${phase} (the name the client gave it)`,
@@ -5393,14 +6145,23 @@ function renderBuildRecordV2Markdown(record2) {
5393
6145
  [
5394
6146
  `- Method version: ${p.text(record2.observed.methodVersion)}. The Halfcycle method is updated from time to time, and each update has its own version number. This is the one the phase was run under. A project moves to a newer version only between phases.`,
5395
6147
  `- Project identifier: ${p.text(record2.observed.engagementId)}. Halfcycle keeps this project's records under this identifier.`,
5396
- `- Record format: ${p.text(record2.format)}. This names the layout this file and the .json file beside it follow, for software that reads them.`
6148
+ `- Record format: ${p.text(record2.format)}. This names the layout this file and the .json file beside it follow, for software that reads them.`,
6149
+ ...path === null ? [] : [`- ${PATH_LINE[path]}`]
5397
6150
  ].join("\n"),
5398
6151
  total === 1 ? roundsAfter === null ? "This phase was closed once." : `This phase was closed once, ${roundsAfter}` : `This phase was closed ${String(total)} times. The latest close is described first; each earlier close follows, as it was recorded when it was made.${roundsAfter === null ? "" : ` The latest close came ${roundsAfter}`}`,
5399
6152
  ...renderChecks(record2.observed.checks, p),
5400
6153
  ...renderClose(total === 1 ? "## The close" : `## The latest close (close ${String(latest.sequence)} of ${String(total)})`, latest, attestedBySequence.get(latest.sequence), record2.observed.checks.state, p, rounds.length > 1)
5401
6154
  ];
5402
- if (record2.format === "halfcycle-build-record/v3")
5403
- body.push(...renderReview(record2, p), ...renderEarlierRounds(record2, p));
6155
+ if (hasOwnerReview(record2))
6156
+ body.push(...renderReview(record2, p));
6157
+ const calls = keptCallsOf(record2);
6158
+ if (calls !== null)
6159
+ body.push(...renderCalls(calls, p));
6160
+ const claims = keptClaimsOf(record2);
6161
+ if (claims !== null)
6162
+ body.push(...renderClaims(claims, p));
6163
+ if (hasOwnerReview(record2))
6164
+ body.push(...renderEarlierRounds(record2, p));
5404
6165
  if (total > 1) {
5405
6166
  body.push("## Earlier closes");
5406
6167
  for (const close of closes.slice(0, -1)) {
@@ -5415,16 +6176,11 @@ function renderBuildRecordV2Markdown(record2) {
5415
6176
  }
5416
6177
 
5417
6178
  // dist/build-record/write.js
5418
- import { closeSync, existsSync as existsSync6, mkdirSync as mkdirSync6, openSync, readFileSync as readFileSync7, renameSync as renameSync2, rmSync as rmSync3, writeSync } from "node:fs";
5419
- import { basename, join as join8, resolve, relative as relative2, isAbsolute as isAbsolute2 } from "node:path";
5420
- var BUILD_RECORD_DIR = join8("docs", "build-records");
5421
- function assertInsideOutDir(outDir, candidate, identity) {
5422
- const rel = relative2(outDir, resolve(candidate));
5423
- const isPlainFilename = rel !== "" && rel !== ".." && !isAbsolute2(rel) && rel === basename(rel);
5424
- if (!isPlainFilename) {
5425
- throw new InvalidPhaseIdentityError(identity, BUILD_RECORD_DIR);
5426
- }
5427
- }
6179
+ import { existsSync as existsSync6, mkdirSync as mkdirSync6, rmSync as rmSync4 } from "node:fs";
6180
+ import { basename, join as join8, resolve as resolve2, relative as relative2, isAbsolute as isAbsolute3 } from "node:path";
6181
+
6182
+ // dist/build-record/pair-io.js
6183
+ import { closeSync, openSync, readFileSync as readFileSync7, renameSync as renameSync2, rmSync as rmSync3, writeSync } from "node:fs";
5428
6184
  function createExclusiveOnDisk(path, data) {
5429
6185
  const bytes = typeof data === "string" ? Buffer.from(data, "utf-8") : data;
5430
6186
  const fd = openSync(path, "wx");
@@ -5460,6 +6216,41 @@ function readIfPresent(path) {
5460
6216
  return null;
5461
6217
  }
5462
6218
  }
6219
+ function createPairExclusive(io, jsonPath, jsonText, mdPath, mdText) {
6220
+ let createdJson = false;
6221
+ try {
6222
+ io.createExclusive(jsonPath, jsonText);
6223
+ createdJson = true;
6224
+ io.createExclusive(mdPath, mdText);
6225
+ return "created";
6226
+ } catch (err) {
6227
+ if (createdJson)
6228
+ rmSync3(jsonPath, { force: true });
6229
+ if (!isEexist(err))
6230
+ throw err;
6231
+ return "exists";
6232
+ }
6233
+ }
6234
+ function replaceOne(io, tmp, finalPath, text) {
6235
+ rmSync3(tmp, { force: true });
6236
+ try {
6237
+ io.createExclusive(tmp, text);
6238
+ io.rename(tmp, finalPath);
6239
+ } catch (err) {
6240
+ rmSync3(tmp, { force: true });
6241
+ throw err;
6242
+ }
6243
+ }
6244
+
6245
+ // dist/build-record/write.js
6246
+ var BUILD_RECORD_DIR = join8("docs", "build-records");
6247
+ function assertInsideOutDir(outDir, candidate, identity) {
6248
+ const rel = relative2(outDir, resolve2(candidate));
6249
+ const isPlainFilename = rel !== "" && rel !== ".." && !isAbsolute3(rel) && rel === basename(rel);
6250
+ if (!isPlainFilename) {
6251
+ throw new InvalidPhaseIdentityError(identity, BUILD_RECORD_DIR);
6252
+ }
6253
+ }
5463
6254
  function isOwnEarlierRecord(existingText, served) {
5464
6255
  let parsedJson;
5465
6256
  try {
@@ -5488,7 +6279,7 @@ function writeRecordPair(repoRoot, phase, rawRecord, record2, io = NODE_RECORD_P
5488
6279
  if (!isValidPhaseIdentity(phase)) {
5489
6280
  throw new InvalidPhaseIdentityError(phase, BUILD_RECORD_DIR);
5490
6281
  }
5491
- const outDir = resolve(repoRoot, BUILD_RECORD_DIR);
6282
+ const outDir = resolve2(repoRoot, BUILD_RECORD_DIR);
5492
6283
  const segment = renderPhaseSegment(phase);
5493
6284
  const jsonPath = join8(outDir, `${segment}.json`);
5494
6285
  const mdPath = join8(outDir, `${segment}.md`);
@@ -5500,17 +6291,8 @@ function writeRecordPair(repoRoot, phase, rawRecord, record2, io = NODE_RECORD_P
5500
6291
  const jsonText = serialiseServedRecord(rawRecord);
5501
6292
  const mdText = renderBuildRecordV2Markdown(record2);
5502
6293
  mkdirSync6(outDir, { recursive: true });
5503
- let createdJson = false;
5504
- try {
5505
- io.createExclusive(jsonPath, jsonText);
5506
- createdJson = true;
5507
- io.createExclusive(mdPath, mdText);
6294
+ if (createPairExclusive(io, jsonPath, jsonText, mdPath, mdText) === "created") {
5508
6295
  return { kind: "created", jsonPath, mdPath };
5509
- } catch (err) {
5510
- if (createdJson)
5511
- rmSync3(jsonPath, { force: true });
5512
- if (!isEexist(err))
5513
- throw err;
5514
6296
  }
5515
6297
  const existingJson = readIfPresent(jsonPath);
5516
6298
  if (existingJson === null) {
@@ -5530,22 +6312,12 @@ function writeRecordPair(repoRoot, phase, rawRecord, record2, io = NODE_RECORD_P
5530
6312
  try {
5531
6313
  replaceOne(io, jsonTmp, jsonPath, existingJson);
5532
6314
  } catch {
5533
- rmSync3(jsonPath, { force: true });
6315
+ rmSync4(jsonPath, { force: true });
5534
6316
  }
5535
6317
  throw err;
5536
6318
  }
5537
6319
  return { kind: "replaced", jsonPath, mdPath };
5538
6320
  }
5539
- function replaceOne(io, tmp, finalPath, text) {
5540
- rmSync3(tmp, { force: true });
5541
- try {
5542
- io.createExclusive(tmp, text);
5543
- io.rename(tmp, finalPath);
5544
- } catch (err) {
5545
- rmSync3(tmp, { force: true });
5546
- throw err;
5547
- }
5548
- }
5549
6321
 
5550
6322
  // dist/build-record/obtain.js
5551
6323
  import { relative as relative3 } from "node:path";