halfcycle 0.4.8 → 0.4.10
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.
- package/.claude-plugin/plugin.json +1 -1
- package/bin/bin.bundle.mjs +2162 -335
- package/commands/agent-teams-orchestration.md +1 -1
- package/commands/consistency-check.md +1 -1
- package/commands/context-update.md +1 -1
- package/commands/halfcycle-design.md +1 -1
- package/commands/halfcycle-setup.md +1 -1
- package/commands/stack-assembly-worker.md +1 -1
- package/commands/task-execution.md +1 -1
- package/commands/task-graph.md +1 -1
- package/commands/write-spec.md +1 -1
- package/dist/bin.d.ts +2 -1
- package/dist/bin.d.ts.map +1 -1
- package/dist/bin.js +0 -0
- package/dist/build-record/obtain.d.ts +14 -7
- package/dist/build-record/obtain.d.ts.map +1 -1
- package/dist/build-record/render-v2.d.ts +32 -6
- package/dist/build-record/render-v2.d.ts.map +1 -1
- package/dist/build-record/write.d.ts +2 -2
- package/dist/build-record/write.d.ts.map +1 -1
- package/dist/cli-contract.d.ts +2 -9
- package/dist/cli-contract.d.ts.map +1 -1
- package/dist/cli.js +3859 -1946
- package/dist/cli.js.map +3 -3
- package/dist/close-phase.d.ts +223 -134
- package/dist/close-phase.d.ts.map +1 -1
- package/dist/draw/index.d.ts +29 -6
- package/dist/draw/index.d.ts.map +1 -1
- package/dist/identity.d.ts +78 -9
- package/dist/identity.d.ts.map +1 -1
- package/dist/index.js +1685 -232
- package/dist/index.js.map +3 -3
- package/dist/install.d.ts +16 -0
- package/dist/install.d.ts.map +1 -1
- package/dist/open-phase.d.ts +25 -1
- package/dist/open-phase.d.ts.map +1 -1
- package/dist/plan.d.ts +75 -23
- package/dist/plan.d.ts.map +1 -1
- package/dist/pre-create-checks.d.ts.map +1 -1
- package/dist/review.d.ts +231 -0
- package/dist/review.d.ts.map +1 -0
- package/dist/shown-record.d.ts +46 -0
- package/dist/shown-record.d.ts.map +1 -0
- package/dist/system-report.d.ts +28 -4
- package/dist/system-report.d.ts.map +1 -1
- package/package.json +11 -11
- package/dist/project-page.d.ts +0 -24
- package/dist/project-page.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -238,6 +238,25 @@ var HALFCYCLE_COMMAND = "npx halfcycle@latest";
|
|
|
238
238
|
// ../core/dist/release-version.js
|
|
239
239
|
var RELEASE_VERSION_PATTERN = /^\d+\.\d+\.\d+$/;
|
|
240
240
|
|
|
241
|
+
// ../core/dist/github-address.js
|
|
242
|
+
var GITHUB_REPOSITORY_ADDRESS = /^https:\/\/github\.com\/[A-Za-z0-9][A-Za-z0-9-]{0,38}\/(?!\.{1,2}$)[A-Za-z0-9._-]{1,100}$/;
|
|
243
|
+
var HOST = "[Gg][Ii][Tt][Hh][Uu][Bb]\\.[Cc][Oo][Mm]";
|
|
244
|
+
var OWNER = "[A-Za-z0-9][A-Za-z0-9-]{0,38}";
|
|
245
|
+
var RAW_REPOSITORY = "[A-Za-z0-9._-]{1,104}";
|
|
246
|
+
var TAIL = `(${OWNER})/(${RAW_REPOSITORY})/?$`;
|
|
247
|
+
var URL_USERINFO = "(?:[^\\s/?#@]+@)?";
|
|
248
|
+
var SSH_USER = "(?:[^\\s/?#@:]+@)?";
|
|
249
|
+
var REMOTE_FORMS = [
|
|
250
|
+
new RegExp(`^https?://${URL_USERINFO}${HOST}/${TAIL}`),
|
|
251
|
+
new RegExp(`^ssh://${SSH_USER}${HOST}/${TAIL}`),
|
|
252
|
+
new RegExp(`^git://${HOST}/${TAIL}`),
|
|
253
|
+
new RegExp(`^${SSH_USER}${HOST}:${TAIL}`)
|
|
254
|
+
];
|
|
255
|
+
|
|
256
|
+
// ../core/dist/sign-in.js
|
|
257
|
+
var SIGN_IN_COMMAND = '"npx halfcycle"';
|
|
258
|
+
var HOOK_REMEDY = `Run ${SIGN_IN_COMMAND} in the project folder to sign in.`;
|
|
259
|
+
|
|
241
260
|
// ../events/dist/result.js
|
|
242
261
|
import { z as z2 } from "zod";
|
|
243
262
|
var firedGuardSchema = z2.object({
|
|
@@ -305,17 +324,29 @@ function hasLoneSurrogate(text) {
|
|
|
305
324
|
var LONE_SURROGATE_MESSAGE = "must be valid text: it holds half of a character pair with no other half";
|
|
306
325
|
|
|
307
326
|
// ../events/dist/state.js
|
|
327
|
+
import { z as z4 } from "zod";
|
|
328
|
+
|
|
329
|
+
// ../events/dist/human-agent.js
|
|
308
330
|
import { z as z3 } from "zod";
|
|
309
|
-
var
|
|
331
|
+
var HUMAN_AGENT = "Human";
|
|
332
|
+
var AGENT_NAME_MAX = 32;
|
|
333
|
+
var agentNameSchema = z3.string().min(1).max(AGENT_NAME_MAX);
|
|
334
|
+
|
|
335
|
+
// ../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");
|
|
310
337
|
var STATE_RECORD_FORMAT = "halfcycle-state-record/v1";
|
|
311
|
-
var stepIdSchema =
|
|
312
|
-
var methodVersionSchema =
|
|
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);
|
|
313
340
|
var anchorPathRegex = /^(?!\/)(?!.*#)(?!method\/)(?!(.*\/)?docs\/method\/).+$/;
|
|
314
|
-
var recordKindSchema =
|
|
315
|
-
var methodLayerSchema =
|
|
316
|
-
var
|
|
317
|
-
|
|
318
|
-
|
|
341
|
+
var recordKindSchema = z4.enum(["artefact", "step-run", "gate", "intervention"]);
|
|
342
|
+
var methodLayerSchema = z4.enum(["l0", "l1", "l2", "l3", "l3-l4", "l4", "l5", "xl"]);
|
|
343
|
+
var PHASE_SCOPED_LAYERS = ["l4", "l5"];
|
|
344
|
+
function isPhaseScopedLayer(layer) {
|
|
345
|
+
return PHASE_SCOPED_LAYERS.includes(layer);
|
|
346
|
+
}
|
|
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([
|
|
319
350
|
"prevented",
|
|
320
351
|
"corrected-in-spec",
|
|
321
352
|
"corrected-in-scope",
|
|
@@ -325,20 +356,20 @@ var dispositionSchema = z3.enum([
|
|
|
325
356
|
"surfaced",
|
|
326
357
|
"filed-forward"
|
|
327
358
|
]);
|
|
328
|
-
var mechanismSchema =
|
|
329
|
-
var artefactStateSchema =
|
|
359
|
+
var mechanismSchema = z4.enum(["guard", "verdict", "served-step", "method-step"]);
|
|
360
|
+
var artefactStateSchema = z4.enum(["created", "updated"]);
|
|
330
361
|
var QUALIFIED_OUTCOMES = ["declined", "parked"];
|
|
331
|
-
var qualifiedOutcomeSchema =
|
|
362
|
+
var qualifiedOutcomeSchema = z4.enum(QUALIFIED_OUTCOMES);
|
|
332
363
|
function isQualifiedOutcome(outcome) {
|
|
333
364
|
return QUALIFIED_OUTCOMES.includes(outcome);
|
|
334
365
|
}
|
|
335
|
-
var runOutcomeSchema =
|
|
366
|
+
var runOutcomeSchema = z4.enum([
|
|
336
367
|
"completed",
|
|
337
368
|
"attempted-failed",
|
|
338
369
|
...QUALIFIED_OUTCOMES
|
|
339
370
|
]);
|
|
340
|
-
var gateKindSchema =
|
|
341
|
-
var gateVerdictSchema =
|
|
371
|
+
var gateKindSchema = z4.enum(["approval", "mechanical"]);
|
|
372
|
+
var gateVerdictSchema = z4.enum(["pass", "fail", ...QUALIFIED_OUTCOMES]);
|
|
342
373
|
function outcomeReasonIssue(outcome, reason) {
|
|
343
374
|
if (isQualifiedOutcome(outcome) && reason === void 0) {
|
|
344
375
|
return { message: `outcomeReason is required when the outcome is '${outcome}'` };
|
|
@@ -350,47 +381,47 @@ function outcomeReasonIssue(outcome, reason) {
|
|
|
350
381
|
}
|
|
351
382
|
return null;
|
|
352
383
|
}
|
|
353
|
-
var anchorEvidenceSchema =
|
|
354
|
-
kind:
|
|
355
|
-
path:
|
|
384
|
+
var anchorEvidenceSchema = z4.object({
|
|
385
|
+
kind: z4.literal("anchor"),
|
|
386
|
+
path: z4.string().regex(anchorPathRegex),
|
|
356
387
|
// Optional 1-based line number within the file at `path`. Present when the
|
|
357
388
|
// writer knows the exact line; omitted — never `0`, never `null` — when it
|
|
358
389
|
// does not, because a line number is not always obtainable. Absence is a
|
|
359
390
|
// valid, permanent state here, not a gap meant to be filled in later.
|
|
360
|
-
line:
|
|
391
|
+
line: z4.number().optional()
|
|
361
392
|
}).strict();
|
|
362
|
-
var diffEvidenceSchema =
|
|
363
|
-
kind:
|
|
364
|
-
ref:
|
|
393
|
+
var diffEvidenceSchema = z4.object({
|
|
394
|
+
kind: z4.literal("diff"),
|
|
395
|
+
ref: z4.string()
|
|
365
396
|
}).strict();
|
|
366
|
-
var reproEvidenceSchema =
|
|
367
|
-
kind:
|
|
368
|
-
steps:
|
|
397
|
+
var reproEvidenceSchema = z4.object({
|
|
398
|
+
kind: z4.literal("repro"),
|
|
399
|
+
steps: z4.string().max(2e3)
|
|
369
400
|
}).strict();
|
|
370
|
-
var questionAnswerEvidenceSchema =
|
|
371
|
-
kind:
|
|
372
|
-
question:
|
|
373
|
-
before:
|
|
374
|
-
after:
|
|
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)
|
|
375
406
|
}).strict();
|
|
376
|
-
var noOpEvidenceSchema =
|
|
377
|
-
kind:
|
|
378
|
-
inspected:
|
|
379
|
-
unchangedBecause:
|
|
407
|
+
var noOpEvidenceSchema = z4.object({
|
|
408
|
+
kind: z4.literal("no-op"),
|
|
409
|
+
inspected: z4.string().max(2e3),
|
|
410
|
+
unchangedBecause: z4.string().max(2e3)
|
|
380
411
|
}).strict();
|
|
381
|
-
var evidenceSchema =
|
|
412
|
+
var evidenceSchema = z4.discriminatedUnion("kind", [
|
|
382
413
|
anchorEvidenceSchema,
|
|
383
414
|
diffEvidenceSchema,
|
|
384
415
|
reproEvidenceSchema,
|
|
385
416
|
questionAnswerEvidenceSchema,
|
|
386
417
|
noOpEvidenceSchema
|
|
387
418
|
]);
|
|
388
|
-
var stateRecordEnvelopeSchema =
|
|
389
|
-
format:
|
|
419
|
+
var stateRecordEnvelopeSchema = z4.object({
|
|
420
|
+
format: z4.literal(STATE_RECORD_FORMAT),
|
|
390
421
|
recordKind: recordKindSchema,
|
|
391
|
-
recordId:
|
|
422
|
+
recordId: z4.string().uuid(),
|
|
392
423
|
methodVersion: methodVersionSchema,
|
|
393
|
-
engagementId:
|
|
424
|
+
engagementId: z4.string(),
|
|
394
425
|
stepId: stepIdSchema,
|
|
395
426
|
// Immutable: names when the thing happened, never when a later disposition
|
|
396
427
|
// moved.
|
|
@@ -406,80 +437,94 @@ var stateRecordEnvelopeSchema = z3.object({
|
|
|
406
437
|
// for the whole project rather than per phase. Absence is meaningful on its
|
|
407
438
|
// own — it is never replaced with a placeholder value, and it is read as
|
|
408
439
|
// "satisfies any phase", not as "unknown".
|
|
409
|
-
phase:
|
|
440
|
+
phase: z4.string().optional()
|
|
410
441
|
});
|
|
411
|
-
var artefactRefSchema =
|
|
412
|
-
name:
|
|
413
|
-
parent:
|
|
442
|
+
var artefactRefSchema = z4.object({
|
|
443
|
+
name: z4.string(),
|
|
444
|
+
parent: z4.string()
|
|
414
445
|
}).strict();
|
|
415
|
-
var coverageSchema =
|
|
416
|
-
asked:
|
|
417
|
-
answered:
|
|
446
|
+
var coverageSchema = z4.object({
|
|
447
|
+
asked: z4.number().int().nonnegative(),
|
|
448
|
+
answered: z4.number().int().nonnegative()
|
|
418
449
|
}).strict().superRefine((coverage, ctx) => {
|
|
419
450
|
if (coverage.answered > coverage.asked) {
|
|
420
451
|
ctx.addIssue({
|
|
421
|
-
code:
|
|
452
|
+
code: z4.ZodIssueCode.custom,
|
|
422
453
|
path: ["answered"],
|
|
423
454
|
message: "answered cannot be more than asked"
|
|
424
455
|
});
|
|
425
456
|
}
|
|
426
457
|
});
|
|
458
|
+
var projectTierSchema = z4.number().int().min(0).max(3);
|
|
427
459
|
var artefactRecordSchema = stateRecordEnvelopeSchema.extend({
|
|
428
|
-
recordKind:
|
|
460
|
+
recordKind: z4.literal("artefact"),
|
|
429
461
|
artefactRef: artefactRefSchema,
|
|
430
462
|
// Some layer wrote it, by construction — an empty array is not a legitimate
|
|
431
463
|
// state, so the schema says so.
|
|
432
|
-
writtenByLayers:
|
|
464
|
+
writtenByLayers: z4.array(methodLayerSchema).min(1),
|
|
433
465
|
state: artefactStateSchema
|
|
434
466
|
}).strict();
|
|
435
467
|
var stepRunRecordSchema = stateRecordEnvelopeSchema.extend({
|
|
436
|
-
recordKind:
|
|
468
|
+
recordKind: z4.literal("step-run"),
|
|
437
469
|
runOutcome: runOutcomeSchema,
|
|
438
|
-
failureReason:
|
|
439
|
-
outcomeReason:
|
|
470
|
+
failureReason: z4.string().optional(),
|
|
471
|
+
outcomeReason: z4.string().max(2e3).optional(),
|
|
472
|
+
// Who did this step, as the session reported it. Absent when not reported.
|
|
473
|
+
agent: agentNameSchema.optional(),
|
|
474
|
+
// When the session began this step. Absent when not reported.
|
|
475
|
+
startedAt: utcIso8601.optional(),
|
|
476
|
+
// The project's tier, when this step settled it. Absent on every other step.
|
|
477
|
+
projectTier: projectTierSchema.optional()
|
|
440
478
|
}).strict().superRefine((rec, ctx) => {
|
|
441
479
|
if (rec.runOutcome === "attempted-failed" && rec.failureReason === void 0) {
|
|
442
480
|
ctx.addIssue({
|
|
443
|
-
code:
|
|
481
|
+
code: z4.ZodIssueCode.custom,
|
|
444
482
|
path: ["failureReason"],
|
|
445
483
|
message: "failureReason is required when runOutcome is 'attempted-failed'"
|
|
446
484
|
});
|
|
447
485
|
}
|
|
448
486
|
if (rec.runOutcome !== "attempted-failed" && rec.failureReason !== void 0) {
|
|
449
487
|
ctx.addIssue({
|
|
450
|
-
code:
|
|
488
|
+
code: z4.ZodIssueCode.custom,
|
|
451
489
|
path: ["failureReason"],
|
|
452
490
|
message: "failureReason is only present when runOutcome is 'attempted-failed'"
|
|
453
491
|
});
|
|
454
492
|
}
|
|
455
493
|
const issue = outcomeReasonIssue(rec.runOutcome, rec.outcomeReason);
|
|
456
494
|
if (issue) {
|
|
457
|
-
ctx.addIssue({ code:
|
|
495
|
+
ctx.addIssue({ code: z4.ZodIssueCode.custom, path: ["outcomeReason"], ...issue });
|
|
458
496
|
}
|
|
459
497
|
});
|
|
460
498
|
var gateRecordSchema = stateRecordEnvelopeSchema.extend({
|
|
461
|
-
recordKind:
|
|
499
|
+
recordKind: z4.literal("gate"),
|
|
462
500
|
gateKind: gateKindSchema,
|
|
463
501
|
verdict: gateVerdictSchema,
|
|
464
|
-
actor:
|
|
465
|
-
outcomeReason:
|
|
466
|
-
coverage: coverageSchema.optional()
|
|
502
|
+
actor: z4.string(),
|
|
503
|
+
outcomeReason: z4.string().max(2e3).optional(),
|
|
504
|
+
coverage: coverageSchema.optional(),
|
|
505
|
+
// Who did the step this checkpoint is on, as reported. Absent when not reported.
|
|
506
|
+
agent: agentNameSchema.optional(),
|
|
507
|
+
// When that step, or the review this checkpoint closes, began. Absent when not
|
|
508
|
+
// reported.
|
|
509
|
+
startedAt: utcIso8601.optional()
|
|
467
510
|
}).strict().superRefine((rec, ctx) => {
|
|
468
511
|
const issue = outcomeReasonIssue(rec.verdict, rec.outcomeReason);
|
|
469
512
|
if (issue) {
|
|
470
|
-
ctx.addIssue({ code:
|
|
513
|
+
ctx.addIssue({ code: z4.ZodIssueCode.custom, path: ["outcomeReason"], ...issue });
|
|
471
514
|
}
|
|
472
515
|
});
|
|
473
516
|
var interventionRecordSchema = stateRecordEnvelopeSchema.extend({
|
|
474
|
-
recordKind:
|
|
517
|
+
recordKind: z4.literal("intervention"),
|
|
475
518
|
layer: methodLayerSchema,
|
|
476
519
|
mechanism: mechanismSchema,
|
|
477
520
|
severity: severitySchema,
|
|
478
521
|
disposition: dispositionSchema,
|
|
479
|
-
summary:
|
|
480
|
-
coverage: coverageSchema.optional()
|
|
522
|
+
summary: z4.string().max(2e3),
|
|
523
|
+
coverage: coverageSchema.optional(),
|
|
524
|
+
// Who caught it, as the session reported. Absent when not reported.
|
|
525
|
+
agent: agentNameSchema.optional()
|
|
481
526
|
}).strict();
|
|
482
|
-
var stateRecordSchema =
|
|
527
|
+
var stateRecordSchema = z4.discriminatedUnion("recordKind", [
|
|
483
528
|
artefactRecordSchema,
|
|
484
529
|
stepRunRecordSchema,
|
|
485
530
|
gateRecordSchema,
|
|
@@ -487,7 +532,7 @@ var stateRecordSchema = z3.discriminatedUnion("recordKind", [
|
|
|
487
532
|
]);
|
|
488
533
|
|
|
489
534
|
// ../events/dist/build-record.js
|
|
490
|
-
import { z as
|
|
535
|
+
import { z as z5 } from "zod";
|
|
491
536
|
|
|
492
537
|
// ../events/dist/control-characters.js
|
|
493
538
|
var CONTROL_CHARACTER_CLASS = "\\p{Cc}";
|
|
@@ -577,11 +622,16 @@ var ACTOR_MAX = 200;
|
|
|
577
622
|
var FINDING_MAX = 500;
|
|
578
623
|
var FINDINGS_SHOWN_MAX = 50;
|
|
579
624
|
var PHASE_CLOSE_REASON_MAX = 1900;
|
|
580
|
-
var closeCommitSchema =
|
|
581
|
-
var walkVerdictSchema =
|
|
582
|
-
var
|
|
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({
|
|
628
|
+
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())
|
|
631
|
+
}).strict();
|
|
632
|
+
var closeDecisionSchema = z5.enum(["done-when-met", "override"]);
|
|
583
633
|
function plainText(max) {
|
|
584
|
-
const base = max === void 0 ?
|
|
634
|
+
const base = max === void 0 ? z5.string() : z5.string().max(max);
|
|
585
635
|
return base.refine((v) => !hasControlCharacter(v), {
|
|
586
636
|
message: "must be one line with no control characters (no line breaks, tabs or NUL)"
|
|
587
637
|
}).refine((v) => !hasLoneSurrogate(v), { message: LONE_SURROGATE_MESSAGE });
|
|
@@ -593,81 +643,81 @@ function statementText(max) {
|
|
|
593
643
|
}
|
|
594
644
|
var closeActorSchema = plainText(ACTOR_MAX);
|
|
595
645
|
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`;
|
|
596
|
-
var phaseNameSchema =
|
|
646
|
+
var phaseNameSchema = z5.string().refine((v) => isValidPhaseIdentity(v), {
|
|
597
647
|
message: PHASE_NAME_RULE
|
|
598
648
|
});
|
|
599
|
-
var phaseIdentitySchema =
|
|
600
|
-
var closeStatementsSchema =
|
|
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({
|
|
601
651
|
thesis: statementText(STATEMENT_MAX).nullable(),
|
|
602
652
|
outcome: statementText(STATEMENT_MAX).nullable(),
|
|
603
|
-
deviations:
|
|
653
|
+
deviations: z5.array(statementText(DEVIATION_MAX)).max(DEVIATIONS_MAX),
|
|
604
654
|
closeCommit: closeCommitSchema.nullable()
|
|
605
655
|
}).strict();
|
|
606
|
-
var closedBySchema =
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
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()
|
|
611
661
|
]);
|
|
612
662
|
var STEP_NAME_MAX = 200;
|
|
613
663
|
var stepNameSchema = plainText(STEP_NAME_MAX).refine((v) => v.trim().length > 0, {
|
|
614
664
|
message: "must not be blank"
|
|
615
665
|
});
|
|
616
|
-
var unfinishedStepSchema =
|
|
666
|
+
var unfinishedStepSchema = z5.object({
|
|
617
667
|
stepId: plainText(),
|
|
618
|
-
state:
|
|
668
|
+
state: z5.enum(["parked", "failed"]),
|
|
619
669
|
name: stepNameSchema.optional()
|
|
620
670
|
}).strict();
|
|
621
|
-
var crossedSchema =
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
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()
|
|
626
676
|
]);
|
|
627
|
-
var seatCoverageSchema =
|
|
628
|
-
enrolled:
|
|
629
|
-
conformant:
|
|
677
|
+
var seatCoverageSchema = z5.object({
|
|
678
|
+
enrolled: z5.number().int().nonnegative(),
|
|
679
|
+
conformant: z5.number().int().nonnegative()
|
|
630
680
|
}).strict().refine((c) => c.conformant <= c.enrolled, {
|
|
631
681
|
message: "conformant cannot exceed enrolled",
|
|
632
682
|
path: ["conformant"]
|
|
633
683
|
});
|
|
634
|
-
var observedCloseSchema =
|
|
635
|
-
sequence:
|
|
684
|
+
var observedCloseSchema = z5.object({
|
|
685
|
+
sequence: z5.number().int().min(1),
|
|
636
686
|
closedAt: utcIso8601,
|
|
637
687
|
closedBy: closedBySchema,
|
|
638
688
|
crossed: crossedSchema,
|
|
639
689
|
seatCoverage: seatCoverageSchema.nullable()
|
|
640
690
|
}).strict();
|
|
641
|
-
var couldNotRunCheckSchema =
|
|
691
|
+
var couldNotRunCheckSchema = z5.object({
|
|
642
692
|
check: plainText(),
|
|
643
693
|
description: plainText().nullable(),
|
|
644
694
|
reason: plainText()
|
|
645
695
|
}).strict();
|
|
646
|
-
var firedCheckSchema =
|
|
696
|
+
var firedCheckSchema = z5.object({
|
|
647
697
|
check: plainText(),
|
|
648
698
|
description: plainText().nullable(),
|
|
649
699
|
severity: severitySchema,
|
|
650
700
|
explanation: plainText().nullable(),
|
|
651
|
-
occurrences:
|
|
701
|
+
occurrences: z5.number().int().min(1)
|
|
652
702
|
}).strict();
|
|
653
|
-
var phaseChecksSchema =
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
state:
|
|
657
|
-
runs:
|
|
658
|
-
couldNotRun:
|
|
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)
|
|
659
709
|
}).strict(),
|
|
660
|
-
|
|
661
|
-
state:
|
|
662
|
-
runs:
|
|
663
|
-
couldNotRun:
|
|
664
|
-
fired:
|
|
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)
|
|
665
715
|
}).strict()
|
|
666
716
|
]);
|
|
667
|
-
var observedSchema =
|
|
717
|
+
var observedSchema = z5.object({
|
|
668
718
|
engagementId: plainText(),
|
|
669
719
|
methodVersion: methodVersionSchema,
|
|
670
|
-
closes:
|
|
720
|
+
closes: z5.array(observedCloseSchema).min(1),
|
|
671
721
|
checks: phaseChecksSchema
|
|
672
722
|
}).strict().superRefine((o, ctx) => {
|
|
673
723
|
o.closes.forEach((close, i) => {
|
|
@@ -680,18 +730,18 @@ var observedSchema = z4.object({
|
|
|
680
730
|
}
|
|
681
731
|
});
|
|
682
732
|
});
|
|
683
|
-
var attestedWalkSchema =
|
|
733
|
+
var attestedWalkSchema = z5.object({
|
|
684
734
|
verdict: walkVerdictSchema,
|
|
685
|
-
findings:
|
|
686
|
-
findingsNotShown:
|
|
735
|
+
findings: z5.array(plainText(FINDING_MAX)).max(FINDINGS_SHOWN_MAX),
|
|
736
|
+
findingsNotShown: z5.number().int().nonnegative()
|
|
687
737
|
}).strict();
|
|
688
|
-
var attestedStatementsSchema =
|
|
738
|
+
var attestedStatementsSchema = z5.object({
|
|
689
739
|
thesis: plainText(STATEMENT_MAX).nullable(),
|
|
690
740
|
outcome: plainText(STATEMENT_MAX).nullable(),
|
|
691
|
-
deviations:
|
|
741
|
+
deviations: z5.array(plainText(DEVIATION_MAX)).max(DEVIATIONS_MAX)
|
|
692
742
|
}).strict();
|
|
693
|
-
var attestedCloseSchema =
|
|
694
|
-
sequence:
|
|
743
|
+
var attestedCloseSchema = z5.object({
|
|
744
|
+
sequence: z5.number().int().min(1),
|
|
695
745
|
decision: closeDecisionSchema,
|
|
696
746
|
actor: plainText(ACTOR_MAX).nullable(),
|
|
697
747
|
reason: plainText(PHASE_CLOSE_REASON_MAX).nullable(),
|
|
@@ -699,15 +749,10 @@ var attestedCloseSchema = z4.object({
|
|
|
699
749
|
closeCommit: closeCommitSchema.nullable(),
|
|
700
750
|
statements: attestedStatementsSchema
|
|
701
751
|
}).strict();
|
|
702
|
-
var attestedSchema =
|
|
703
|
-
closes:
|
|
752
|
+
var attestedSchema = z5.object({
|
|
753
|
+
closes: z5.array(attestedCloseSchema)
|
|
704
754
|
}).strict();
|
|
705
|
-
|
|
706
|
-
format: z4.literal(BUILD_RECORD_V2_FORMAT),
|
|
707
|
-
phase: phaseNameSchema,
|
|
708
|
-
observed: observedSchema,
|
|
709
|
-
attested: attestedSchema
|
|
710
|
-
}).strict().superRefine((record2, ctx) => {
|
|
755
|
+
function checkRecordHalves(record2, ctx) {
|
|
711
756
|
const observedBySequence = new Map(record2.observed.closes.map((c) => [c.sequence, c]));
|
|
712
757
|
const attestedSequences = record2.attested.closes.map((c) => c.sequence);
|
|
713
758
|
const sameSet = attestedSequences.length === observedBySequence.size && new Set(attestedSequences).size === attestedSequences.length && attestedSequences.every((s) => observedBySequence.has(s));
|
|
@@ -735,18 +780,24 @@ var buildRecordV2Schema = z4.object({
|
|
|
735
780
|
});
|
|
736
781
|
}
|
|
737
782
|
});
|
|
738
|
-
}
|
|
783
|
+
}
|
|
784
|
+
var buildRecordV2Schema = z5.object({
|
|
785
|
+
format: z5.literal(BUILD_RECORD_V2_FORMAT),
|
|
786
|
+
phase: phaseNameSchema,
|
|
787
|
+
observed: observedSchema,
|
|
788
|
+
attested: attestedSchema
|
|
789
|
+
}).strict().superRefine(checkRecordHalves);
|
|
739
790
|
|
|
740
791
|
// ../events/dist/crew.js
|
|
741
|
-
import { z as
|
|
742
|
-
var crewGroupSchema =
|
|
743
|
-
var crewTierSchema =
|
|
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"]);
|
|
744
795
|
var CREW_DISCIPLINE_MAX = 64;
|
|
745
|
-
var crewFaceHeadSchema =
|
|
746
|
-
var crewFaceAntennaSchema =
|
|
747
|
-
var crewFaceEyesSchema =
|
|
748
|
-
var crewFaceMouthSchema =
|
|
749
|
-
var crewFaceSchema =
|
|
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({
|
|
750
801
|
// The outline of the head. Members who do the same kind of work share one, so a
|
|
751
802
|
// team is recognisable before any name is read.
|
|
752
803
|
head: crewFaceHeadSchema,
|
|
@@ -757,17 +808,30 @@ var crewFaceSchema = z5.object({
|
|
|
757
808
|
// The mouth.
|
|
758
809
|
mouth: crewFaceMouthSchema,
|
|
759
810
|
// Whether this member is drawn with ears.
|
|
760
|
-
ears:
|
|
811
|
+
ears: z6.boolean()
|
|
761
812
|
}).strict();
|
|
762
|
-
var
|
|
763
|
-
|
|
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"
|
|
823
|
+
]);
|
|
824
|
+
var crewMemberSchema = z6.object({
|
|
825
|
+
// The agent's name. The same names on every project, so a name is a
|
|
764
826
|
// vocabulary a returning client already knows.
|
|
765
|
-
callsign:
|
|
766
|
-
// Which of the
|
|
827
|
+
callsign: z6.string().min(1).max(32),
|
|
828
|
+
// Which of the four teams this member belongs to.
|
|
767
829
|
group: crewGroupSchema,
|
|
830
|
+
// The part this member plays, which decides the stages it leads and helps with.
|
|
831
|
+
role: crewRoleSchema,
|
|
768
832
|
// The kind of work this member takes. A task names a discipline, and only
|
|
769
833
|
// agents holding it can be dispatched to it.
|
|
770
|
-
discipline:
|
|
834
|
+
discipline: z6.string().min(1).max(CREW_DISCIPLINE_MAX),
|
|
771
835
|
// How much thinking this member brings. There is no field naming the supplier
|
|
772
836
|
// behind it, on this shape or on any other.
|
|
773
837
|
tier: crewTierSchema,
|
|
@@ -776,10 +840,44 @@ var crewMemberSchema = z5.object({
|
|
|
776
840
|
face: crewFaceSchema
|
|
777
841
|
}).strict();
|
|
778
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
|
+
},
|
|
779
876
|
// ── builders: code ──────────────────────────────────────────────────────────
|
|
780
877
|
{
|
|
781
878
|
callsign: "ADA",
|
|
782
879
|
group: "builder",
|
|
880
|
+
role: "builder",
|
|
783
881
|
discipline: "implementation",
|
|
784
882
|
tier: "deep",
|
|
785
883
|
face: { head: "sq", antenna: "stalk", eyes: "dots", mouth: "line", ears: false }
|
|
@@ -787,6 +885,7 @@ var CREW_ROSTER = [
|
|
|
787
885
|
{
|
|
788
886
|
callsign: "OTTO",
|
|
789
887
|
group: "builder",
|
|
888
|
+
role: "builder",
|
|
790
889
|
discipline: "implementation",
|
|
791
890
|
tier: "deep",
|
|
792
891
|
face: { head: "sq", antenna: "twin", eyes: "bars", mouth: "grid", ears: true }
|
|
@@ -794,6 +893,7 @@ var CREW_ROSTER = [
|
|
|
794
893
|
{
|
|
795
894
|
callsign: "MILO",
|
|
796
895
|
group: "builder",
|
|
896
|
+
role: "builder",
|
|
797
897
|
discipline: "implementation",
|
|
798
898
|
tier: "standard",
|
|
799
899
|
face: { head: "sqr", antenna: "stalk", eyes: "dots", mouth: "dots", ears: false }
|
|
@@ -801,42 +901,24 @@ var CREW_ROSTER = [
|
|
|
801
901
|
{
|
|
802
902
|
callsign: "NOVA",
|
|
803
903
|
group: "builder",
|
|
904
|
+
role: "builder",
|
|
804
905
|
discipline: "implementation",
|
|
805
906
|
tier: "standard",
|
|
806
907
|
face: { head: "sq", antenna: "dish", eyes: "cyclops", mouth: "line", ears: false }
|
|
807
908
|
},
|
|
808
|
-
{
|
|
809
|
-
callsign: "KAI",
|
|
810
|
-
group: "builder",
|
|
811
|
-
discipline: "implementation",
|
|
812
|
-
tier: "standard",
|
|
813
|
-
face: { head: "sqr", antenna: "none", eyes: "bars", mouth: "line", ears: true }
|
|
814
|
-
},
|
|
815
909
|
{
|
|
816
910
|
callsign: "HUGO",
|
|
817
911
|
group: "builder",
|
|
912
|
+
role: "builder",
|
|
818
913
|
discipline: "infrastructure",
|
|
819
914
|
tier: "standard",
|
|
820
915
|
face: { head: "sq", antenna: "twin", eyes: "dots", mouth: "grid", ears: true }
|
|
821
916
|
},
|
|
822
|
-
{
|
|
823
|
-
callsign: "WREN",
|
|
824
|
-
group: "builder",
|
|
825
|
-
discipline: "refactor",
|
|
826
|
-
tier: "fast",
|
|
827
|
-
face: { head: "sqr", antenna: "stalk", eyes: "bars", mouth: "wave", ears: false }
|
|
828
|
-
},
|
|
829
|
-
{
|
|
830
|
-
callsign: "PIP",
|
|
831
|
-
group: "builder",
|
|
832
|
-
discipline: "refactor",
|
|
833
|
-
tier: "fast",
|
|
834
|
-
face: { head: "sqr", antenna: "none", eyes: "dots", mouth: "dots", ears: false }
|
|
835
|
-
},
|
|
836
917
|
// ── builders: specs and docs ────────────────────────────────────────────────
|
|
837
918
|
{
|
|
838
919
|
callsign: "IRIS",
|
|
839
920
|
group: "builder",
|
|
921
|
+
role: "spec-writer",
|
|
840
922
|
discipline: "feature spec",
|
|
841
923
|
tier: "deep",
|
|
842
924
|
face: { head: "dome", antenna: "stalk", eyes: "visor", mouth: "line", ears: false }
|
|
@@ -844,6 +926,7 @@ var CREW_ROSTER = [
|
|
|
844
926
|
{
|
|
845
927
|
callsign: "JUNO",
|
|
846
928
|
group: "builder",
|
|
929
|
+
role: "spec-writer",
|
|
847
930
|
discipline: "feature spec",
|
|
848
931
|
tier: "standard",
|
|
849
932
|
face: { head: "dome", antenna: "none", eyes: "dots", mouth: "wave", ears: false }
|
|
@@ -851,6 +934,7 @@ var CREW_ROSTER = [
|
|
|
851
934
|
{
|
|
852
935
|
callsign: "LEX",
|
|
853
936
|
group: "builder",
|
|
937
|
+
role: "context-docs",
|
|
854
938
|
discipline: "context & docs",
|
|
855
939
|
tier: "fast",
|
|
856
940
|
face: { head: "dome", antenna: "twin", eyes: "bars", mouth: "dots", ears: false }
|
|
@@ -859,6 +943,7 @@ var CREW_ROSTER = [
|
|
|
859
943
|
{
|
|
860
944
|
callsign: "VERA",
|
|
861
945
|
group: "reviewer",
|
|
946
|
+
role: "spec-reviewer",
|
|
862
947
|
discipline: "spec consistency",
|
|
863
948
|
tier: "deep",
|
|
864
949
|
face: { head: "rnd", antenna: "stalk", eyes: "visor", mouth: "line", ears: false }
|
|
@@ -866,6 +951,7 @@ var CREW_ROSTER = [
|
|
|
866
951
|
{
|
|
867
952
|
callsign: "ODIN",
|
|
868
953
|
group: "reviewer",
|
|
954
|
+
role: "spec-reviewer",
|
|
869
955
|
discipline: "cross-spec audit",
|
|
870
956
|
tier: "deep",
|
|
871
957
|
face: { head: "rnd", antenna: "dish", eyes: "cyclops", mouth: "line", ears: false }
|
|
@@ -873,6 +959,7 @@ var CREW_ROSTER = [
|
|
|
873
959
|
{
|
|
874
960
|
callsign: "CASS",
|
|
875
961
|
group: "reviewer",
|
|
962
|
+
role: "code-test-reviewer",
|
|
876
963
|
discipline: "code review",
|
|
877
964
|
tier: "deep",
|
|
878
965
|
face: { head: "rnd", antenna: "none", eyes: "visor", mouth: "grid", ears: true }
|
|
@@ -880,13 +967,23 @@ var CREW_ROSTER = [
|
|
|
880
967
|
{
|
|
881
968
|
callsign: "THEO",
|
|
882
969
|
group: "reviewer",
|
|
970
|
+
role: "code-test-reviewer",
|
|
883
971
|
discipline: "code review",
|
|
884
972
|
tier: "standard",
|
|
885
973
|
face: { head: "rnd", antenna: "twin", eyes: "bars", mouth: "line", ears: false }
|
|
886
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
|
+
},
|
|
887
983
|
{
|
|
888
984
|
callsign: "ECHO",
|
|
889
985
|
group: "reviewer",
|
|
986
|
+
role: "code-test-reviewer",
|
|
890
987
|
discipline: "test quality",
|
|
891
988
|
tier: "standard",
|
|
892
989
|
face: { head: "rnd", antenna: "stalk", eyes: "dots", mouth: "wave", ears: false }
|
|
@@ -894,6 +991,7 @@ var CREW_ROSTER = [
|
|
|
894
991
|
{
|
|
895
992
|
callsign: "MAVIS",
|
|
896
993
|
group: "reviewer",
|
|
994
|
+
role: "code-test-reviewer",
|
|
897
995
|
discipline: "suites & coverage",
|
|
898
996
|
tier: "standard",
|
|
899
997
|
face: { head: "rnd", antenna: "none", eyes: "visor", mouth: "dots", ears: false }
|
|
@@ -901,6 +999,7 @@ var CREW_ROSTER = [
|
|
|
901
999
|
{
|
|
902
1000
|
callsign: "ZARA",
|
|
903
1001
|
group: "reviewer",
|
|
1002
|
+
role: "code-test-reviewer",
|
|
904
1003
|
discipline: "smoke & walk aid",
|
|
905
1004
|
tier: "standard",
|
|
906
1005
|
face: { head: "rnd", antenna: "dish", eyes: "bars", mouth: "wave", ears: false }
|
|
@@ -908,32 +1007,1206 @@ var CREW_ROSTER = [
|
|
|
908
1007
|
{
|
|
909
1008
|
callsign: "NORA",
|
|
910
1009
|
group: "reviewer",
|
|
1010
|
+
role: "code-test-reviewer",
|
|
911
1011
|
discipline: "evidence & provenance",
|
|
912
1012
|
tier: "fast",
|
|
913
1013
|
face: { head: "rnd", antenna: "twin", eyes: "dots", mouth: "line", ears: false }
|
|
914
1014
|
},
|
|
915
|
-
// ──
|
|
1015
|
+
// ── leads ───────────────────────────────────────────────────────────────────
|
|
916
1016
|
{
|
|
917
1017
|
callsign: "ARGUS",
|
|
918
|
-
group: "
|
|
919
|
-
|
|
1018
|
+
group: "lead",
|
|
1019
|
+
role: "quality-lead",
|
|
1020
|
+
discipline: "quality lead \xB7 every diff",
|
|
920
1021
|
tier: "standard",
|
|
921
1022
|
face: { head: "hex", antenna: "dish", eyes: "visor", mouth: "grid", ears: true }
|
|
922
1023
|
},
|
|
923
1024
|
{
|
|
924
1025
|
callsign: "ATLAS",
|
|
925
|
-
group: "
|
|
926
|
-
|
|
1026
|
+
group: "lead",
|
|
1027
|
+
role: "delivery-lead",
|
|
1028
|
+
discipline: "delivery lead",
|
|
927
1029
|
tier: "deep",
|
|
928
1030
|
face: { head: "hex", antenna: "twin", eyes: "cyclops", mouth: "line", ears: true }
|
|
929
1031
|
}
|
|
930
1032
|
];
|
|
931
1033
|
var CREW_ROSTER_SIZE = CREW_ROSTER.length;
|
|
932
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)
|
|
1140
|
+
}).strict();
|
|
1141
|
+
var systemDrawPhaseRequestSchema = z7.object({
|
|
1142
|
+
picture: z7.literal("phase"),
|
|
1143
|
+
phase: phaseIdentitySchema,
|
|
1144
|
+
planVersion: z7.number().int().min(1)
|
|
1145
|
+
}).strict();
|
|
1146
|
+
var systemDrawClosedPhaseRequestSchema = z7.object({
|
|
1147
|
+
picture: z7.literal("closed-phase"),
|
|
1148
|
+
phase: phaseIdentitySchema
|
|
1149
|
+
}).strict();
|
|
1150
|
+
var systemDrawRequestSchema = z7.discriminatedUnion("picture", [
|
|
1151
|
+
systemDrawArchitectureRequestSchema,
|
|
1152
|
+
systemDrawPhaseRequestSchema,
|
|
1153
|
+
systemDrawClosedPhaseRequestSchema
|
|
1154
|
+
]);
|
|
1155
|
+
function utf8ByteLength2(text) {
|
|
1156
|
+
let bytes = 0;
|
|
1157
|
+
for (let i = 0; i < text.length; i++) {
|
|
1158
|
+
const unit = text.charCodeAt(i);
|
|
1159
|
+
if (unit < 128)
|
|
1160
|
+
bytes += 1;
|
|
1161
|
+
else if (unit < 2048)
|
|
1162
|
+
bytes += 2;
|
|
1163
|
+
else if (unit >= 55296 && unit <= 56319 && i + 1 < text.length) {
|
|
1164
|
+
const next = text.charCodeAt(i + 1);
|
|
1165
|
+
if (next >= 56320 && next <= 57343) {
|
|
1166
|
+
bytes += 4;
|
|
1167
|
+
i++;
|
|
1168
|
+
} else {
|
|
1169
|
+
bytes += 3;
|
|
1170
|
+
}
|
|
1171
|
+
} else
|
|
1172
|
+
bytes += 3;
|
|
1173
|
+
}
|
|
1174
|
+
return bytes;
|
|
1175
|
+
}
|
|
1176
|
+
var BASE64 = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
1177
|
+
function base64DecodedLength(text) {
|
|
1178
|
+
const padding = text.endsWith("==") ? 2 : text.endsWith("=") ? 1 : 0;
|
|
1179
|
+
return text.length / 4 * 3 - padding;
|
|
1180
|
+
}
|
|
1181
|
+
var systemPictureSchema = z7.object({
|
|
1182
|
+
svg: z7.string().min(1).refine((v) => utf8ByteLength2(v) <= SYSTEM_DRAW_SVG_MAX_BYTES, { message: "the picture is larger than 2 MB" }),
|
|
1183
|
+
png: z7.string().min(1).refine((v) => v.length % 4 === 0 && BASE64.test(v), { message: "the image is not valid base64" }).refine((v) => base64DecodedLength(v) <= SYSTEM_DRAW_PNG_MAX_BYTES, { message: "the image is larger than 8 MB" })
|
|
1184
|
+
}).strict();
|
|
1185
|
+
var systemDrawRefusedSchema = z7.object({
|
|
1186
|
+
outcome: z7.literal("refused"),
|
|
1187
|
+
reason: z7.string().min(1)
|
|
1188
|
+
}).strict();
|
|
1189
|
+
var systemDrawUnavailableSchema = z7.object({
|
|
1190
|
+
outcome: z7.literal("unavailable")
|
|
1191
|
+
}).strict();
|
|
1192
|
+
var systemDrawNotClosedSchema = z7.object({
|
|
1193
|
+
outcome: z7.literal("not-closed")
|
|
1194
|
+
}).strict();
|
|
1195
|
+
|
|
1196
|
+
// ../events/dist/build-snapshot.js
|
|
1197
|
+
var buildTaskStateSchema = z8.enum([
|
|
1198
|
+
"pending",
|
|
1199
|
+
"in-flight",
|
|
1200
|
+
"completed",
|
|
1201
|
+
"failed",
|
|
1202
|
+
"blocked"
|
|
1203
|
+
]);
|
|
1204
|
+
var BUILD_TASK_TITLE_MAX = 240;
|
|
1205
|
+
var BUILD_SNAPSHOT_TASK_LIMIT = 500;
|
|
1206
|
+
var BUILD_TASK_WHY_OUTSIDE_PLAN_MAX = 160;
|
|
1207
|
+
var BUILD_TASK_BUDGET_MAX_MINUTES = 10080;
|
|
1208
|
+
var BUILD_TASK_NOTE_MAX = 240;
|
|
1209
|
+
var BUILD_TASK_CLEAR_COMMAND_MAX = 240;
|
|
1210
|
+
var buildTaskKindSchema = z8.enum(["build", "review", "spec", "walk"]);
|
|
1211
|
+
var buildTaskIdSchema = z8.string().min(1).max(64);
|
|
1212
|
+
var buildTaskSchema = z8.object({
|
|
1213
|
+
// The task's own id, as the coordinator's graph names it.
|
|
1214
|
+
id: buildTaskIdSchema,
|
|
1215
|
+
// What the task is, in the coordinator's words. A description of the work,
|
|
1216
|
+
// bounded and scanned — never an excerpt of anybody's code.
|
|
1217
|
+
title: z8.string().min(1).max(BUILD_TASK_TITLE_MAX),
|
|
1218
|
+
// What the task is doing right now.
|
|
1219
|
+
state: buildTaskStateSchema,
|
|
1220
|
+
// Who holds it — a bare callsign, or the value naming a person when the work is
|
|
1221
|
+
// a person's. Absent when nothing has been dispatched to it yet.
|
|
1222
|
+
agent: agentNameSchema.optional(),
|
|
1223
|
+
// The kind of work the holder does. Present exactly when an agent holds it.
|
|
1224
|
+
discipline: z8.string().min(1).max(64).optional(),
|
|
1225
|
+
// How much thinking the holder brings. Present exactly when an agent holds it,
|
|
1226
|
+
// and there is no field naming the supplier behind it.
|
|
1227
|
+
tier: z8.string().min(1).max(32).optional(),
|
|
1228
|
+
// The tasks this one cannot start before. Always present; empty means it waits
|
|
1229
|
+
// on nothing, which is different from not having been computed.
|
|
1230
|
+
dependsOn: z8.array(buildTaskIdSchema).max(BUILD_SNAPSHOT_TASK_LIMIT),
|
|
1231
|
+
// The one task this task checks, when it is a check on other work. Absent on a
|
|
1232
|
+
// task that is doing the work rather than checking it.
|
|
1233
|
+
reviews: buildTaskIdSchema.optional(),
|
|
1234
|
+
// Which wave of the phase this task belongs to — the wave that writes the
|
|
1235
|
+
// specifications, or the wave that builds them. Absent when the graph does not
|
|
1236
|
+
// record it, which is different from the task belonging to neither.
|
|
1237
|
+
layer: methodLayerSchema.optional(),
|
|
1238
|
+
// When it was picked up, and when it settled. A reader computes how long it
|
|
1239
|
+
// took; neither this row nor this payload carries a duration.
|
|
1240
|
+
startedAt: utcIso8601.optional(),
|
|
1241
|
+
settledAt: utcIso8601.optional(),
|
|
1242
|
+
// The parts of this project's system the task builds, by their ids. Absent when
|
|
1243
|
+
// it builds none; never an empty list.
|
|
1244
|
+
builds: z8.array(systemPartIdSchema).min(1).max(SYSTEM_PARTS_MAX).optional(),
|
|
1245
|
+
// In one line, why the task built a part the phase's plan does not include, in
|
|
1246
|
+
// the words of whoever holds it. Absent when it gave no reason.
|
|
1247
|
+
whyOutsidePlan: descriptionText(BUILD_TASK_WHY_OUTSIDE_PLAN_MAX).optional(),
|
|
1248
|
+
// How long the task is expected to take, in whole minutes, from one minute to one
|
|
1249
|
+
// week. A plan, not a measurement; absent when no budget was set.
|
|
1250
|
+
budgetMinutes: z8.number().int().min(1).max(BUILD_TASK_BUDGET_MAX_MINUTES).optional(),
|
|
1251
|
+
// 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.
|
|
1253
|
+
kind: buildTaskKindSchema.optional(),
|
|
1254
|
+
// One line from whoever holds the task about where it stands. Absent when there
|
|
1255
|
+
// is nothing to say.
|
|
1256
|
+
note: descriptionText(BUILD_TASK_NOTE_MAX).optional(),
|
|
1257
|
+
// On a task held for a person, the one thing they do or run to clear it, in one
|
|
1258
|
+
// line. Absent when the task names none.
|
|
1259
|
+
clearCommand: descriptionText(BUILD_TASK_CLEAR_COMMAND_MAX).optional(),
|
|
1260
|
+
// On a task held for a person, the crew member ready to take it on once it is
|
|
1261
|
+
// cleared, by callsign. Absent when nobody is standing by.
|
|
1262
|
+
standingBy: agentNameSchema.optional()
|
|
1263
|
+
}).strict().superRefine((task, ctx) => {
|
|
1264
|
+
if (task.reviews !== void 0 && task.reviews === task.id) {
|
|
1265
|
+
ctx.addIssue({
|
|
1266
|
+
code: z8.ZodIssueCode.custom,
|
|
1267
|
+
path: ["reviews"],
|
|
1268
|
+
message: `'${task.id}' reviews itself \u2014 a review names the OTHER task it checks.`
|
|
1269
|
+
});
|
|
1270
|
+
}
|
|
1271
|
+
if (task.layer !== void 0 && !isPhaseScopedLayer(task.layer)) {
|
|
1272
|
+
ctx.addIssue({
|
|
1273
|
+
code: z8.ZodIssueCode.custom,
|
|
1274
|
+
path: ["layer"],
|
|
1275
|
+
message: `'${task.layer}' is not a wave a phase runs \u2014 expected one of ${PHASE_SCOPED_LAYERS.join(", ")}.`
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
const profiled = task.agent !== void 0 && task.agent !== HUMAN_AGENT;
|
|
1279
|
+
for (const field of ["discipline", "tier"]) {
|
|
1280
|
+
if (profiled && task[field] === void 0) {
|
|
1281
|
+
ctx.addIssue({
|
|
1282
|
+
code: z8.ZodIssueCode.custom,
|
|
1283
|
+
path: [field],
|
|
1284
|
+
message: `'${field}' is required on a task an agent holds.`
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
1287
|
+
if (!profiled && task[field] !== void 0) {
|
|
1288
|
+
ctx.addIssue({
|
|
1289
|
+
code: z8.ZodIssueCode.custom,
|
|
1290
|
+
path: [field],
|
|
1291
|
+
message: task.agent === void 0 ? `'${field}' must be absent on a task nobody holds.` : `'${field}' must be absent on work a person holds.`
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
});
|
|
1296
|
+
var buildPhaseSchema = z8.string().min(1).max(128);
|
|
1297
|
+
var BUILD_PHASE_TITLE_MAX = 160;
|
|
1298
|
+
var buildPhaseTitleSchema = z8.string().min(1).max(BUILD_PHASE_TITLE_MAX);
|
|
1299
|
+
var buildTaskListSchema = z8.array(buildTaskSchema).max(BUILD_SNAPSHOT_TASK_LIMIT).superRefine((tasks, ctx) => {
|
|
1300
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1301
|
+
for (const [index, task] of tasks.entries()) {
|
|
1302
|
+
if (seen.has(task.id)) {
|
|
1303
|
+
ctx.addIssue({
|
|
1304
|
+
code: z8.ZodIssueCode.custom,
|
|
1305
|
+
path: [index, "id"],
|
|
1306
|
+
message: `duplicate task id '${task.id}' \u2014 one graph names each task once.`
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
seen.add(task.id);
|
|
1310
|
+
}
|
|
1311
|
+
});
|
|
1312
|
+
var buildTaskMarkSchema = z8.object({
|
|
1313
|
+
// The task that was marked.
|
|
1314
|
+
task: buildTaskIdSchema,
|
|
1315
|
+
// When it was first marked.
|
|
1316
|
+
at: utcIso8601
|
|
1317
|
+
}).strict();
|
|
1318
|
+
var buildSnapshotSchema = z8.object({
|
|
1319
|
+
// Which phase this graph belongs to.
|
|
1320
|
+
phase: buildPhaseSchema,
|
|
1321
|
+
// That phase's name in words, when the project gives it one.
|
|
1322
|
+
phaseTitle: buildPhaseTitleSchema.optional(),
|
|
1323
|
+
// Every task on it. Never a partial graph and never a change since last time.
|
|
1324
|
+
tasks: buildTaskListSchema,
|
|
1325
|
+
// When this graph was last reported.
|
|
1326
|
+
reportedAt: utcIso8601,
|
|
1327
|
+
// When work on this phase was first reported with no approval of its scope in force.
|
|
1328
|
+
builtWithoutApproval: utcIso8601.optional(),
|
|
1329
|
+
// Each task that started on a part still waiting for approval, and when it was
|
|
1330
|
+
// first seen doing so. Absent when there is none.
|
|
1331
|
+
tasksBuiltWithoutApproval: z8.array(buildTaskMarkSchema).min(1).max(BUILD_SNAPSHOT_TASK_LIMIT).optional()
|
|
1332
|
+
}).strict();
|
|
1333
|
+
var buildPhaseEntrySchema = z8.object({
|
|
1334
|
+
// Which phase has a stored graph.
|
|
1335
|
+
phase: buildPhaseSchema,
|
|
1336
|
+
// That phase's name in words, when the project gave it one.
|
|
1337
|
+
phaseTitle: buildPhaseTitleSchema.optional(),
|
|
1338
|
+
// When that phase's graph was last reported.
|
|
1339
|
+
reportedAt: utcIso8601,
|
|
1340
|
+
// Present when work on this phase was reported with no approval of its scope in force.
|
|
1341
|
+
builtWithoutApproval: z8.literal(true).optional()
|
|
1342
|
+
}).strict();
|
|
1343
|
+
var buildSnapshotReportSchema = z8.object({
|
|
1344
|
+
// Which phase this graph belongs to.
|
|
1345
|
+
phase: buildPhaseSchema,
|
|
1346
|
+
// That phase's name in words, when the project gives it one.
|
|
1347
|
+
phaseTitle: buildPhaseTitleSchema.optional(),
|
|
1348
|
+
// The whole graph, every time. Reporting a change instead of the graph is the
|
|
1349
|
+
// one thing this route does not accept.
|
|
1350
|
+
tasks: buildTaskListSchema
|
|
1351
|
+
}).strict();
|
|
1352
|
+
var buildSnapshotReportResultSchema = z8.object({
|
|
1353
|
+
// What happened. One outcome today; a refusal travels as the shared wire error.
|
|
1354
|
+
outcome: z8.literal("recorded"),
|
|
1355
|
+
// Which phase the graph was recorded against.
|
|
1356
|
+
phase: buildPhaseSchema,
|
|
1357
|
+
// When it was recorded.
|
|
1358
|
+
reportedAt: utcIso8601
|
|
1359
|
+
}).strict();
|
|
1360
|
+
|
|
1361
|
+
// ../events/dist/phase-plan.js
|
|
1362
|
+
import { z as z9 } from "zod";
|
|
1363
|
+
var PHASE_PLAN_FORMAT = 1;
|
|
1364
|
+
var PHASE_PLAN_CHANGES_MAX = 60;
|
|
1365
|
+
var PHASE_PLAN_SUMMARY_MAX = 160;
|
|
1366
|
+
var SCOPE_NOTE_MAX = 500;
|
|
1367
|
+
var PHASE_PLAN_PHASE_MAX = buildPhaseSchema.maxLength ?? 0;
|
|
1368
|
+
var PHASE_PLAN_CHANGE_KINDS = ["add", "change", "remove"];
|
|
1369
|
+
var phasePlanChangeSchema = z9.enum(PHASE_PLAN_CHANGE_KINDS, {
|
|
1370
|
+
error: `must be one of ${PHASE_PLAN_CHANGE_KINDS.join(", ")}`
|
|
1371
|
+
});
|
|
1372
|
+
var phasePlanPhaseSchema = phaseIdentitySchema.refine((v) => !isValidPhaseIdentity(v) || buildPhaseSchema.safeParse(String(v)).success, { message: `must be at most ${PHASE_PLAN_PHASE_MAX} characters` });
|
|
1373
|
+
var phasePlanSummarySchema = descriptionText(PHASE_PLAN_SUMMARY_MAX);
|
|
1374
|
+
var phasePlanEntrySchema = z9.object({
|
|
1375
|
+
part: systemPartIdSchema,
|
|
1376
|
+
change: phasePlanChangeSchema,
|
|
1377
|
+
summary: phasePlanSummarySchema
|
|
1378
|
+
}).strict();
|
|
1379
|
+
var phasePlanSchema = z9.object({
|
|
1380
|
+
format: z9.literal(PHASE_PLAN_FORMAT, { error: `must be ${PHASE_PLAN_FORMAT}` }),
|
|
1381
|
+
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` })
|
|
1383
|
+
}).strict();
|
|
1384
|
+
var scopePhaseSchema = buildPhaseSchema.refine((v) => isValidPhaseIdentity(v), {
|
|
1385
|
+
message: "must be a phase name"
|
|
1386
|
+
});
|
|
1387
|
+
var SCOPE_STATE_NAMES = [
|
|
1388
|
+
"not-planned",
|
|
1389
|
+
"waiting",
|
|
1390
|
+
"sent-back",
|
|
1391
|
+
"approved",
|
|
1392
|
+
"partly-stale"
|
|
1393
|
+
];
|
|
1394
|
+
var scopeStateNameSchema = z9.enum(SCOPE_STATE_NAMES);
|
|
1395
|
+
var plannedScopeStateSchema = scopeStateNameSchema.exclude(["not-planned"]);
|
|
1396
|
+
var SCOPE_PART_STATUSES = ["approved", "waiting"];
|
|
1397
|
+
var scopePartStatusSchema = z9.enum(SCOPE_PART_STATUSES);
|
|
1398
|
+
var SCOPE_ACTS = ["approve", "send-back"];
|
|
1399
|
+
var scopeActSchema = z9.enum(SCOPE_ACTS);
|
|
1400
|
+
var SCOPE_GIVEN_IN = ["console", "chat"];
|
|
1401
|
+
var scopeGivenInSchema = z9.enum(SCOPE_GIVEN_IN);
|
|
1402
|
+
var scopeNoteSchema = descriptionText(SCOPE_NOTE_MAX);
|
|
1403
|
+
var scopeNoteWireSchema = z9.string().max(SCOPE_NOTE_MAX);
|
|
1404
|
+
var SCOPE_PART_VERDICTS = ["ok", "change"];
|
|
1405
|
+
var scopePartVerdictSchema = z9.enum(SCOPE_PART_VERDICTS);
|
|
1406
|
+
var scopeDecisionPartSchema = z9.object({
|
|
1407
|
+
part: systemPartIdSchema,
|
|
1408
|
+
verdict: scopePartVerdictSchema,
|
|
1409
|
+
note: scopeNoteWireSchema.optional()
|
|
1410
|
+
}).strict();
|
|
1411
|
+
var planVersionSchema = z9.number().int().min(1);
|
|
1412
|
+
var carriedPhaseSchema = z9.object({
|
|
1413
|
+
phase: scopePhaseSchema,
|
|
1414
|
+
number: z9.number().int().min(1)
|
|
1415
|
+
}).strict();
|
|
1416
|
+
var carriedFromEntrySchema = carriedPhaseSchema.extend({
|
|
1417
|
+
part: systemPartIdSchema
|
|
1418
|
+
}).strict();
|
|
1419
|
+
var phasePlanRequestSchema = z9.object({
|
|
1420
|
+
descriptionVersion: z9.number().int().min(1),
|
|
1421
|
+
plan: z9.unknown()
|
|
1422
|
+
}).strict();
|
|
1423
|
+
var phasePlanRecordedSchema = z9.object({
|
|
1424
|
+
outcome: z9.literal("recorded"),
|
|
1425
|
+
version: planVersionSchema,
|
|
1426
|
+
phaseNumber: z9.number().int().min(1),
|
|
1427
|
+
state: plannedScopeStateSchema,
|
|
1428
|
+
changedParts: z9.array(systemPartIdSchema).max(PHASE_PLAN_CHANGES_MAX),
|
|
1429
|
+
typedApprovalOffered: z9.boolean(),
|
|
1430
|
+
reportedAt: utcIso8601,
|
|
1431
|
+
carriedFrom: z9.array(carriedFromEntrySchema).min(1).max(PHASE_PLAN_CHANGES_MAX).optional()
|
|
1432
|
+
}).strict();
|
|
1433
|
+
var phasePlanRefusedSchema = z9.object({
|
|
1434
|
+
outcome: z9.literal("refused"),
|
|
1435
|
+
problems: z9.array(systemDescriptionProblemSchema).min(1)
|
|
1436
|
+
}).strict();
|
|
1437
|
+
var phasePlanUnavailableSchema = z9.object({
|
|
1438
|
+
outcome: z9.literal("unavailable")
|
|
1439
|
+
}).strict();
|
|
1440
|
+
var REMOVAL_OWED_ACTS = ["dropped", "removed"];
|
|
1441
|
+
var removalOwedPartSchema = z9.object({
|
|
1442
|
+
part: systemPartIdSchema,
|
|
1443
|
+
name: systemPartSchema.shape.name,
|
|
1444
|
+
phase: scopePhaseSchema,
|
|
1445
|
+
number: z9.number().int().min(1),
|
|
1446
|
+
act: z9.enum(REMOVAL_OWED_ACTS)
|
|
1447
|
+
}).strict();
|
|
1448
|
+
var phasePlanRemovalOwedSchema = z9.object({
|
|
1449
|
+
outcome: z9.literal("removal-owed"),
|
|
1450
|
+
parts: z9.array(removalOwedPartSchema).min(1).max(SYSTEM_PARTS_MAX)
|
|
1451
|
+
}).strict();
|
|
1452
|
+
var phasePlanResultSchema = z9.discriminatedUnion("outcome", [
|
|
1453
|
+
phasePlanRecordedSchema,
|
|
1454
|
+
phasePlanRefusedSchema,
|
|
1455
|
+
phasePlanUnavailableSchema,
|
|
1456
|
+
phasePlanRemovalOwedSchema
|
|
1457
|
+
]);
|
|
1458
|
+
var scopeApproveRequestSchema = z9.object({
|
|
1459
|
+
act: z9.literal("approve"),
|
|
1460
|
+
planVersion: planVersionSchema,
|
|
1461
|
+
parts: z9.array(scopeDecisionPartSchema).max(PHASE_PLAN_CHANGES_MAX)
|
|
1462
|
+
}).strict();
|
|
1463
|
+
var scopeSendBackRequestSchema = z9.object({
|
|
1464
|
+
act: z9.literal("send-back"),
|
|
1465
|
+
planVersion: planVersionSchema,
|
|
1466
|
+
parts: z9.array(scopeDecisionPartSchema).max(PHASE_PLAN_CHANGES_MAX),
|
|
1467
|
+
note: scopeNoteWireSchema.optional()
|
|
1468
|
+
}).strict();
|
|
1469
|
+
var scopeDecisionRequestSchema = z9.discriminatedUnion("act", [
|
|
1470
|
+
scopeApproveRequestSchema,
|
|
1471
|
+
scopeSendBackRequestSchema
|
|
1472
|
+
]);
|
|
1473
|
+
var scopeDecisionRecordedSchema = z9.object({
|
|
1474
|
+
outcome: z9.literal("recorded"),
|
|
1475
|
+
act: scopeActSchema,
|
|
1476
|
+
phase: scopePhaseSchema,
|
|
1477
|
+
planVersion: planVersionSchema,
|
|
1478
|
+
state: plannedScopeStateSchema,
|
|
1479
|
+
decidedAt: utcIso8601
|
|
1480
|
+
}).strict();
|
|
1481
|
+
var scopeDecisionAlreadyApprovedSchema = z9.object({
|
|
1482
|
+
outcome: z9.literal("already-approved"),
|
|
1483
|
+
replanNeeded: z9.boolean(),
|
|
1484
|
+
reason: z9.string().min(1)
|
|
1485
|
+
}).strict();
|
|
1486
|
+
var scopeDecisionRefusedSchema = z9.object({
|
|
1487
|
+
outcome: z9.literal("refused"),
|
|
1488
|
+
problems: z9.array(systemDescriptionProblemSchema).min(1)
|
|
1489
|
+
}).strict();
|
|
1490
|
+
var scopeDecisionUnavailableSchema = z9.object({
|
|
1491
|
+
outcome: z9.literal("unavailable")
|
|
1492
|
+
}).strict();
|
|
1493
|
+
function reasonOutcome(outcome) {
|
|
1494
|
+
return z9.object({ outcome: z9.literal(outcome), reason: z9.string().min(1) }).strict();
|
|
1495
|
+
}
|
|
1496
|
+
var scopeDecisionResultSchema = z9.discriminatedUnion("outcome", [
|
|
1497
|
+
scopeDecisionRecordedSchema,
|
|
1498
|
+
scopeDecisionAlreadyApprovedSchema,
|
|
1499
|
+
scopeDecisionRefusedSchema,
|
|
1500
|
+
scopeDecisionUnavailableSchema,
|
|
1501
|
+
reasonOutcome("not-owner"),
|
|
1502
|
+
reasonOutcome("no-owner"),
|
|
1503
|
+
reasonOutcome("plan-changed"),
|
|
1504
|
+
reasonOutcome("build-started")
|
|
1505
|
+
]);
|
|
1506
|
+
var scopeReadPartSchema = z9.object({
|
|
1507
|
+
part: systemPartIdSchema,
|
|
1508
|
+
name: systemPartSchema.shape.name,
|
|
1509
|
+
change: phasePlanChangeSchema,
|
|
1510
|
+
summary: phasePlanSummarySchema,
|
|
1511
|
+
status: scopePartStatusSchema,
|
|
1512
|
+
changed: z9.boolean(),
|
|
1513
|
+
carriedFrom: carriedPhaseSchema.optional()
|
|
1514
|
+
}).strict();
|
|
1515
|
+
var scopeReadDecisionSchema = z9.object({
|
|
1516
|
+
act: scopeActSchema,
|
|
1517
|
+
planVersion: planVersionSchema,
|
|
1518
|
+
givenIn: scopeGivenInSchema,
|
|
1519
|
+
at: utcIso8601,
|
|
1520
|
+
parts: z9.array(scopeDecisionPartSchema).max(PHASE_PLAN_CHANGES_MAX),
|
|
1521
|
+
note: scopeNoteWireSchema.optional()
|
|
1522
|
+
}).strict();
|
|
1523
|
+
var scopeReadNotPlannedSchema = z9.object({
|
|
1524
|
+
phase: scopePhaseSchema,
|
|
1525
|
+
state: z9.literal("not-planned"),
|
|
1526
|
+
canDecide: z9.boolean()
|
|
1527
|
+
}).strict();
|
|
1528
|
+
var scopeReadPlannedSchema = z9.object({
|
|
1529
|
+
phase: scopePhaseSchema,
|
|
1530
|
+
state: plannedScopeStateSchema,
|
|
1531
|
+
phaseNumber: z9.number().int().min(1),
|
|
1532
|
+
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()
|
|
1537
|
+
}).strict();
|
|
1538
|
+
var scopeReadSchema = z9.union([scopeReadNotPlannedSchema, scopeReadPlannedSchema]);
|
|
1539
|
+
|
|
1540
|
+
// ../events/dist/build-record-v3.js
|
|
1541
|
+
import { z as z14 } from "zod";
|
|
1542
|
+
|
|
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
|
+
]);
|
|
1560
|
+
|
|
1561
|
+
// ../events/dist/code-link.js
|
|
1562
|
+
import { z as z11 } from "zod";
|
|
1563
|
+
var codeLinkAddressSchema = z11.string().regex(GITHUB_REPOSITORY_ADDRESS, { message: "must be a GitHub repository address: https://github.com/<owner>/<repo>" }).refine((v) => !looksLikeSecret(v), { message: "looks like a password or key, so it is not sent" });
|
|
1564
|
+
var codeLinkFactsSchema = z11.object({
|
|
1565
|
+
repositoryAddress: codeLinkAddressSchema.nullable(),
|
|
1566
|
+
headOnGitHub: z11.boolean()
|
|
1567
|
+
}).strict().refine((v) => !(v.headOnGitHub && v.repositoryAddress === null), {
|
|
1568
|
+
message: "cannot be true when the code is not on GitHub",
|
|
1569
|
+
path: ["headOnGitHub"]
|
|
1570
|
+
});
|
|
1571
|
+
var PART_CODE_LINK_SCOPES = ["part", "phase"];
|
|
1572
|
+
var PART_CODE_LINK_RANGE_REASONS = ["not-github", "not-pushed"];
|
|
1573
|
+
var PART_CODE_LINK_URL_PREFIX = "https://github.com/";
|
|
1574
|
+
var partCodeLinkGithubSchema = z11.object({
|
|
1575
|
+
kind: z11.literal("github"),
|
|
1576
|
+
scope: z11.enum(PART_CODE_LINK_SCOPES),
|
|
1577
|
+
url: z11.string().startsWith(PART_CODE_LINK_URL_PREFIX, { message: "must start with https://github.com/" })
|
|
1578
|
+
}).strict();
|
|
1579
|
+
var partCodeLinkRangeSchema = z11.object({
|
|
1580
|
+
kind: z11.literal("range"),
|
|
1581
|
+
why: z11.enum(PART_CODE_LINK_RANGE_REASONS),
|
|
1582
|
+
from: closeCommitSchema,
|
|
1583
|
+
to: closeCommitSchema
|
|
1584
|
+
}).strict();
|
|
1585
|
+
var partCodeLinkNoneSchema = z11.object({
|
|
1586
|
+
kind: z11.literal("none")
|
|
1587
|
+
}).strict();
|
|
1588
|
+
var partCodeLinkSchema = z11.discriminatedUnion("kind", [
|
|
1589
|
+
partCodeLinkGithubSchema,
|
|
1590
|
+
partCodeLinkRangeSchema,
|
|
1591
|
+
partCodeLinkNoneSchema
|
|
1592
|
+
]);
|
|
1593
|
+
|
|
1594
|
+
// ../events/dist/observed-changes.js
|
|
1595
|
+
import { z as z12 } from "zod";
|
|
1596
|
+
var OBSERVED_PATHS_LISTED_MAX = 50;
|
|
1597
|
+
var OBSERVED_PATH_MAX = 300;
|
|
1598
|
+
var OBSERVED_COUNT_MAX = 1e6;
|
|
1599
|
+
var observedCountSchema = z12.number({ error: `must be a whole number from 0 to ${OBSERVED_COUNT_MAX}` }).int({ error: `must be a whole number from 0 to ${OBSERVED_COUNT_MAX}` }).min(0, { error: `must be a whole number from 0 to ${OBSERVED_COUNT_MAX}` }).max(OBSERVED_COUNT_MAX, { error: `must be a whole number from 0 to ${OBSERVED_COUNT_MAX}` });
|
|
1600
|
+
var observedDescriptionVersionSchema = z12.number().int().min(1);
|
|
1601
|
+
var observedPathSchema = z12.string().min(1, { message: "must not be empty" }).max(OBSERVED_PATH_MAX, { message: `must be at most ${OBSERVED_PATH_MAX} characters` });
|
|
1602
|
+
var observedPathListSchema = z12.array(observedPathSchema).max(OBSERVED_PATHS_LISTED_MAX, { message: `can list at most ${OBSERVED_PATHS_LISTED_MAX} paths` }).refine((paths) => new Set(paths).size === paths.length, { message: "must name each path once" });
|
|
1603
|
+
var observedMethodFilesSchema = z12.object({
|
|
1604
|
+
added: observedCountSchema,
|
|
1605
|
+
changed: observedCountSchema,
|
|
1606
|
+
removed: observedCountSchema
|
|
1607
|
+
}).strict();
|
|
1608
|
+
var observedFilesSchema = z12.object({
|
|
1609
|
+
added: observedCountSchema,
|
|
1610
|
+
changed: observedCountSchema,
|
|
1611
|
+
removed: observedCountSchema,
|
|
1612
|
+
testFilesAdded: observedCountSchema
|
|
1613
|
+
}).strict().refine((v) => v.testFilesAdded <= v.added, {
|
|
1614
|
+
message: "cannot be more than the files added",
|
|
1615
|
+
path: ["testFilesAdded"]
|
|
1616
|
+
});
|
|
1617
|
+
function filesChanged(files) {
|
|
1618
|
+
return files.added + files.changed + files.removed;
|
|
1619
|
+
}
|
|
1620
|
+
function pathsFitTheCount(files, paths) {
|
|
1621
|
+
return files === null ? paths.length === 0 : paths.length <= filesChanged(files);
|
|
1622
|
+
}
|
|
1623
|
+
function checkPartsUnique(parts, ctx, at) {
|
|
1624
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1625
|
+
parts.forEach((entry, i) => {
|
|
1626
|
+
if (seen.has(entry.part)) {
|
|
1627
|
+
ctx.addIssue({ code: "custom", path: [...at, i, "part"], message: `names the part "${entry.part}" twice` });
|
|
1628
|
+
}
|
|
1629
|
+
seen.add(entry.part);
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
var observedBasisCountSchema = z12.object({
|
|
1633
|
+
outcome: z12.literal("count"),
|
|
1634
|
+
phase: phaseNameSchema,
|
|
1635
|
+
startCommit: closeCommitSchema,
|
|
1636
|
+
descriptionVersion: observedDescriptionVersionSchema,
|
|
1637
|
+
description: systemDescriptionSchema
|
|
1638
|
+
}).strict();
|
|
1639
|
+
var observedBasisCannotCountSchema = z12.object({
|
|
1640
|
+
outcome: z12.literal("cannot-count"),
|
|
1641
|
+
phase: phaseNameSchema,
|
|
1642
|
+
reason: z12.literal("no-start-commit")
|
|
1643
|
+
}).strict();
|
|
1644
|
+
var observedBasisNothingToCountSchema = z12.object({
|
|
1645
|
+
outcome: z12.literal("nothing-to-count"),
|
|
1646
|
+
reason: z12.enum(["no-open-phase", "no-description"])
|
|
1647
|
+
}).strict();
|
|
1648
|
+
var observedBasisSchema = z12.discriminatedUnion("outcome", [
|
|
1649
|
+
observedBasisCountSchema,
|
|
1650
|
+
observedBasisCannotCountSchema,
|
|
1651
|
+
observedBasisNothingToCountSchema
|
|
1652
|
+
]);
|
|
1653
|
+
var observedReportPartSchema = z12.object({
|
|
1654
|
+
part: systemPartIdSchema,
|
|
1655
|
+
added: observedCountSchema,
|
|
1656
|
+
changed: observedCountSchema,
|
|
1657
|
+
removed: observedCountSchema,
|
|
1658
|
+
testFilesAdded: observedCountSchema
|
|
1659
|
+
}).strict().refine((v) => v.testFilesAdded <= v.added, {
|
|
1660
|
+
message: "cannot be more than the files added",
|
|
1661
|
+
path: ["testFilesAdded"]
|
|
1662
|
+
}).refine((v) => filesChanged(v) >= 1, {
|
|
1663
|
+
message: "a part is listed only when at least one file changed under it",
|
|
1664
|
+
path: ["added"]
|
|
1665
|
+
});
|
|
1666
|
+
var observedReportOutsideSchema = z12.object({
|
|
1667
|
+
added: observedCountSchema,
|
|
1668
|
+
changed: observedCountSchema,
|
|
1669
|
+
removed: observedCountSchema,
|
|
1670
|
+
testFilesAdded: observedCountSchema,
|
|
1671
|
+
paths: observedPathListSchema
|
|
1672
|
+
}).strict().refine((v) => v.testFilesAdded <= v.added, {
|
|
1673
|
+
message: "cannot be more than the files added",
|
|
1674
|
+
path: ["testFilesAdded"]
|
|
1675
|
+
}).refine((v) => pathsFitTheCount(v, v.paths), {
|
|
1676
|
+
message: "lists more paths than files changed",
|
|
1677
|
+
path: ["paths"]
|
|
1678
|
+
});
|
|
1679
|
+
var observedChangesReportSchema = z12.object({
|
|
1680
|
+
phase: phaseNameSchema,
|
|
1681
|
+
startCommit: closeCommitSchema,
|
|
1682
|
+
headCommit: closeCommitSchema,
|
|
1683
|
+
headCommittedAt: utcIso8601,
|
|
1684
|
+
commits: observedCountSchema,
|
|
1685
|
+
descriptionVersion: observedDescriptionVersionSchema,
|
|
1686
|
+
parts: z12.array(observedReportPartSchema).max(SYSTEM_PARTS_MAX, { message: `can list at most ${SYSTEM_PARTS_MAX} parts` }),
|
|
1687
|
+
methodFiles: observedMethodFilesSchema,
|
|
1688
|
+
outsideEveryPart: observedReportOutsideSchema,
|
|
1689
|
+
codeLink: codeLinkFactsSchema
|
|
1690
|
+
}).strict().superRefine((v, ctx) => checkPartsUnique(v.parts, ctx, ["parts"]));
|
|
1691
|
+
var observedSaidSchema = z12.object({
|
|
1692
|
+
title: buildTaskSchema.shape.title,
|
|
1693
|
+
agent: buildTaskSchema.shape.agent,
|
|
1694
|
+
whyOutsidePlan: buildTaskSchema.shape.whyOutsidePlan
|
|
1695
|
+
}).strict();
|
|
1696
|
+
var observedNotInPlanSchema = z12.object({
|
|
1697
|
+
part: systemPartIdSchema,
|
|
1698
|
+
name: systemPartSchema.shape.name,
|
|
1699
|
+
said: z12.array(observedSaidSchema).max(BUILD_SNAPSHOT_TASK_LIMIT)
|
|
1700
|
+
}).strict();
|
|
1701
|
+
var observedChangesRecordedSchema = z12.object({
|
|
1702
|
+
outcome: z12.literal("recorded"),
|
|
1703
|
+
notInPlan: z12.array(observedNotInPlanSchema).max(SYSTEM_PARTS_MAX).superRefine((parts, ctx) => checkPartsUnique(parts, ctx, [])),
|
|
1704
|
+
outside: z12.object({
|
|
1705
|
+
files: observedCountSchema,
|
|
1706
|
+
example: observedPathSchema.nullable()
|
|
1707
|
+
}).strict().refine((v) => v.example === null || v.files >= 1, {
|
|
1708
|
+
message: "names an example when no file changed",
|
|
1709
|
+
path: ["example"]
|
|
1710
|
+
})
|
|
1711
|
+
}).strict();
|
|
1712
|
+
var observedChangesKeptSchema = z12.object({
|
|
1713
|
+
outcome: z12.literal("kept")
|
|
1714
|
+
}).strict();
|
|
1715
|
+
var observedChangesReportResultSchema = z12.discriminatedUnion("outcome", [
|
|
1716
|
+
observedChangesRecordedSchema,
|
|
1717
|
+
observedChangesKeptSchema
|
|
1718
|
+
]);
|
|
1719
|
+
var OBSERVE_NOT_SENT_REASONS = [
|
|
1720
|
+
"not-configured",
|
|
1721
|
+
"nothing-to-count",
|
|
1722
|
+
"cannot-count",
|
|
1723
|
+
"start-not-here",
|
|
1724
|
+
"start-not-on-branch",
|
|
1725
|
+
"basis-moved",
|
|
1726
|
+
"refused",
|
|
1727
|
+
"unreachable"
|
|
1728
|
+
];
|
|
1729
|
+
var observeRunRecordedSchema = z12.object({ outcome: z12.literal("recorded") }).strict();
|
|
1730
|
+
var observeRunKeptSchema = z12.object({ outcome: z12.literal("kept") }).strict();
|
|
1731
|
+
var observeRunNotSentSchema = z12.object({
|
|
1732
|
+
outcome: z12.literal("not-sent"),
|
|
1733
|
+
reason: z12.enum(OBSERVE_NOT_SENT_REASONS)
|
|
1734
|
+
}).strict();
|
|
1735
|
+
var observeRunResultSchema = z12.discriminatedUnion("outcome", [
|
|
1736
|
+
observeRunRecordedSchema,
|
|
1737
|
+
observeRunKeptSchema,
|
|
1738
|
+
observeRunNotSentSchema
|
|
1739
|
+
]);
|
|
1740
|
+
var observedChecksFiredSchema = z12.array(z12.string().min(1)).refine((names) => names.every((name, i) => i === 0 || names[i - 1] < name), {
|
|
1741
|
+
message: "must be sorted, each name once"
|
|
1742
|
+
});
|
|
1743
|
+
var observedCountingSchema = z12.discriminatedUnion("state", [
|
|
1744
|
+
z12.object({
|
|
1745
|
+
state: z12.literal("counted"),
|
|
1746
|
+
startCommit: closeCommitSchema,
|
|
1747
|
+
startRecordedAt: utcIso8601,
|
|
1748
|
+
headCommit: closeCommitSchema,
|
|
1749
|
+
headCommittedAt: utcIso8601,
|
|
1750
|
+
commits: observedCountSchema,
|
|
1751
|
+
countedAt: utcIso8601
|
|
1752
|
+
}).strict(),
|
|
1753
|
+
z12.object({
|
|
1754
|
+
state: z12.literal("not-counted-yet"),
|
|
1755
|
+
startCommit: closeCommitSchema,
|
|
1756
|
+
startRecordedAt: utcIso8601
|
|
1757
|
+
}).strict(),
|
|
1758
|
+
z12.object({
|
|
1759
|
+
state: z12.literal("cannot-count")
|
|
1760
|
+
}).strict()
|
|
1761
|
+
]);
|
|
1762
|
+
var observedPartSchema = z12.object({
|
|
1763
|
+
part: systemPartIdSchema,
|
|
1764
|
+
files: observedFilesSchema.nullable(),
|
|
1765
|
+
checksFired: observedChecksFiredSchema
|
|
1766
|
+
}).strict().refine((v) => v.files !== null && filesChanged(v.files) >= 1 || v.checksFired.length >= 1, {
|
|
1767
|
+
message: "a part is listed only when files changed under it or a check fired on one",
|
|
1768
|
+
path: ["files"]
|
|
1769
|
+
});
|
|
1770
|
+
var observedOutsideSchema = z12.object({
|
|
1771
|
+
files: observedFilesSchema.nullable(),
|
|
1772
|
+
paths: observedPathListSchema,
|
|
1773
|
+
checksFired: observedChecksFiredSchema
|
|
1774
|
+
}).strict().refine((v) => pathsFitTheCount(v.files, v.paths), {
|
|
1775
|
+
message: "lists more paths than files changed",
|
|
1776
|
+
path: ["paths"]
|
|
1777
|
+
});
|
|
1778
|
+
var observedChangesSchema = z12.object({
|
|
1779
|
+
phase: phaseNameSchema,
|
|
1780
|
+
counting: observedCountingSchema,
|
|
1781
|
+
descriptionVersion: observedDescriptionVersionSchema.nullable(),
|
|
1782
|
+
parts: z12.array(observedPartSchema).max(SYSTEM_PARTS_MAX),
|
|
1783
|
+
methodFiles: observedMethodFilesSchema.nullable(),
|
|
1784
|
+
outsideEveryPart: observedOutsideSchema,
|
|
1785
|
+
codeLink: codeLinkFactsSchema.nullable()
|
|
1786
|
+
}).strict().superRefine((v, ctx) => {
|
|
1787
|
+
checkPartsUnique(v.parts, ctx, ["parts"]);
|
|
1788
|
+
const counted2 = v.counting.state === "counted";
|
|
1789
|
+
const rule = counted2 ? "must be present when the phase is counted" : "must be null until the phase is counted";
|
|
1790
|
+
if (v.methodFiles !== null !== counted2)
|
|
1791
|
+
ctx.addIssue({ code: "custom", path: ["methodFiles"], message: rule });
|
|
1792
|
+
if (v.outsideEveryPart.files !== null !== counted2) {
|
|
1793
|
+
ctx.addIssue({ code: "custom", path: ["outsideEveryPart", "files"], message: rule });
|
|
1794
|
+
}
|
|
1795
|
+
if (v.codeLink !== null !== counted2)
|
|
1796
|
+
ctx.addIssue({ code: "custom", path: ["codeLink"], message: rule });
|
|
1797
|
+
v.parts.forEach((entry, i) => {
|
|
1798
|
+
if (entry.files !== null !== counted2)
|
|
1799
|
+
ctx.addIssue({ code: "custom", path: ["parts", i, "files"], message: rule });
|
|
1800
|
+
});
|
|
1801
|
+
if (counted2 && v.descriptionVersion === null) {
|
|
1802
|
+
ctx.addIssue({ code: "custom", path: ["descriptionVersion"], message: "must name the version the counts used" });
|
|
1803
|
+
}
|
|
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)
|
|
1810
|
+
}).strict();
|
|
1811
|
+
function shortCommit(commit) {
|
|
1812
|
+
return commit.slice(0, 7);
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
// ../events/dist/phase-review.js
|
|
1816
|
+
import { z as z13 } from "zod";
|
|
1817
|
+
var BUILT_PART_ACTS = Object.freeze(["accept", "question", "send-back"]);
|
|
1818
|
+
var NOT_BUILT_PART_ACTS = Object.freeze(["carry", "drop"]);
|
|
1819
|
+
var REVIEW_ACTS = Object.freeze([...BUILT_PART_ACTS, ...NOT_BUILT_PART_ACTS]);
|
|
1820
|
+
var reviewActSchema = z13.enum(REVIEW_ACTS);
|
|
1821
|
+
function reviewActsFor(built) {
|
|
1822
|
+
return built ? BUILT_PART_ACTS : NOT_BUILT_PART_ACTS;
|
|
1823
|
+
}
|
|
1824
|
+
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);
|
|
1827
|
+
var REVIEW_ANSWER_MAX = 1e3;
|
|
1828
|
+
var reviewAgentAnswerSchema = z13.string().min(1).max(REVIEW_ANSWER_MAX);
|
|
1829
|
+
var REVIEW_PARTS_MAX = PHASE_PLAN_CHANGES_MAX * 2;
|
|
1830
|
+
var REVIEW_PART_CHANGES = Object.freeze([...PHASE_PLAN_CHANGE_KINDS, "not-in-plan"]);
|
|
1831
|
+
var reviewPartChangeSchema = z13.enum(REVIEW_PART_CHANGES);
|
|
1832
|
+
var REVIEW_DECISIONS = Object.freeze(["accepted", "carried", "dropped"]);
|
|
1833
|
+
var reviewDecisionSchema = z13.enum(REVIEW_DECISIONS);
|
|
1834
|
+
function actOf(decision) {
|
|
1835
|
+
if (decision === "accepted")
|
|
1836
|
+
return "accept";
|
|
1837
|
+
if (decision === "carried")
|
|
1838
|
+
return "carry";
|
|
1839
|
+
return "drop";
|
|
1840
|
+
}
|
|
1841
|
+
var reviewCarriedToSchema = carriedPhaseSchema.nullable();
|
|
1842
|
+
var reviewStepNameSchema = stepNameSchema.refine((v) => !stepIdSchema.safeParse(v.trim()).success, {
|
|
1843
|
+
message: "must be the step's plain name"
|
|
1844
|
+
});
|
|
1845
|
+
var reviewStepSchema = z13.object({
|
|
1846
|
+
name: reviewStepNameSchema.nullable(),
|
|
1847
|
+
state: unfinishedStepSchema.shape.state,
|
|
1848
|
+
judged: z13.boolean()
|
|
1849
|
+
}).strict();
|
|
1850
|
+
function codeLinkLine(link) {
|
|
1851
|
+
switch (link.kind) {
|
|
1852
|
+
case "github":
|
|
1853
|
+
return link.scope === "part" ? `The code: ${link.url}` : `The code: ${link.url} GitHub cannot show only this part's code, so this opens every change in this phase.`;
|
|
1854
|
+
case "range":
|
|
1855
|
+
return link.why === "not-github" ? `The code is on your machine, not on GitHub. This phase's changes are ${shortCommit(link.from)}..${shortCommit(link.to)}.` : `This phase's newest changes are not on GitHub yet. They are ${shortCommit(link.from)}..${shortCommit(link.to)} on your machine.`;
|
|
1856
|
+
case "none":
|
|
1857
|
+
return void 0;
|
|
1858
|
+
default:
|
|
1859
|
+
return assertNever(link);
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
function assertNever(value) {
|
|
1863
|
+
throw new Error(`unexpected value: ${JSON.stringify(value)}`);
|
|
1864
|
+
}
|
|
1865
|
+
var openReviewRequestSchema = z13.object({
|
|
1866
|
+
actor: closeActorSchema,
|
|
1867
|
+
acceptanceOutcome: acceptanceOutcomeSchema,
|
|
1868
|
+
statements: closeStatementsSchema
|
|
1869
|
+
}).strict();
|
|
1870
|
+
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()
|
|
1878
|
+
}).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)
|
|
1883
|
+
}).strict();
|
|
1884
|
+
var reviewOutstandingSchema = z13.object({
|
|
1885
|
+
outcome: z13.literal("outstanding"),
|
|
1886
|
+
steps: z13.array(reviewStepSchema).min(1)
|
|
1887
|
+
}).strict();
|
|
1888
|
+
var reviewCleanOverFindingsSchema = z13.object({
|
|
1889
|
+
outcome: z13.literal("clean-over-findings"),
|
|
1890
|
+
findings: z13.number().int().min(1)
|
|
1891
|
+
}).strict();
|
|
1892
|
+
var reviewRefusedSchema = z13.object({
|
|
1893
|
+
outcome: z13.literal("refused"),
|
|
1894
|
+
problems: z13.array(systemDescriptionProblemSchema).min(1)
|
|
1895
|
+
}).strict();
|
|
1896
|
+
function bareReviewOutcome(outcome) {
|
|
1897
|
+
return z13.object({ outcome: z13.literal(outcome) }).strict();
|
|
1898
|
+
}
|
|
1899
|
+
var openReviewResultSchema = z13.discriminatedUnion("outcome", [
|
|
1900
|
+
reviewOpenedSchema,
|
|
1901
|
+
reviewTasksOpenSchema,
|
|
1902
|
+
bareReviewOutcome("no-build"),
|
|
1903
|
+
bareReviewOutcome("review-open"),
|
|
1904
|
+
reviewOutstandingSchema,
|
|
1905
|
+
reviewCleanOverFindingsSchema,
|
|
1906
|
+
reviewRefusedSchema,
|
|
1907
|
+
bareReviewOutcome("unavailable")
|
|
1908
|
+
]);
|
|
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()
|
|
1914
|
+
}).strict();
|
|
1915
|
+
var openReviewAnswerSchema = z13.union([openReviewResultSchema, reviewPhaseNotOpenSchema]);
|
|
1916
|
+
var reviewDecisionEntrySchema = z13.object({
|
|
1917
|
+
part: systemPartIdSchema,
|
|
1918
|
+
act: reviewActSchema,
|
|
1919
|
+
note: reviewNoteWireSchema.optional()
|
|
1920
|
+
}).strict();
|
|
1921
|
+
var reviewDecisionRequestSchema = z13.object({
|
|
1922
|
+
reviewVersion: z13.number().int().min(0),
|
|
1923
|
+
decisions: z13.array(reviewDecisionEntrySchema).max(REVIEW_PARTS_MAX)
|
|
1924
|
+
}).strict();
|
|
1925
|
+
var reviewDecidedSchema = z13.object({
|
|
1926
|
+
part: systemPartIdSchema,
|
|
1927
|
+
name: systemPartSchema.shape.name,
|
|
1928
|
+
act: reviewActSchema,
|
|
1929
|
+
carriedTo: reviewCarriedToSchema.optional()
|
|
1930
|
+
}).strict().refine((v) => v.carriedTo !== void 0 === (v.act === "carry"), {
|
|
1931
|
+
message: "is present exactly on a carry",
|
|
1932
|
+
path: ["carriedTo"]
|
|
1933
|
+
});
|
|
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)
|
|
1940
|
+
}).strict().superRefine((v, ctx) => {
|
|
1941
|
+
if (v.closed && v.waiting !== 0) {
|
|
1942
|
+
ctx.addIssue({ code: "custom", path: ["waiting"], message: "must be 0 once the phase is closed" });
|
|
1943
|
+
}
|
|
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
|
+
});
|
|
1948
|
+
var REVIEW_NOT_DECIDABLE_WHY = Object.freeze(["settled", "question-waiting", "sent-back"]);
|
|
1949
|
+
var reviewNotDecidablePartSchema = z13.object({
|
|
1950
|
+
part: systemPartIdSchema,
|
|
1951
|
+
name: systemPartSchema.shape.name,
|
|
1952
|
+
why: z13.enum(REVIEW_NOT_DECIDABLE_WHY)
|
|
1953
|
+
}).strict();
|
|
1954
|
+
var reviewNotDecidableSchema = z13.object({
|
|
1955
|
+
outcome: z13.literal("not-decidable"),
|
|
1956
|
+
parts: z13.array(reviewNotDecidablePartSchema).min(1).max(REVIEW_PARTS_MAX)
|
|
1957
|
+
}).strict();
|
|
1958
|
+
var reviewCloseRefusedSchema = z13.object({
|
|
1959
|
+
outcome: z13.literal("close-refused"),
|
|
1960
|
+
steps: z13.array(reviewStepSchema)
|
|
1961
|
+
}).strict();
|
|
1962
|
+
var reviewWalkChangedSchema = z13.object({
|
|
1963
|
+
outcome: z13.literal("walk-changed"),
|
|
1964
|
+
findings: z13.number().int().min(1)
|
|
1965
|
+
}).strict();
|
|
1966
|
+
var reviewDecisionResultSchema = z13.discriminatedUnion("outcome", [
|
|
1967
|
+
reviewRecordedSchema,
|
|
1968
|
+
bareReviewOutcome("review-changed"),
|
|
1969
|
+
bareReviewOutcome("no-review"),
|
|
1970
|
+
reviewNotDecidableSchema,
|
|
1971
|
+
reviewCloseRefusedSchema,
|
|
1972
|
+
reviewWalkChangedSchema,
|
|
1973
|
+
bareReviewOutcome("not-owner"),
|
|
1974
|
+
bareReviewOutcome("no-owner"),
|
|
1975
|
+
reviewRefusedSchema,
|
|
1976
|
+
bareReviewOutcome("unavailable")
|
|
1977
|
+
]);
|
|
1978
|
+
var reviewSentBackSchema = z13.object({
|
|
1979
|
+
round: z13.number().int().min(1),
|
|
1980
|
+
note: reviewNoteSchema
|
|
1981
|
+
}).strict();
|
|
1982
|
+
var reviewQuestionSchema = z13.object({
|
|
1983
|
+
question: reviewNoteSchema,
|
|
1984
|
+
agentAnswer: reviewAgentAnswerSchema.optional()
|
|
1985
|
+
}).strict();
|
|
1986
|
+
var reviewTaskSchema = z13.object({
|
|
1987
|
+
title: buildTaskSchema.shape.title,
|
|
1988
|
+
state: buildTaskStateSchema
|
|
1989
|
+
}).strict();
|
|
1990
|
+
var reviewViewPartSchema = z13.object({
|
|
1991
|
+
part: systemPartIdSchema,
|
|
1992
|
+
name: systemPartSchema.shape.name,
|
|
1993
|
+
change: reviewPartChangeSchema,
|
|
1994
|
+
built: z13.boolean(),
|
|
1995
|
+
builtWithoutApproval: z13.boolean(),
|
|
1996
|
+
decidable: z13.boolean(),
|
|
1997
|
+
waiting: z13.boolean(),
|
|
1998
|
+
decision: reviewDecisionSchema.optional(),
|
|
1999
|
+
reason: reviewNoteSchema.optional(),
|
|
2000
|
+
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),
|
|
2005
|
+
codeLink: partCodeLinkSchema
|
|
2006
|
+
}).strict().superRefine((v, ctx) => {
|
|
2007
|
+
if (v.waiting !== (v.decision === void 0)) {
|
|
2008
|
+
ctx.addIssue({ code: "custom", path: ["waiting"], message: "must be true exactly when the part has no decision" });
|
|
2009
|
+
}
|
|
2010
|
+
if (v.decidable && !v.waiting) {
|
|
2011
|
+
ctx.addIssue({ code: "custom", path: ["decidable"], message: "cannot be true for a settled part" });
|
|
2012
|
+
}
|
|
2013
|
+
if (v.decision !== void 0 && !reviewActsFor(v.built).includes(actOf(v.decision))) {
|
|
2014
|
+
ctx.addIssue({ code: "custom", path: ["decision"], message: "is not an act this part takes" });
|
|
2015
|
+
}
|
|
2016
|
+
const carriedOrDropped = v.decision === "carried" || v.decision === "dropped";
|
|
2017
|
+
if (v.reason !== void 0 !== carriedOrDropped) {
|
|
2018
|
+
ctx.addIssue({ code: "custom", path: ["reason"], message: "is present exactly on a carry or a drop" });
|
|
2019
|
+
}
|
|
2020
|
+
if (v.carriedTo !== void 0 !== (v.decision === "carried")) {
|
|
2021
|
+
ctx.addIssue({ code: "custom", path: ["carriedTo"], message: "is present exactly on a carry" });
|
|
2022
|
+
}
|
|
2023
|
+
if (v.change === "not-in-plan" && !v.built) {
|
|
2024
|
+
ctx.addIssue({ code: "custom", path: ["built"], message: "is always true for a part outside the plan" });
|
|
2025
|
+
}
|
|
2026
|
+
});
|
|
2027
|
+
var reviewPhaseNumberSchema = z13.number().int().min(1).nullable();
|
|
2028
|
+
var phaseReviewNoneSchema = z13.object({
|
|
2029
|
+
state: z13.literal("none"),
|
|
2030
|
+
phase: scopePhaseSchema,
|
|
2031
|
+
number: reviewPhaseNumberSchema,
|
|
2032
|
+
closedWithoutReview: z13.boolean()
|
|
2033
|
+
}).strict();
|
|
2034
|
+
var reviewWalkSchema = z13.object({
|
|
2035
|
+
verdict: walkVerdictSchema,
|
|
2036
|
+
findings: z13.number().int().min(0)
|
|
2037
|
+
}).strict();
|
|
2038
|
+
function reviewedViewSchema(state) {
|
|
2039
|
+
return z13.object({
|
|
2040
|
+
state: z13.literal(state),
|
|
2041
|
+
phase: scopePhaseSchema,
|
|
2042
|
+
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(),
|
|
2048
|
+
walk: reviewWalkSchema,
|
|
2049
|
+
parts: z13.array(reviewViewPartSchema).max(REVIEW_PARTS_MAX),
|
|
2050
|
+
observed: observedChangesSchema.nullable(),
|
|
2051
|
+
description: systemDescriptionSchema
|
|
2052
|
+
}).strict().superRefine((v, ctx) => {
|
|
2053
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2054
|
+
v.parts.forEach((p, i) => {
|
|
2055
|
+
if (seen.has(p.part))
|
|
2056
|
+
ctx.addIssue({ code: "custom", path: ["parts", i, "part"], message: "is listed twice" });
|
|
2057
|
+
seen.add(p.part);
|
|
2058
|
+
});
|
|
2059
|
+
const counted2 = v.observed !== null && v.observed.counting.state === "counted";
|
|
2060
|
+
if (v.counted !== counted2) {
|
|
2061
|
+
ctx.addIssue({ code: "custom", path: ["counted"], message: "must be true exactly when the counts are counted" });
|
|
2062
|
+
}
|
|
2063
|
+
if (v.observed !== null && v.observed.phase !== v.phase) {
|
|
2064
|
+
ctx.addIssue({ code: "custom", path: ["observed", "phase"], message: "must be the phase under review" });
|
|
2065
|
+
}
|
|
2066
|
+
if (v.state === "closed") {
|
|
2067
|
+
v.parts.forEach((p, i) => {
|
|
2068
|
+
if (p.decision === void 0) {
|
|
2069
|
+
ctx.addIssue({ code: "custom", path: ["parts", i, "decision"], message: "every part of a closed review is decided" });
|
|
2070
|
+
}
|
|
2071
|
+
});
|
|
2072
|
+
}
|
|
2073
|
+
});
|
|
2074
|
+
}
|
|
2075
|
+
var phaseReviewOpenSchema = reviewedViewSchema("open");
|
|
2076
|
+
var phaseReviewClosedSchema = reviewedViewSchema("closed");
|
|
2077
|
+
var phaseReviewViewSchema = z13.discriminatedUnion("state", [
|
|
2078
|
+
phaseReviewNoneSchema,
|
|
2079
|
+
phaseReviewOpenSchema,
|
|
2080
|
+
phaseReviewClosedSchema
|
|
2081
|
+
]);
|
|
2082
|
+
|
|
2083
|
+
// ../events/dist/build-record-v3.js
|
|
2084
|
+
var BUILD_RECORD_V3_FORMAT = "halfcycle-build-record/v3";
|
|
2085
|
+
var recordReviewRoundSchema = z14.object({
|
|
2086
|
+
round: z14.number().int().min(1),
|
|
2087
|
+
openedAt: utcIso8601,
|
|
2088
|
+
verdict: walkVerdictSchema,
|
|
2089
|
+
findings: z14.number().int().min(0)
|
|
2090
|
+
}).strict();
|
|
2091
|
+
var recordReviewApprovalSchema = z14.discriminatedUnion("kind", [
|
|
2092
|
+
z14.object({
|
|
2093
|
+
kind: z14.literal("approved"),
|
|
2094
|
+
planVersion: planVersionSchema,
|
|
2095
|
+
givenIn: scopeGivenInSchema,
|
|
2096
|
+
approvedAt: utcIso8601
|
|
2097
|
+
}).strict(),
|
|
2098
|
+
z14.object({ kind: z14.literal("none") }).strict()
|
|
2099
|
+
]);
|
|
2100
|
+
var recordReviewGateSchema = z14.object({
|
|
2101
|
+
gate: boardGateNameSchema,
|
|
2102
|
+
state: boardStepStateSchema
|
|
2103
|
+
}).strict();
|
|
2104
|
+
var recordReviewQuestionSchema = z14.object({
|
|
2105
|
+
question: reviewNoteSchema,
|
|
2106
|
+
agentAnswer: reviewAgentAnswerSchema
|
|
2107
|
+
}).strict();
|
|
2108
|
+
var recordReviewPartSchema = z14.object({
|
|
2109
|
+
part: systemPartIdSchema,
|
|
2110
|
+
name: systemPartSchema.shape.name,
|
|
2111
|
+
change: reviewPartChangeSchema,
|
|
2112
|
+
built: z14.boolean(),
|
|
2113
|
+
builtWithoutApproval: z14.boolean(),
|
|
2114
|
+
decision: reviewDecisionSchema,
|
|
2115
|
+
reason: reviewNoteSchema.optional(),
|
|
2116
|
+
carriedTo: reviewCarriedToSchema.optional(),
|
|
2117
|
+
sentBack: z14.array(reviewSentBackSchema),
|
|
2118
|
+
questions: z14.array(recordReviewQuestionSchema),
|
|
2119
|
+
codeLink: partCodeLinkSchema
|
|
2120
|
+
}).strict().superRefine((v, ctx) => {
|
|
2121
|
+
const carriedOrDropped = v.decision === "carried" || v.decision === "dropped";
|
|
2122
|
+
if (v.reason !== void 0 !== carriedOrDropped) {
|
|
2123
|
+
ctx.addIssue({ code: "custom", path: ["reason"], message: "is present exactly on a carry or a drop" });
|
|
2124
|
+
}
|
|
2125
|
+
if (v.carriedTo !== void 0 !== (v.decision === "carried")) {
|
|
2126
|
+
ctx.addIssue({ code: "custom", path: ["carriedTo"], message: "is present exactly on a carry" });
|
|
2127
|
+
}
|
|
2128
|
+
});
|
|
2129
|
+
var SPINE_ORDER = boardGateNameSchema.options;
|
|
2130
|
+
var recordReviewGatesSchema = z14.array(recordReviewGateSchema).superRefine((gates, ctx) => {
|
|
2131
|
+
const inOrder = gates.length === SPINE_ORDER.length && gates.every((g, i) => g.gate === SPINE_ORDER[i]);
|
|
2132
|
+
if (!inOrder) {
|
|
2133
|
+
ctx.addIssue({ code: "custom", message: `must be exactly the five checkpoints in order: ${SPINE_ORDER.join(", ")}` });
|
|
2134
|
+
}
|
|
2135
|
+
});
|
|
2136
|
+
var recordReviewSchema = z14.object({
|
|
2137
|
+
sequence: z14.number().int().min(1),
|
|
2138
|
+
cycle: z14.number().int().min(0),
|
|
2139
|
+
rounds: z14.array(recordReviewRoundSchema).min(1),
|
|
2140
|
+
approval: recordReviewApprovalSchema,
|
|
2141
|
+
planVersion: planVersionSchema.nullable(),
|
|
2142
|
+
descriptionVersion: observedDescriptionVersionSchema,
|
|
2143
|
+
gates: recordReviewGatesSchema,
|
|
2144
|
+
observed: observedChangesSchema.nullable(),
|
|
2145
|
+
parts: z14.array(recordReviewPartSchema).max(REVIEW_PARTS_MAX)
|
|
2146
|
+
}).strict().superRefine((v, ctx) => {
|
|
2147
|
+
v.rounds.forEach((r, i) => {
|
|
2148
|
+
if (r.round !== i + 1) {
|
|
2149
|
+
ctx.addIssue({ code: "custom", path: ["rounds", i, "round"], message: "rounds must be numbered 1, 2, 3 \u2026 in order with no gap" });
|
|
2150
|
+
}
|
|
2151
|
+
});
|
|
2152
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2153
|
+
v.parts.forEach((p, i) => {
|
|
2154
|
+
if (seen.has(p.part))
|
|
2155
|
+
ctx.addIssue({ code: "custom", path: ["parts", i, "part"], message: "is listed twice" });
|
|
2156
|
+
seen.add(p.part);
|
|
2157
|
+
if (v.observed === null && p.codeLink.kind !== "none") {
|
|
2158
|
+
ctx.addIssue({ code: "custom", path: ["parts", i, "codeLink"], message: "must be none when nothing was observed" });
|
|
2159
|
+
}
|
|
2160
|
+
});
|
|
2161
|
+
});
|
|
2162
|
+
var buildRecordV3Schema = z14.object({
|
|
2163
|
+
...buildRecordV2Schema.shape,
|
|
2164
|
+
format: z14.literal(BUILD_RECORD_V3_FORMAT),
|
|
2165
|
+
review: recordReviewSchema
|
|
2166
|
+
}).strict().superRefine((record2, ctx) => {
|
|
2167
|
+
checkRecordHalves(record2, ctx);
|
|
2168
|
+
const closes = record2.observed.closes;
|
|
2169
|
+
const newest = closes[closes.length - 1].sequence;
|
|
2170
|
+
if (record2.review.sequence !== newest) {
|
|
2171
|
+
ctx.addIssue({ code: "custom", path: ["review", "sequence"], message: "must be the newest close" });
|
|
2172
|
+
}
|
|
2173
|
+
const at = record2.attested.closes.findIndex((c) => c.sequence === record2.review.sequence);
|
|
2174
|
+
const close = at === -1 ? void 0 : record2.attested.closes[at];
|
|
2175
|
+
if (close !== void 0 && close.decision !== "done-when-met") {
|
|
2176
|
+
ctx.addIssue({ code: "custom", path: ["attested", "closes", at, "decision"], message: "a close made by a review is done-when-met" });
|
|
2177
|
+
}
|
|
2178
|
+
const last = record2.review.rounds[record2.review.rounds.length - 1];
|
|
2179
|
+
if (close !== void 0 && last !== void 0) {
|
|
2180
|
+
const attested = close.walk === null ? null : close.walk.findings.length + close.walk.findingsNotShown;
|
|
2181
|
+
if (attested !== last.findings) {
|
|
2182
|
+
ctx.addIssue({
|
|
2183
|
+
code: "custom",
|
|
2184
|
+
path: ["review", "rounds", record2.review.rounds.length - 1, "findings"],
|
|
2185
|
+
message: "must equal the findings of the closing walk"
|
|
2186
|
+
});
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
if (record2.review.observed !== null && record2.review.observed.phase !== record2.phase) {
|
|
2190
|
+
ctx.addIssue({ code: "custom", path: ["review", "observed", "phase"], message: "must be this record's phase" });
|
|
2191
|
+
}
|
|
2192
|
+
});
|
|
2193
|
+
var buildRecordSchema = z14.discriminatedUnion("format", [buildRecordV2Schema, buildRecordV3Schema]);
|
|
2194
|
+
function removalsOf(record2) {
|
|
2195
|
+
if (record2.format !== BUILD_RECORD_V3_FORMAT)
|
|
2196
|
+
return [];
|
|
2197
|
+
const out = [];
|
|
2198
|
+
for (const p of record2.review.parts) {
|
|
2199
|
+
if (p.change === "add" && p.decision === "dropped")
|
|
2200
|
+
out.push({ part: p.part, name: p.name, act: "dropped" });
|
|
2201
|
+
else if (p.change === "remove" && p.decision === "accepted")
|
|
2202
|
+
out.push({ part: p.part, name: p.name, act: "removed" });
|
|
2203
|
+
}
|
|
2204
|
+
return out;
|
|
2205
|
+
}
|
|
933
2206
|
|
|
934
2207
|
// ../events/dist/device-auth.js
|
|
935
|
-
import { z as
|
|
936
|
-
var utcIso86012 =
|
|
2208
|
+
import { z as z15 } from "zod";
|
|
2209
|
+
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");
|
|
937
2210
|
var DEVICE_AUTH_STATUS = {
|
|
938
2211
|
/** The poll: minted, nobody has acted yet. Keep polling. 200. */
|
|
939
2212
|
PENDING: "pending",
|
|
@@ -974,25 +2247,25 @@ var DEVICE_POLL_REFUSALS = [
|
|
|
974
2247
|
DEVICE_AUTH_STATUS.EXPIRED,
|
|
975
2248
|
DEVICE_AUTH_STATUS.UNKNOWN
|
|
976
2249
|
];
|
|
977
|
-
var deviceAuthStartResponseSchema =
|
|
978
|
-
deviceCode:
|
|
979
|
-
userCode:
|
|
980
|
-
verificationUrl:
|
|
2250
|
+
var deviceAuthStartResponseSchema = z15.object({
|
|
2251
|
+
deviceCode: z15.string().min(1),
|
|
2252
|
+
userCode: z15.string().min(1),
|
|
2253
|
+
verificationUrl: z15.string().min(1),
|
|
981
2254
|
expiresAt: utcIso86012,
|
|
982
|
-
pollIntervalMs:
|
|
2255
|
+
pollIntervalMs: z15.number().int().positive()
|
|
983
2256
|
}).strict();
|
|
984
|
-
var devicePollPendingSchema =
|
|
985
|
-
status:
|
|
986
|
-
pollIntervalMs:
|
|
2257
|
+
var devicePollPendingSchema = z15.object({
|
|
2258
|
+
status: z15.literal(DEVICE_AUTH_STATUS.PENDING),
|
|
2259
|
+
pollIntervalMs: z15.number().int().positive()
|
|
987
2260
|
}).strict();
|
|
988
|
-
var devicePollApprovedSchema =
|
|
989
|
-
status:
|
|
990
|
-
accountId:
|
|
991
|
-
credential:
|
|
2261
|
+
var devicePollApprovedSchema = z15.object({
|
|
2262
|
+
status: z15.literal(DEVICE_AUTH_STATUS.APPROVED),
|
|
2263
|
+
accountId: z15.string().min(1),
|
|
2264
|
+
credential: z15.string().min(1),
|
|
992
2265
|
expiresAt: utcIso86012
|
|
993
2266
|
}).strict();
|
|
994
|
-
var deviceAuthRefusalSchema =
|
|
995
|
-
status:
|
|
2267
|
+
var deviceAuthRefusalSchema = z15.object({
|
|
2268
|
+
status: z15.enum([
|
|
996
2269
|
DEVICE_AUTH_STATUS.DECLINED,
|
|
997
2270
|
DEVICE_AUTH_STATUS.EXPIRED,
|
|
998
2271
|
DEVICE_AUTH_STATUS.UNKNOWN,
|
|
@@ -1003,25 +2276,25 @@ var deviceAuthRefusalSchema = z6.object({
|
|
|
1003
2276
|
DEVICE_AUTH_STATUS.ALREADY_DECIDED,
|
|
1004
2277
|
DEVICE_AUTH_STATUS.TERMS_REQUIRED
|
|
1005
2278
|
]),
|
|
1006
|
-
message:
|
|
2279
|
+
message: z15.string().min(1)
|
|
1007
2280
|
}).strict();
|
|
1008
|
-
var devicePollResponseSchema =
|
|
2281
|
+
var devicePollResponseSchema = z15.union([
|
|
1009
2282
|
devicePollPendingSchema,
|
|
1010
2283
|
devicePollApprovedSchema,
|
|
1011
2284
|
deviceAuthRefusalSchema
|
|
1012
2285
|
]);
|
|
1013
|
-
var deviceConfirmRequestSchema =
|
|
1014
|
-
userCode:
|
|
1015
|
-
decision:
|
|
1016
|
-
externalAuthId:
|
|
1017
|
-
email:
|
|
1018
|
-
acceptTerms:
|
|
2286
|
+
var deviceConfirmRequestSchema = z15.object({
|
|
2287
|
+
userCode: z15.string().min(1),
|
|
2288
|
+
decision: z15.enum(["approve", "decline"]),
|
|
2289
|
+
externalAuthId: z15.string().min(1).optional(),
|
|
2290
|
+
email: z15.string().optional(),
|
|
2291
|
+
acceptTerms: z15.boolean().optional()
|
|
1019
2292
|
}).strict();
|
|
1020
|
-
var deviceConfirmRecordedSchema =
|
|
1021
|
-
status:
|
|
1022
|
-
accountId:
|
|
2293
|
+
var deviceConfirmRecordedSchema = z15.object({
|
|
2294
|
+
status: z15.literal(DEVICE_AUTH_STATUS.RECORDED),
|
|
2295
|
+
accountId: z15.string().min(1).optional()
|
|
1023
2296
|
}).strict();
|
|
1024
|
-
var deviceConfirmResponseSchema =
|
|
2297
|
+
var deviceConfirmResponseSchema = z15.union([
|
|
1025
2298
|
deviceConfirmRecordedSchema,
|
|
1026
2299
|
deviceAuthRefusalSchema
|
|
1027
2300
|
]);
|
|
@@ -1033,20 +2306,20 @@ function projectPageUrl(engagementId) {
|
|
|
1033
2306
|
}
|
|
1034
2307
|
|
|
1035
2308
|
// ../events/dist/credential-wire.js
|
|
1036
|
-
import { z as
|
|
1037
|
-
var engagementCredentialResponseSchema =
|
|
1038
|
-
engagementId:
|
|
1039
|
-
sessionToken:
|
|
1040
|
-
mcpUrl:
|
|
1041
|
-
guardUrl:
|
|
1042
|
-
controlTelemetryUrl:
|
|
1043
|
-
methodVersion:
|
|
1044
|
-
consoleUrl:
|
|
2309
|
+
import { z as z16 } from "zod";
|
|
2310
|
+
var engagementCredentialResponseSchema = z16.object({
|
|
2311
|
+
engagementId: z16.string(),
|
|
2312
|
+
sessionToken: z16.string(),
|
|
2313
|
+
mcpUrl: z16.string(),
|
|
2314
|
+
guardUrl: z16.string(),
|
|
2315
|
+
controlTelemetryUrl: z16.string(),
|
|
2316
|
+
methodVersion: z16.string().optional(),
|
|
2317
|
+
consoleUrl: z16.string().optional()
|
|
1045
2318
|
}).strict();
|
|
1046
|
-
var ciTokenExchangeResponseSchema =
|
|
1047
|
-
token:
|
|
2319
|
+
var ciTokenExchangeResponseSchema = z16.object({
|
|
2320
|
+
token: z16.string(),
|
|
1048
2321
|
expiresAt: utcIso8601,
|
|
1049
|
-
engagementId:
|
|
2322
|
+
engagementId: z16.string()
|
|
1050
2323
|
}).strict();
|
|
1051
2324
|
var NO_CREDENTIAL_PLACEHOLDER = "halfcycle:no-credential";
|
|
1052
2325
|
var NOT_THIS_ACCOUNT_PLACEHOLDER = "halfcycle:not-this-account";
|
|
@@ -1063,37 +2336,37 @@ var PLACEHOLDER_SENTENCES = Object.freeze({
|
|
|
1063
2336
|
});
|
|
1064
2337
|
|
|
1065
2338
|
// ../events/dist/phase-record.js
|
|
1066
|
-
import { z as
|
|
1067
|
-
var phaseRecordRefusalReasonSchema =
|
|
2339
|
+
import { z as z17 } from "zod";
|
|
2340
|
+
var phaseRecordRefusalReasonSchema = z17.enum([
|
|
1068
2341
|
"still-open",
|
|
1069
2342
|
"reopened",
|
|
1070
2343
|
"never-opened",
|
|
1071
2344
|
"never-closed",
|
|
1072
2345
|
"closed-before-records"
|
|
1073
2346
|
]);
|
|
1074
|
-
var phaseRecordRecordSchema =
|
|
1075
|
-
outcome:
|
|
2347
|
+
var phaseRecordRecordSchema = z17.object({
|
|
2348
|
+
outcome: z17.literal("record"),
|
|
1076
2349
|
phase: phaseNameSchema,
|
|
1077
|
-
record:
|
|
2350
|
+
record: buildRecordSchema
|
|
1078
2351
|
}).strict().refine((body) => body.record.phase === body.phase, {
|
|
1079
2352
|
message: "the record is for a different phase than the one asked for",
|
|
1080
2353
|
path: ["record", "phase"]
|
|
1081
2354
|
});
|
|
1082
|
-
var phaseRecordWithheldSchema =
|
|
1083
|
-
outcome:
|
|
2355
|
+
var phaseRecordWithheldSchema = z17.object({
|
|
2356
|
+
outcome: z17.literal("withheld"),
|
|
1084
2357
|
phase: phaseNameSchema,
|
|
1085
|
-
sequence:
|
|
2358
|
+
sequence: z17.number().int().min(1),
|
|
1086
2359
|
closedAt: utcIso8601,
|
|
1087
|
-
message:
|
|
2360
|
+
message: z17.string()
|
|
1088
2361
|
}).strict();
|
|
1089
2362
|
var phaseRecordRefusedSchema = wireErrorSchema.extend({
|
|
1090
|
-
outcome:
|
|
2363
|
+
outcome: z17.literal("refused"),
|
|
1091
2364
|
phase: phaseNameSchema,
|
|
1092
|
-
statusCode:
|
|
1093
|
-
error:
|
|
2365
|
+
statusCode: z17.literal(409),
|
|
2366
|
+
error: z17.literal("PhaseRecordRefused"),
|
|
1094
2367
|
reason: phaseRecordRefusalReasonSchema
|
|
1095
2368
|
}).strict();
|
|
1096
|
-
var phaseRecordResponseSchema =
|
|
2369
|
+
var phaseRecordResponseSchema = z17.discriminatedUnion("outcome", [
|
|
1097
2370
|
phaseRecordRecordSchema,
|
|
1098
2371
|
phaseRecordWithheldSchema,
|
|
1099
2372
|
phaseRecordRefusedSchema
|
|
@@ -1147,6 +2420,7 @@ var ENGAGEMENT_ENV_KEYS = [
|
|
|
1147
2420
|
...GUARD_ENV_KEYS,
|
|
1148
2421
|
"CONTROL_TELEMETRY_URL"
|
|
1149
2422
|
];
|
|
2423
|
+
var PHASE_STAMP_ENV_KEY = "HALFCYCLE_PHASE";
|
|
1150
2424
|
function reconcileEnvText2(existing, values, keys = ENGAGEMENT_ENV_KEYS) {
|
|
1151
2425
|
return reconcileEnvText(existing, values, keys);
|
|
1152
2426
|
}
|
|
@@ -1247,7 +2521,7 @@ halfcycle_env_file() {
|
|
|
1247
2521
|
|
|
1248
2522
|
// dist/identity.js
|
|
1249
2523
|
import { execFileSync } from "node:child_process";
|
|
1250
|
-
import { existsSync, linkSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
2524
|
+
import { existsSync, linkSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1251
2525
|
import { dirname as dirname2, isAbsolute, join as join3 } from "node:path";
|
|
1252
2526
|
import { randomUUID } from "node:crypto";
|
|
1253
2527
|
var PROJECT_IDENTITY_NOTE = 'projectId is NOT the engagement id \u2014 it identifies this repository/worktree to Halfcycle, not an engagement. The engagement id lives in .halfcycle/bundle.json, in its "engagementId" field.';
|
|
@@ -1296,6 +2570,41 @@ function readRootCommit(targetRepoRoot) {
|
|
|
1296
2570
|
function readRemote(targetRepoRoot) {
|
|
1297
2571
|
return git(targetRepoRoot, ["remote", "get-url", "origin"]);
|
|
1298
2572
|
}
|
|
2573
|
+
function stripUrlUserinfoTextually(trimmed) {
|
|
2574
|
+
const schemeEnd = trimmed.indexOf("://") + 3;
|
|
2575
|
+
const pathStart = (() => {
|
|
2576
|
+
const match = /[/?#]/.exec(trimmed.slice(schemeEnd));
|
|
2577
|
+
return match ? schemeEnd + match.index : trimmed.length;
|
|
2578
|
+
})();
|
|
2579
|
+
const authority = trimmed.slice(schemeEnd, pathStart);
|
|
2580
|
+
const at = authority.lastIndexOf("@");
|
|
2581
|
+
if (at === -1)
|
|
2582
|
+
return trimmed;
|
|
2583
|
+
return trimmed.slice(0, schemeEnd) + authority.slice(at + 1) + trimmed.slice(pathStart);
|
|
2584
|
+
}
|
|
2585
|
+
function redactRemoteCredential(remote) {
|
|
2586
|
+
if (remote === null)
|
|
2587
|
+
return null;
|
|
2588
|
+
const trimmed = remote.trim();
|
|
2589
|
+
if (trimmed.includes("://")) {
|
|
2590
|
+
try {
|
|
2591
|
+
const url = new URL(trimmed);
|
|
2592
|
+
if (url.username === "" && url.password === "")
|
|
2593
|
+
return trimmed;
|
|
2594
|
+
url.username = "";
|
|
2595
|
+
url.password = "";
|
|
2596
|
+
return url.toString();
|
|
2597
|
+
} catch {
|
|
2598
|
+
return stripUrlUserinfoTextually(trimmed);
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
const at = trimmed.lastIndexOf("@");
|
|
2602
|
+
if (at === -1)
|
|
2603
|
+
return trimmed;
|
|
2604
|
+
const userinfo = trimmed.slice(0, at);
|
|
2605
|
+
const rest = trimmed.slice(at + 1);
|
|
2606
|
+
return userinfo.includes(":") && rest.includes(":") ? rest : trimmed;
|
|
2607
|
+
}
|
|
1299
2608
|
function readIgnoredPaths(targetRepoRoot, paths) {
|
|
1300
2609
|
if (paths.length === 0)
|
|
1301
2610
|
return null;
|
|
@@ -1331,11 +2640,12 @@ function readIgnoredPaths(targetRepoRoot, paths) {
|
|
|
1331
2640
|
function mintOrReadIdentity(targetRepoRoot) {
|
|
1332
2641
|
const path = join3(targetRepoRoot, ".halfcycle", "project.json");
|
|
1333
2642
|
if (existsSync(path)) {
|
|
2643
|
+
repairRemoteCredential(targetRepoRoot);
|
|
1334
2644
|
const onDisk = JSON.parse(readFileSync2(path, "utf-8"));
|
|
1335
2645
|
const identity = {
|
|
1336
2646
|
projectId: onDisk.projectId ?? "",
|
|
1337
2647
|
rootCommit: onDisk.rootCommit ?? null,
|
|
1338
|
-
remote: onDisk.remote ?? null,
|
|
2648
|
+
remote: redactRemoteCredential(onDisk.remote ?? null),
|
|
1339
2649
|
note: PROJECT_IDENTITY_NOTE
|
|
1340
2650
|
};
|
|
1341
2651
|
return { identity, minted: false };
|
|
@@ -1346,10 +2656,40 @@ function mintIdentity(targetRepoRoot) {
|
|
|
1346
2656
|
return {
|
|
1347
2657
|
projectId: randomUUID(),
|
|
1348
2658
|
rootCommit: readRootCommit(targetRepoRoot),
|
|
1349
|
-
remote: readRemote(targetRepoRoot),
|
|
2659
|
+
remote: redactRemoteCredential(readRemote(targetRepoRoot)),
|
|
1350
2660
|
note: PROJECT_IDENTITY_NOTE
|
|
1351
2661
|
};
|
|
1352
2662
|
}
|
|
2663
|
+
function repairRemoteCredential(targetRepoRoot) {
|
|
2664
|
+
const path = join3(targetRepoRoot, ".halfcycle", "project.json");
|
|
2665
|
+
let onDisk;
|
|
2666
|
+
try {
|
|
2667
|
+
const parsed = JSON.parse(readFileSync2(path, "utf-8"));
|
|
2668
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
|
|
2669
|
+
return;
|
|
2670
|
+
onDisk = parsed;
|
|
2671
|
+
} catch {
|
|
2672
|
+
return;
|
|
2673
|
+
}
|
|
2674
|
+
const storedRemote = onDisk.remote ?? null;
|
|
2675
|
+
const remote = redactRemoteCredential(storedRemote);
|
|
2676
|
+
if (remote === storedRemote)
|
|
2677
|
+
return;
|
|
2678
|
+
const identity = {
|
|
2679
|
+
projectId: onDisk.projectId ?? "",
|
|
2680
|
+
rootCommit: onDisk.rootCommit ?? null,
|
|
2681
|
+
remote,
|
|
2682
|
+
note: PROJECT_IDENTITY_NOTE
|
|
2683
|
+
};
|
|
2684
|
+
const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
2685
|
+
try {
|
|
2686
|
+
writeFileSync2(temporary, serializeProjectIdentity(identity), { encoding: "utf-8", flag: "wx" });
|
|
2687
|
+
renameSync(temporary, path);
|
|
2688
|
+
} catch {
|
|
2689
|
+
} finally {
|
|
2690
|
+
rmSync(temporary, { force: true });
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
1353
2693
|
function serializeProjectIdentity(identity) {
|
|
1354
2694
|
return JSON.stringify(identity, null, 2) + "\n";
|
|
1355
2695
|
}
|
|
@@ -2399,18 +3739,21 @@ exit 0
|
|
|
2399
3739
|
var GENERATED_USER_PROMPT_SCOPE_HEADER = "# Typed scope approval \u2014 generated by";
|
|
2400
3740
|
function generateUserPromptScopeHook() {
|
|
2401
3741
|
return `#!/usr/bin/env bash
|
|
2402
|
-
${GENERATED_USER_PROMPT_SCOPE_HEADER} the Halfcycle installer.
|
|
3742
|
+
${GENERATED_USER_PROMPT_SCOPE_HEADER} the Halfcycle installer, now also for the close review.
|
|
2403
3743
|
#
|
|
2404
|
-
# Lets the owner of this project approve a phase's scope, or send it back,
|
|
2405
|
-
#
|
|
2406
|
-
# their own Claude Code session
|
|
3744
|
+
# Lets the owner of this project approve a phase's scope, or send it back, and
|
|
3745
|
+
# decide one part of a phase's close review, by typing one line as the whole
|
|
3746
|
+
# prompt in their own Claude Code session: "approve <phase>", "send back
|
|
3747
|
+
# <phase>: <note>", "accept <phase>/<part>", "question <phase>/<part>: <words>",
|
|
3748
|
+
# "carry <phase>/<part>: <words>" or "drop <phase>/<part>: <words>".
|
|
2407
3749
|
#
|
|
2408
3750
|
# AN ORDINARY PROMPT STARTS NOTHING. This script reads the hook input once and
|
|
2409
3751
|
# looks only at the start of the prompt's value. Unless the prompt begins, after
|
|
2410
|
-
# any whitespace (typed, or escaped in the input), with "approve "
|
|
2411
|
-
# "
|
|
2412
|
-
# A quote inside any other field arrives escaped,
|
|
2413
|
-
# the session is in, for one) can read as the
|
|
3752
|
+
# any whitespace (typed, or escaped in the input), with "approve ", "send back ",
|
|
3753
|
+
# "accept ", "question ", "carry " or "drop ", in any case, it exits 0 with no
|
|
3754
|
+
# output, and Node never starts. A quote inside any other field arrives escaped,
|
|
3755
|
+
# so no other field (the folder the session is in, for one) can read as the
|
|
3756
|
+
# start of the prompt.
|
|
2414
3757
|
#
|
|
2415
3758
|
# Otherwise it finds the project root from its own location, never from the
|
|
2416
3759
|
# folder the session has moved to, and hands the same input to the guard
|
|
@@ -2431,7 +3774,7 @@ IFS= read -r -d '' HC_INPUT || true
|
|
|
2431
3774
|
# \\u escape, or as the UTF-8 bytes of a Unicode space. Compared byte for byte.
|
|
2432
3775
|
HC_LEAD='[[:space:]]|\\\\[nrtf]|\\\\u(000[9a-d]|0020|00a0|1680|200[0-9a]|202[89f]|205f|3000|feff)'
|
|
2433
3776
|
HC_LEAD="$HC_LEAD|"$'\\xc2\\xa0|\\xe1\\x9a\\x80|\\xe2\\x80[\\x80-\\x8a\\xa8\\xa9\\xaf]|\\xe2\\x81\\x9f|\\xe3\\x80\\x80|\\xef\\xbb\\xbf'
|
|
2434
|
-
if ! printf '%s' "$HC_INPUT" | LC_ALL=C grep -Eiq "\\"prompt\\"[[:space:]]*:[[:space:]]*\\"($HC_LEAD)*(approve|send back) "; then
|
|
3777
|
+
if ! printf '%s' "$HC_INPUT" | LC_ALL=C grep -Eiq "\\"prompt\\"[[:space:]]*:[[:space:]]*\\"($HC_LEAD)*(approve|send back|accept|question|carry|drop) "; then
|
|
2435
3778
|
exit 0
|
|
2436
3779
|
fi
|
|
2437
3780
|
|
|
@@ -2711,8 +4054,10 @@ function checkDrift(targetRepoRoot) {
|
|
|
2711
4054
|
return { drifted: installed !== current, installed, current, methodVersion: pin?.methodVersion ?? null };
|
|
2712
4055
|
}
|
|
2713
4056
|
function writeProjectIdentity(targetRepo) {
|
|
2714
|
-
if (existsSync3(join5(targetRepo, PROJECT_IDENTITY_REL)))
|
|
4057
|
+
if (existsSync3(join5(targetRepo, PROJECT_IDENTITY_REL))) {
|
|
4058
|
+
repairRemoteCredential(targetRepo);
|
|
2715
4059
|
return "skipped";
|
|
4060
|
+
}
|
|
2716
4061
|
createProjectIdentityFile(targetRepo);
|
|
2717
4062
|
return "written";
|
|
2718
4063
|
}
|
|
@@ -3682,6 +5027,32 @@ function printRecordText(text) {
|
|
|
3682
5027
|
});
|
|
3683
5028
|
return out;
|
|
3684
5029
|
}
|
|
5030
|
+
var MARKUP = /[\\`*_~$<>&[\]{}#|]/;
|
|
5031
|
+
function printStrictText(text) {
|
|
5032
|
+
if (text === "")
|
|
5033
|
+
return "(blank)";
|
|
5034
|
+
let out = "";
|
|
5035
|
+
for (const ch of text) {
|
|
5036
|
+
if (HIDDEN.test(ch))
|
|
5037
|
+
out += `U+${ch.codePointAt(0).toString(16).toUpperCase().padStart(4, "0")}`;
|
|
5038
|
+
else if (MARKUP.test(ch))
|
|
5039
|
+
out += `\\${ch}`;
|
|
5040
|
+
else
|
|
5041
|
+
out += ch;
|
|
5042
|
+
}
|
|
5043
|
+
return out;
|
|
5044
|
+
}
|
|
5045
|
+
function printItemStartText(text) {
|
|
5046
|
+
const out = printStrictText(text);
|
|
5047
|
+
if (out.startsWith(" "))
|
|
5048
|
+
return ` ${out.slice(1)}`;
|
|
5049
|
+
if (out.startsWith("-") || out.startsWith("+"))
|
|
5050
|
+
return `\\${out}`;
|
|
5051
|
+
const ordered = /^(\d{1,9})([.)])/.exec(out);
|
|
5052
|
+
if (ordered !== null)
|
|
5053
|
+
return `${ordered[1]}\\${out.slice(ordered[1].length)}`;
|
|
5054
|
+
return out;
|
|
5055
|
+
}
|
|
3685
5056
|
function makePrinter() {
|
|
3686
5057
|
let hidden = false;
|
|
3687
5058
|
return {
|
|
@@ -3690,6 +5061,16 @@ function makePrinter() {
|
|
|
3690
5061
|
hidden = true;
|
|
3691
5062
|
return printRecordText(value);
|
|
3692
5063
|
},
|
|
5064
|
+
strict(value) {
|
|
5065
|
+
if (!hidden && HIDDEN.test(value))
|
|
5066
|
+
hidden = true;
|
|
5067
|
+
return printStrictText(value);
|
|
5068
|
+
},
|
|
5069
|
+
itemStart(value) {
|
|
5070
|
+
if (!hidden && HIDDEN.test(value))
|
|
5071
|
+
hidden = true;
|
|
5072
|
+
return printItemStartText(value);
|
|
5073
|
+
},
|
|
3693
5074
|
sawHidden() {
|
|
3694
5075
|
return hidden;
|
|
3695
5076
|
}
|
|
@@ -3905,6 +5286,52 @@ function renderClose(heading, observed, attested, checks, p) {
|
|
|
3905
5286
|
renderStated(attested, p)
|
|
3906
5287
|
];
|
|
3907
5288
|
}
|
|
5289
|
+
var REVIEW_LABEL = "**Recorded by Halfcycle from the owner's review.** Each part the review held, and how the project's owner decided it. A line marked as the agent's is the agent's own answer, not Halfcycle's.";
|
|
5290
|
+
var REVIEW_NO_PARTS = "The review had no parts, so it closed on one accept that names no part.";
|
|
5291
|
+
function approvalLine(approval) {
|
|
5292
|
+
if (approval.kind === "none")
|
|
5293
|
+
return "Built without an approved scope.";
|
|
5294
|
+
const where = approval.givenIn === "console" ? "in the console" : "in their own session";
|
|
5295
|
+
return `Scope approved by the project's owner, plan version ${String(approval.planVersion)}, ${where}.`;
|
|
5296
|
+
}
|
|
5297
|
+
function decisionText(part, p) {
|
|
5298
|
+
switch (part.decision) {
|
|
5299
|
+
case "accepted":
|
|
5300
|
+
return "Accepted";
|
|
5301
|
+
case "carried":
|
|
5302
|
+
return part.carriedTo === null || part.carriedTo === void 0 ? `Carried to the next phase planned: ${p.text(part.reason ?? "")}` : `Carried to phase "${part.carriedTo.phase}": ${p.text(part.reason ?? "")}`;
|
|
5303
|
+
case "dropped":
|
|
5304
|
+
return `Dropped: ${p.text(part.reason ?? "")}`;
|
|
5305
|
+
}
|
|
5306
|
+
}
|
|
5307
|
+
function reviewPartLines(part, p) {
|
|
5308
|
+
const lines = [`- ${p.itemStart(part.name)} \u2014 ${decisionText(part, p)}`];
|
|
5309
|
+
const code = codeLinkLine(part.codeLink);
|
|
5310
|
+
if (code !== void 0)
|
|
5311
|
+
lines.push(` - ${p.text(code)}`);
|
|
5312
|
+
for (const sent of part.sentBack)
|
|
5313
|
+
lines.push(` - Sent back in round ${String(sent.round)}: ${p.text(sent.note)}`);
|
|
5314
|
+
for (const q of part.questions) {
|
|
5315
|
+
lines.push(` - Question: ${p.text(q.question)}`);
|
|
5316
|
+
lines.push(` - The agent answered: ${p.text(q.agentAnswer)}`);
|
|
5317
|
+
}
|
|
5318
|
+
return lines;
|
|
5319
|
+
}
|
|
5320
|
+
function removalsLine(record2, p) {
|
|
5321
|
+
const removals = removalsOf(record2);
|
|
5322
|
+
if (removals.length === 0)
|
|
5323
|
+
return null;
|
|
5324
|
+
return `Take these parts out of docs/system.json: ${removals.map((r) => `"${p.strict(r.name)}" (${r.act})`).join(", ")}.`;
|
|
5325
|
+
}
|
|
5326
|
+
function renderReview(record2, p) {
|
|
5327
|
+
const review = record2.review;
|
|
5328
|
+
const blocks = ["## The owner's review", REVIEW_LABEL, approvalLine(review.approval)];
|
|
5329
|
+
blocks.push(review.parts.length === 0 ? REVIEW_NO_PARTS : review.parts.flatMap((part) => reviewPartLines(part, p)).join("\n"));
|
|
5330
|
+
const removals = removalsLine(record2, p);
|
|
5331
|
+
if (removals !== null)
|
|
5332
|
+
blocks.push(removals);
|
|
5333
|
+
return blocks;
|
|
5334
|
+
}
|
|
3908
5335
|
function renderBuildRecordV2Markdown(record2) {
|
|
3909
5336
|
const p = makePrinter();
|
|
3910
5337
|
const attestedBySequence = /* @__PURE__ */ new Map();
|
|
@@ -3930,6 +5357,8 @@ function renderBuildRecordV2Markdown(record2) {
|
|
|
3930
5357
|
...renderChecks(record2.observed.checks, p),
|
|
3931
5358
|
...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)
|
|
3932
5359
|
];
|
|
5360
|
+
if (record2.format === "halfcycle-build-record/v3")
|
|
5361
|
+
body.push(...renderReview(record2, p));
|
|
3933
5362
|
if (total > 1) {
|
|
3934
5363
|
body.push("## Earlier closes");
|
|
3935
5364
|
for (const close of closes.slice(0, -1)) {
|
|
@@ -3944,7 +5373,7 @@ function renderBuildRecordV2Markdown(record2) {
|
|
|
3944
5373
|
}
|
|
3945
5374
|
|
|
3946
5375
|
// dist/build-record/write.js
|
|
3947
|
-
import { closeSync, existsSync as existsSync6, mkdirSync as mkdirSync6, openSync, readFileSync as readFileSync7, renameSync, rmSync as rmSync3, writeSync } from "node:fs";
|
|
5376
|
+
import { closeSync, existsSync as existsSync6, mkdirSync as mkdirSync6, openSync, readFileSync as readFileSync7, renameSync as renameSync2, rmSync as rmSync3, writeSync } from "node:fs";
|
|
3948
5377
|
import { basename, join as join8, resolve, relative as relative2, isAbsolute as isAbsolute2 } from "node:path";
|
|
3949
5378
|
var BUILD_RECORD_DIR = join8("docs", "build-records");
|
|
3950
5379
|
function assertInsideOutDir(outDir, candidate, identity) {
|
|
@@ -3970,7 +5399,7 @@ function createExclusiveOnDisk(path, data) {
|
|
|
3970
5399
|
closeSync(fd);
|
|
3971
5400
|
}
|
|
3972
5401
|
function renameOnDisk(from, to) {
|
|
3973
|
-
|
|
5402
|
+
renameSync2(from, to);
|
|
3974
5403
|
}
|
|
3975
5404
|
var NODE_RECORD_PAIR_IO = {
|
|
3976
5405
|
createExclusive: createExclusiveOnDisk,
|
|
@@ -3996,7 +5425,7 @@ function isOwnEarlierRecord(existingText, served) {
|
|
|
3996
5425
|
} catch {
|
|
3997
5426
|
return false;
|
|
3998
5427
|
}
|
|
3999
|
-
const existing =
|
|
5428
|
+
const existing = buildRecordSchema.safeParse(parsedJson);
|
|
4000
5429
|
if (!existing.success)
|
|
4001
5430
|
return false;
|
|
4002
5431
|
const old = existing.data;
|
|
@@ -4079,6 +5508,23 @@ function replaceOne(io, tmp, finalPath, text) {
|
|
|
4079
5508
|
// dist/build-record/obtain.js
|
|
4080
5509
|
import { relative as relative3 } from "node:path";
|
|
4081
5510
|
|
|
5511
|
+
// dist/open-phase.js
|
|
5512
|
+
function writePhaseStamp(engagementId, phase, home) {
|
|
5513
|
+
if (!isEngagementId(engagementId)) {
|
|
5514
|
+
return { path: "(none \u2014 no valid project id)", reason: new InvalidEngagementIdError(engagementId).message };
|
|
5515
|
+
}
|
|
5516
|
+
const path = engagementEnvPath(engagementId, home);
|
|
5517
|
+
if (phase !== null && !isValidPhaseIdentity(phase)) {
|
|
5518
|
+
return { path, reason: new InvalidPhaseIdentityError(phase).message };
|
|
5519
|
+
}
|
|
5520
|
+
try {
|
|
5521
|
+
writeEngagementEnv(engagementId, { [PHASE_STAMP_ENV_KEY]: phase }, home, [PHASE_STAMP_ENV_KEY]);
|
|
5522
|
+
return null;
|
|
5523
|
+
} catch (err) {
|
|
5524
|
+
return { path, reason: err instanceof Error ? err.message : String(err) };
|
|
5525
|
+
}
|
|
5526
|
+
}
|
|
5527
|
+
|
|
4082
5528
|
// dist/missing-route.js
|
|
4083
5529
|
var MESSAGE = {
|
|
4084
5530
|
GET: /^Route GET .* not found\.$/s,
|
|
@@ -4105,7 +5551,13 @@ var REFUSAL_REASONS = phaseRecordRefusalReasonSchema.options;
|
|
|
4105
5551
|
function buildRecordUrl(credential, phase) {
|
|
4106
5552
|
return `${credential.serviceUrl}/engagements/${encodeURIComponent(credential.engagementId)}/phases/${encodeURIComponent(phase)}/build-record`;
|
|
4107
5553
|
}
|
|
4108
|
-
|
|
5554
|
+
function clearStampFor(engagementId, phase, home) {
|
|
5555
|
+
const stamped = readEngagementEnv(engagementId, home)?.[PHASE_STAMP_ENV_KEY];
|
|
5556
|
+
if (stamped !== phase)
|
|
5557
|
+
return null;
|
|
5558
|
+
return writePhaseStamp(engagementId, null, home);
|
|
5559
|
+
}
|
|
5560
|
+
async function obtainBuildRecord(credential, repoRoot, phase, io = NODE_RECORD_PAIR_IO, home) {
|
|
4109
5561
|
const url = buildRecordUrl(credential, phase);
|
|
4110
5562
|
let res;
|
|
4111
5563
|
let text;
|
|
@@ -4158,7 +5610,8 @@ async function obtainBuildRecord(credential, repoRoot, phase, io = NODE_RECORD_P
|
|
|
4158
5610
|
kind: "written",
|
|
4159
5611
|
jsonPath: relative3(repoRoot, result.jsonPath),
|
|
4160
5612
|
mdPath: relative3(repoRoot, result.mdPath),
|
|
4161
|
-
headline: latestCloseHeadline(answer.record)
|
|
5613
|
+
headline: latestCloseHeadline(answer.record),
|
|
5614
|
+
stampFailure: clearStampFor(credential.engagementId, phase, home)
|
|
4162
5615
|
};
|
|
4163
5616
|
}
|
|
4164
5617
|
if (isMissingRoute("GET", res.status, raw)) {
|