@patronage/software-factory 1.0.0-alpha.13 → 1.0.0-alpha.16
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/README.md +5 -1
- package/dist/index.d.ts +432 -714
- package/dist/index.js +6459 -7293
- package/dist/schemas.d.ts +98 -738
- package/dist/schemas.js +95 -426
- package/package.json +4 -3
package/dist/schemas.js
CHANGED
|
@@ -517,82 +517,111 @@ const assertPrVerifyProofRules = (proof, context) => {
|
|
|
517
517
|
assertBatteryCompleteness(proof, context);
|
|
518
518
|
assertNotRequiredStampAuthorization(proof, context);
|
|
519
519
|
};
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
const
|
|
520
|
+
//#endregion
|
|
521
|
+
//#region src/pr-verify-proof-contract.ts
|
|
522
|
+
const SUPPORTED_PR_VERIFY_SCHEMA_VERSIONS = [
|
|
523
|
+
1,
|
|
524
|
+
2,
|
|
525
|
+
3,
|
|
526
|
+
4
|
|
527
|
+
];
|
|
528
|
+
const executedCommandSchema = z.object({
|
|
529
|
+
command: z.string().min(1),
|
|
530
|
+
counts: z.object({
|
|
531
|
+
testFiles: z.number().nonnegative().optional(),
|
|
532
|
+
tests: z.number().nonnegative().optional()
|
|
533
|
+
}).optional(),
|
|
534
|
+
durationMs: z.number().nonnegative(),
|
|
535
|
+
exitCode: z.number(),
|
|
536
|
+
name: z.string().min(1),
|
|
537
|
+
scope: z.enum([
|
|
538
|
+
"always",
|
|
539
|
+
"docs-only",
|
|
540
|
+
"trivial",
|
|
541
|
+
"full"
|
|
542
|
+
])
|
|
543
|
+
});
|
|
544
|
+
const notRequiredCommandSchema = z.object({
|
|
545
|
+
basis: z.string().min(1),
|
|
546
|
+
impactTarget: z.string().min(1),
|
|
547
|
+
name: z.string().min(1)
|
|
548
|
+
});
|
|
549
|
+
const prVerifySchemaVersionSchema = z.number().refine((value) => SUPPORTED_PR_VERIFY_SCHEMA_VERSIONS.includes(value), { message: `schemaVersion must be one of: ${SUPPORTED_PR_VERIFY_SCHEMA_VERSIONS.join(", ")}` });
|
|
550
|
+
const prVerifyProofSchema = z.object({
|
|
551
|
+
authoringSession: z.string().trim().min(1).optional(),
|
|
552
|
+
base: z.string().min(1),
|
|
553
|
+
baselineFullProofs: z.array(z.object({
|
|
554
|
+
base: z.string().min(1),
|
|
555
|
+
changedFiles: z.array(z.string()),
|
|
556
|
+
headSha: z.string().regex(/^[0-9a-f]{40}$/u),
|
|
557
|
+
profilePath: z.string().min(1),
|
|
558
|
+
projectKey: z.string().min(1),
|
|
559
|
+
repository: z.string().min(1)
|
|
560
|
+
})).optional(),
|
|
561
|
+
changedFiles: z.array(z.string()),
|
|
562
|
+
classification: z.enum([
|
|
563
|
+
"docs/process-only",
|
|
564
|
+
"trivial",
|
|
565
|
+
"non-trivial"
|
|
566
|
+
]),
|
|
567
|
+
classificationReasons: z.array(z.string()),
|
|
568
|
+
command: z.literal("patronage-factory pr:verify"),
|
|
569
|
+
durationMs: z.number().nonnegative(),
|
|
570
|
+
endedAt: z.iso.datetime(),
|
|
571
|
+
executedCommands: z.array(executedCommandSchema).optional(),
|
|
572
|
+
headSha: z.string().regex(/^[0-9a-f]{40}$/u),
|
|
573
|
+
impactStamp: impactStampSchema.optional(),
|
|
574
|
+
mergeBaseSha: z.string().regex(/^[0-9a-f]{40}$/u).optional(),
|
|
575
|
+
mode: z.enum([
|
|
576
|
+
"docs-only",
|
|
577
|
+
"trivial",
|
|
578
|
+
"full"
|
|
579
|
+
]),
|
|
580
|
+
notRequiredCommands: z.array(notRequiredCommandSchema).min(1).optional(),
|
|
581
|
+
outcome: z.enum(["aborted", "passed"]).optional(),
|
|
582
|
+
patchId: z.string().regex(/^[0-9a-f]{40}$/u).optional(),
|
|
583
|
+
profilePath: z.string().min(1),
|
|
584
|
+
projectKey: z.string().min(1),
|
|
585
|
+
repository: z.string().min(1),
|
|
586
|
+
schemaVersion: prVerifySchemaVersionSchema,
|
|
587
|
+
startedAt: z.iso.datetime(),
|
|
588
|
+
verificationCommands: z.array(z.object({
|
|
589
|
+
command: z.string().min(1),
|
|
590
|
+
description: z.string().min(1),
|
|
591
|
+
impactTarget: z.string().min(1).optional(),
|
|
592
|
+
name: z.string().min(1),
|
|
593
|
+
scope: z.enum([
|
|
594
|
+
"always",
|
|
595
|
+
"docs-only",
|
|
596
|
+
"trivial",
|
|
597
|
+
"full"
|
|
598
|
+
])
|
|
599
|
+
}))
|
|
600
|
+
}).superRefine(assertPrVerifyProofRules);
|
|
601
|
+
/** Artifact versions accepted at ingest. */
|
|
602
|
+
const SUPPORTED_CLOSEOUT_SCHEMA_VERSIONS = [5];
|
|
523
603
|
const MAX_CLOSEOUT_ROWS = 1e3;
|
|
524
604
|
const IdentifierSchema = z.string().min(1).max(500);
|
|
525
605
|
const NarrativeSchema = z.string().min(1).max(1e4);
|
|
526
606
|
const RepoSchema = z.string().min(1).max(200);
|
|
527
|
-
const
|
|
528
|
-
const CloseoutLandingSpotSchema = z.enum([
|
|
529
|
-
"CLI",
|
|
530
|
-
"profile",
|
|
531
|
-
"skill",
|
|
532
|
-
"ADR"
|
|
533
|
-
]);
|
|
534
|
-
const CurrentCloseoutSourceSchema = z.object({
|
|
535
|
-
detail: NarrativeSchema,
|
|
536
|
-
kind: z.literal("boundary-thermo-attestation")
|
|
537
|
-
}).strict();
|
|
538
|
-
const LegacyCloseoutSourceSchema = z.object({
|
|
607
|
+
const CloseoutSourceSchema = z.object({
|
|
539
608
|
detail: NarrativeSchema,
|
|
540
|
-
kind: z.
|
|
541
|
-
}).strict();
|
|
542
|
-
const LegacyCloseoutScopingSchema = z.object({
|
|
543
|
-
epic: IdentifierSchema,
|
|
544
|
-
issueFilter: z.number().int().positive().optional(),
|
|
545
|
-
prFilter: z.number().int().positive().optional(),
|
|
546
|
-
repo: RepoSchema,
|
|
547
|
-
scopedByEpicIssueSet: z.boolean(),
|
|
548
|
-
window: z.object({
|
|
549
|
-
from: IsoDateSchema,
|
|
550
|
-
to: IsoDateSchema
|
|
551
|
-
}).strict()
|
|
609
|
+
kind: z.literal("token-harvest")
|
|
552
610
|
}).strict();
|
|
553
611
|
const CloseoutBudgetSchema = z.object({
|
|
554
612
|
note: NarrativeSchema,
|
|
555
613
|
tier: z.literal("overseer")
|
|
556
614
|
}).strict();
|
|
557
|
-
const LegacyCloseoutBudgetSchema = z.object({
|
|
558
|
-
interiorSourcesConsumed: z.number().int().nonnegative(),
|
|
559
|
-
note: NarrativeSchema,
|
|
560
|
-
scoping: LegacyCloseoutScopingSchema.optional(),
|
|
561
|
-
tier: z.literal("overseer")
|
|
562
|
-
}).strict();
|
|
563
|
-
const BoundaryThermoAttestationSchema = z.object({
|
|
564
|
-
owner: z.string().min(1).max(200),
|
|
565
|
-
runs: z.number().int().nonnegative(),
|
|
566
|
-
waived: z.boolean(),
|
|
567
|
-
waiverRationale: NarrativeSchema.optional()
|
|
568
|
-
}).strict();
|
|
569
615
|
const CloseoutMetricRowSchema = z.object({
|
|
570
616
|
label: IdentifierSchema,
|
|
571
617
|
metric: IdentifierSchema,
|
|
572
|
-
source:
|
|
618
|
+
source: CloseoutSourceSchema,
|
|
573
619
|
unit: z.enum([
|
|
574
|
-
"count",
|
|
575
620
|
"boolean",
|
|
576
|
-
"text"
|
|
577
|
-
]),
|
|
578
|
-
value: z.union([
|
|
579
|
-
z.number(),
|
|
580
|
-
NarrativeSchema,
|
|
581
|
-
z.boolean(),
|
|
582
|
-
z.null()
|
|
583
|
-
])
|
|
584
|
-
}).strict();
|
|
585
|
-
const LegacyCloseoutMetricRowSchema = z.object({
|
|
586
|
-
label: IdentifierSchema,
|
|
587
|
-
metric: IdentifierSchema,
|
|
588
|
-
source: LegacyCloseoutSourceSchema,
|
|
589
|
-
unit: z.enum([
|
|
590
|
-
"tokens",
|
|
591
|
-
"usd",
|
|
592
621
|
"count",
|
|
593
|
-
"
|
|
594
|
-
"
|
|
595
|
-
"
|
|
622
|
+
"text",
|
|
623
|
+
"tokens",
|
|
624
|
+
"usd"
|
|
596
625
|
]),
|
|
597
626
|
value: z.union([
|
|
598
627
|
z.number(),
|
|
@@ -601,306 +630,21 @@ const LegacyCloseoutMetricRowSchema = z.object({
|
|
|
601
630
|
z.null()
|
|
602
631
|
])
|
|
603
632
|
}).strict();
|
|
604
|
-
const CloseoutLessonSchema = z.object({
|
|
605
|
-
id: IdentifierSchema,
|
|
606
|
-
landingSpot: CloseoutLandingSpotSchema,
|
|
607
|
-
lesson: NarrativeSchema,
|
|
608
|
-
rationale: NarrativeSchema.optional(),
|
|
609
|
-
source: CurrentCloseoutSourceSchema.optional()
|
|
610
|
-
}).strict();
|
|
611
|
-
const LegacyCloseoutLessonSchema = z.object({
|
|
612
|
-
id: IdentifierSchema,
|
|
613
|
-
landingSpot: CloseoutLandingSpotSchema,
|
|
614
|
-
lesson: NarrativeSchema,
|
|
615
|
-
rationale: NarrativeSchema.optional(),
|
|
616
|
-
source: LegacyCloseoutSourceSchema.optional()
|
|
617
|
-
}).strict();
|
|
618
633
|
const CloseoutBlindspotSchema = z.object({
|
|
619
634
|
id: IdentifierSchema,
|
|
620
635
|
note: NarrativeSchema,
|
|
621
636
|
reason: NarrativeSchema.optional()
|
|
622
637
|
}).strict();
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
generatedAt: z.iso.datetime(),
|
|
627
|
-
key: IdentifierSchema,
|
|
628
|
-
landingSpot: CloseoutLandingSpotSchema.optional(),
|
|
629
|
-
repo: RepoSchema,
|
|
630
|
-
rowType: z.enum([
|
|
631
|
-
"metric",
|
|
632
|
-
"lesson",
|
|
633
|
-
"blindspot"
|
|
634
|
-
]),
|
|
635
|
-
schemaVersion: z.literal(schemaVersion),
|
|
636
|
-
source: NarrativeSchema,
|
|
637
|
-
unit: IdentifierSchema.optional(),
|
|
638
|
-
value: z.union([
|
|
639
|
-
NarrativeSchema,
|
|
640
|
-
z.number(),
|
|
641
|
-
z.null()
|
|
642
|
-
])
|
|
643
|
-
}).strict();
|
|
644
|
-
const CloseoutLedgerRowSchema = closeoutLedgerRowSchema(4);
|
|
645
|
-
const LegacyCloseoutLedgerRowSchema = closeoutLedgerRowSchema(LEGACY_CLOSEOUT_SCHEMA_VERSION);
|
|
646
|
-
const closeoutArtifactShape = (input) => z.object({
|
|
647
|
-
boundaryThermo: BoundaryThermoAttestationSchema,
|
|
648
|
-
budget: input.budget,
|
|
638
|
+
/** Strict, bounded runtime transport contract emitted by factory:closeout. */
|
|
639
|
+
const closeoutArtifactSchema = z.object({
|
|
640
|
+
budget: CloseoutBudgetSchema,
|
|
649
641
|
couldNotSee: z.array(CloseoutBlindspotSchema).min(1).max(MAX_CLOSEOUT_ROWS),
|
|
650
642
|
epic: IdentifierSchema,
|
|
651
643
|
generatedAt: z.iso.datetime(),
|
|
652
|
-
|
|
653
|
-
lessons: z.array(input.lessons).max(MAX_CLOSEOUT_ROWS),
|
|
654
|
-
metrics: z.array(input.metrics).max(MAX_CLOSEOUT_ROWS),
|
|
644
|
+
metrics: z.array(CloseoutMetricRowSchema).max(MAX_CLOSEOUT_ROWS),
|
|
655
645
|
repo: RepoSchema,
|
|
656
|
-
schemaVersion: z.literal(
|
|
646
|
+
schemaVersion: z.literal(5)
|
|
657
647
|
}).strict();
|
|
658
|
-
/** Strict, bounded runtime transport contract emitted by factory:closeout. */
|
|
659
|
-
const closeoutArtifactSchema = closeoutArtifactShape({
|
|
660
|
-
budget: CloseoutBudgetSchema,
|
|
661
|
-
ledgerRows: CloseoutLedgerRowSchema,
|
|
662
|
-
lessons: CloseoutLessonSchema,
|
|
663
|
-
metrics: CloseoutMetricRowSchema,
|
|
664
|
-
schemaVersion: 4
|
|
665
|
-
});
|
|
666
|
-
/** Reader-only compatibility for closeout artifacts written before #554. */
|
|
667
|
-
const legacyCloseoutArtifactSchema = closeoutArtifactShape({
|
|
668
|
-
budget: LegacyCloseoutBudgetSchema,
|
|
669
|
-
ledgerRows: LegacyCloseoutLedgerRowSchema,
|
|
670
|
-
lessons: LegacyCloseoutLessonSchema,
|
|
671
|
-
metrics: LegacyCloseoutMetricRowSchema,
|
|
672
|
-
schemaVersion: LEGACY_CLOSEOUT_SCHEMA_VERSION
|
|
673
|
-
});
|
|
674
|
-
/** Accept current output and retained v3 artifacts when reading or replaying. */
|
|
675
|
-
const closeoutArtifactReadSchema = z.union([closeoutArtifactSchema, legacyCloseoutArtifactSchema]);
|
|
676
|
-
//#endregion
|
|
677
|
-
//#region src/retro-envelope.ts
|
|
678
|
-
/**
|
|
679
|
-
* Versioned retro envelope schema (epic #27 wave 2, issue #34).
|
|
680
|
-
*
|
|
681
|
-
* One envelope per lane, built at `factory:closeout` and delivered through the
|
|
682
|
-
* typed gate-sink as the `retro-envelope` ingest kind. Re-derived in TypeScript
|
|
683
|
-
* from the `spike/telemetry-layer2` S5 scratch schema (reference-only, never
|
|
684
|
-
* merged). This module is the single source of truth for the v1 wire shape
|
|
685
|
-
* and its bounds ({@link RETRO_ENVELOPE_WIRE_BOUNDS}) — producer and consumer
|
|
686
|
-
* alike. HQ imports these exports directly from the Worker-safe
|
|
687
|
-
* `@patronage/software-factory/schemas` subpath
|
|
688
|
-
* (`software-factory-hq/src/contracts/retro-schemas.ts`) instead of
|
|
689
|
-
* maintaining a parallel hand-written copy, so there is exactly one wire
|
|
690
|
-
* contract and no drift-detection machinery is needed (issue #350; formerly
|
|
691
|
-
* a hand-written twin plus a 767-line parity test, #46).
|
|
692
|
-
*
|
|
693
|
-
* DESIGN INVARIANT: cross-family token sums must be UNREPRESENTABLE.
|
|
694
|
-
*
|
|
695
|
-
* The two model families use different tokenizers, prices, and accounting
|
|
696
|
-
* conventions, so any token total that spans Claude and GPT is a lie:
|
|
697
|
-
*
|
|
698
|
-
* 1. There is no combined/total token field anywhere in the envelope.
|
|
699
|
-
* 2. `tokenFamilies` is strict — its only keys are `claude` and `gpt`; data
|
|
700
|
-
* cannot smuggle in a third "all"/"combined" slot.
|
|
701
|
-
* 3. The family BLOCKS are structurally different shapes with DIFFERENT keys
|
|
702
|
-
* (claude is a single flat block keyed on freshInput/cacheReadInput/
|
|
703
|
-
* cacheCreationInput; gpt is `{ roles: [...] }`). The exclusive input-tier
|
|
704
|
-
* COUNTS share no key name across families. The residual names shared
|
|
705
|
-
* between the claude block and a gpt ROLE entry are pinned to exactly
|
|
706
|
-
* {costUsd, model, output} (PR #32 advisory): `costUsd` is deliberate —
|
|
707
|
-
* USD is the one cross-family summable unit (rule 4); `model` is an
|
|
708
|
-
* unsummable label; `output` is the same name at DIFFERENT depths (lane
|
|
709
|
-
* block vs per-role entry), frozen by a tripwire test in
|
|
710
|
-
* `retro-envelope.test.ts` so the overlap cannot grow. Renaming `output`
|
|
711
|
-
* is a schemaVersion-2 wire change, deliberately not spent in v1.
|
|
712
|
-
* 4. Cost is per-family USD and nullable. Combined totals are allowed in USD
|
|
713
|
-
* only, and only as a projection-time sum of per-family USD.
|
|
714
|
-
*
|
|
715
|
-
* Field names also encode the S2/S3 reader lessons: Claude `freshInput` alone
|
|
716
|
-
* is not prompt size (true input context = freshInput + cacheReadInput +
|
|
717
|
-
* cacheCreationInput, requestId-deduped), and codex `inputInclusiveOfCache`
|
|
718
|
-
* already includes `cachedInput`, so `freshInputDerived` (inclusive − cached)
|
|
719
|
-
* is the only value safe to feed a per-token pricer.
|
|
720
|
-
*
|
|
721
|
-
* COMPLETENESS POSTURE: harvest may have no usable native log for a lane, so
|
|
722
|
-
* `tokenFamilies` may legitimately be absent. The closeout build gate demands
|
|
723
|
-
* a valid envelope, not available telemetry. A families-absent envelope keeps
|
|
724
|
-
* its operator-visible data gaps and is a replayable advisory HQ event, so it
|
|
725
|
-
* never substitutes unavailable usage with zero. The wire shape (field names,
|
|
726
|
-
* types, structure) stays byte-parity with HQ v1.
|
|
727
|
-
*/
|
|
728
|
-
const RETRO_ENVELOPE_SCHEMA_VERSION = 1;
|
|
729
|
-
/**
|
|
730
|
-
* v1 wire bounds — the single source the schemas below are built from and the
|
|
731
|
-
* builder's sanitization seam clamps to (`retro-envelope-builder.ts` imports
|
|
732
|
-
* these; it keeps no bound constants of its own). HQ imports this module
|
|
733
|
-
* directly (issue #350), so there is one set of bounds, not a second copy to
|
|
734
|
-
* keep in sync.
|
|
735
|
-
*/
|
|
736
|
-
const RETRO_ENVELOPE_WIRE_BOUNDS = {
|
|
737
|
-
/** archiveRef pointer (key/URL) max characters. */
|
|
738
|
-
archiveRefMaxChars: 2048,
|
|
739
|
-
/** refs.branch max characters (git ref length ceiling). */
|
|
740
|
-
branchMaxChars: 255,
|
|
741
|
-
/** gates[] wire cap — the builder keeps the most recent records. */
|
|
742
|
-
gatesMax: 500,
|
|
743
|
-
/** gpt roles[] cap (delegated roles per lane). */
|
|
744
|
-
gptRolesMax: 20,
|
|
745
|
-
/** Cap for list fields: joinKeys id arrays, phases, dataGaps. */
|
|
746
|
-
listMax: 100,
|
|
747
|
-
/** Bounded name fields: agentRunId, gate, phase name, refs, join-key ids. */
|
|
748
|
-
nameMaxChars: 200,
|
|
749
|
-
/** Short identifier fields: repo name/owner, model, role. */
|
|
750
|
-
shortMaxChars: 100,
|
|
751
|
-
/** Free-text fields: dataGaps entries, outcome.verdict. */
|
|
752
|
-
textMaxChars: 500
|
|
753
|
-
};
|
|
754
|
-
const nonNegInt = z.number().int().nonnegative();
|
|
755
|
-
const isoTimestamp = z.iso.datetime();
|
|
756
|
-
const boundedName = z.string().min(1).max(RETRO_ENVELOPE_WIRE_BOUNDS.nameMaxChars);
|
|
757
|
-
const boundedShortName = z.string().min(1).max(RETRO_ENVELOPE_WIRE_BOUNDS.shortMaxChars);
|
|
758
|
-
const boundedText = z.string().min(1).max(RETRO_ENVELOPE_WIRE_BOUNDS.textMaxChars);
|
|
759
|
-
const RetroRepoSchema = z.object({
|
|
760
|
-
name: boundedShortName,
|
|
761
|
-
owner: boundedShortName
|
|
762
|
-
}).strict();
|
|
763
|
-
const RetroRefsSchema = z.object({
|
|
764
|
-
branch: z.string().min(1).max(RETRO_ENVELOPE_WIRE_BOUNDS.branchMaxChars).optional(),
|
|
765
|
-
epic: boundedName.optional(),
|
|
766
|
-
headSha: z.string().regex(/^[0-9a-f]{7,40}$/u).optional(),
|
|
767
|
-
issue: boundedName.optional(),
|
|
768
|
-
prNumber: z.number().int().positive().optional()
|
|
769
|
-
}).strict();
|
|
770
|
-
/**
|
|
771
|
-
* Per-lane join keys: how the envelope re-joins raw per-session sources.
|
|
772
|
-
* `agentRunId` lives at the envelope root; these carry the per-family session
|
|
773
|
-
* identities (Claude `session.id`, codex `threadId`).
|
|
774
|
-
*/
|
|
775
|
-
const RetroJoinKeysSchema = z.object({
|
|
776
|
-
claudeSessionIds: z.array(boundedName).max(RETRO_ENVELOPE_WIRE_BOUNDS.listMax).default([]),
|
|
777
|
-
codexThreadIds: z.array(boundedName).max(RETRO_ENVELOPE_WIRE_BOUNDS.listMax).default([])
|
|
778
|
-
}).strict();
|
|
779
|
-
const RetroWallClockSchema = z.object({
|
|
780
|
-
endTs: isoTimestamp,
|
|
781
|
-
startTs: isoTimestamp,
|
|
782
|
-
totalSec: z.number().nonnegative()
|
|
783
|
-
}).strict();
|
|
784
|
-
const RetroPhaseMarkSchema = z.object({
|
|
785
|
-
at: isoTimestamp,
|
|
786
|
-
deltaSec: z.number().nonnegative().optional(),
|
|
787
|
-
name: boundedName
|
|
788
|
-
}).strict();
|
|
789
|
-
/** Gate-timing ledger entry: `{gate, startedAt, duration, outcome, cycle}`. */
|
|
790
|
-
const RetroGateLedgerEntrySchema = z.object({
|
|
791
|
-
cycle: nonNegInt,
|
|
792
|
-
duration: z.number().nonnegative(),
|
|
793
|
-
gate: boundedName,
|
|
794
|
-
outcome: z.enum([
|
|
795
|
-
"pass",
|
|
796
|
-
"fail",
|
|
797
|
-
"skip"
|
|
798
|
-
]),
|
|
799
|
-
startedAt: isoTimestamp
|
|
800
|
-
}).strict();
|
|
801
|
-
/**
|
|
802
|
-
* Claude family: Anthropic-style EXCLUSIVE input tiers. True input context is
|
|
803
|
-
* freshInput + cacheReadInput + cacheCreationInput; values are
|
|
804
|
-
* requestId-deduped (S2: naive row sums overcount input ~1.94x).
|
|
805
|
-
*/
|
|
806
|
-
const claudeTokenBlockSchema = z.object({
|
|
807
|
-
cacheCreationInput: nonNegInt,
|
|
808
|
-
cacheReadInput: nonNegInt,
|
|
809
|
-
costUsd: z.number().nonnegative().nullable(),
|
|
810
|
-
family: z.literal("claude"),
|
|
811
|
-
freshInput: nonNegInt,
|
|
812
|
-
model: boundedShortName,
|
|
813
|
-
output: nonNegInt,
|
|
814
|
-
requests: nonNegInt
|
|
815
|
-
}).strict();
|
|
816
|
-
/**
|
|
817
|
-
* GPT/codex family, per delegated role. codex reports `input_tokens`
|
|
818
|
-
* INCLUSIVE of cached tokens; `freshInputDerived` = inclusive − cached is the
|
|
819
|
-
* only value safe for a per-token pricer (S3: skipping this overprices ~3.9x).
|
|
820
|
-
*/
|
|
821
|
-
const gptRoleUsageSchema = z.object({
|
|
822
|
-
cachedInput: nonNegInt,
|
|
823
|
-
costUsd: z.number().nonnegative().nullable(),
|
|
824
|
-
freshInputDerived: nonNegInt,
|
|
825
|
-
inputInclusiveOfCache: nonNegInt,
|
|
826
|
-
model: boundedShortName,
|
|
827
|
-
output: nonNegInt,
|
|
828
|
-
reasoningOutput: nonNegInt,
|
|
829
|
-
role: boundedShortName,
|
|
830
|
-
threadId: boundedName.optional()
|
|
831
|
-
}).strict().refine((usage) => usage.freshInputDerived === usage.inputInclusiveOfCache - usage.cachedInput, { message: "freshInputDerived must equal inputInclusiveOfCache - cachedInput (S3 accounting rule)" });
|
|
832
|
-
const gptTokenBlockSchema = z.object({
|
|
833
|
-
family: z.literal("gpt"),
|
|
834
|
-
roles: z.array(gptRoleUsageSchema).min(1).max(RETRO_ENVELOPE_WIRE_BOUNDS.gptRolesMax)
|
|
835
|
-
}).strict();
|
|
836
|
-
/**
|
|
837
|
-
* Strict: only `claude` and `gpt`, both optional, no third slot.
|
|
838
|
-
*
|
|
839
|
-
* A lane may have no usable native session data. The build gate accepts that
|
|
840
|
-
* state and the advisory sink journals the valid envelope with its recorded
|
|
841
|
-
* gaps, without inventing usage.
|
|
842
|
-
*/
|
|
843
|
-
const retroTokenFamiliesSchema = z.object({
|
|
844
|
-
claude: claudeTokenBlockSchema.optional(),
|
|
845
|
-
gpt: gptTokenBlockSchema.optional()
|
|
846
|
-
}).strict();
|
|
847
|
-
const RetroCycleCountersSchema = z.object({
|
|
848
|
-
gateRunsToFirstGreen: nonNegInt,
|
|
849
|
-
reviewerFixRounds: nonNegInt,
|
|
850
|
-
thermoFixRounds: nonNegInt
|
|
851
|
-
}).strict();
|
|
852
|
-
const RetroOutcomeSchema = z.object({
|
|
853
|
-
status: z.enum([
|
|
854
|
-
"success",
|
|
855
|
-
"blocked",
|
|
856
|
-
"fail",
|
|
857
|
-
"ship-with-followups"
|
|
858
|
-
]),
|
|
859
|
-
verdict: boundedText.optional()
|
|
860
|
-
}).strict();
|
|
861
|
-
const retroEnvelopeV1Schema = z.object({
|
|
862
|
-
agentRunId: boundedName,
|
|
863
|
-
/** Pointer (key/URL) to #8's durable proof archive — never the payload. */
|
|
864
|
-
archiveRef: z.string().min(1).max(RETRO_ENVELOPE_WIRE_BOUNDS.archiveRefMaxChars).optional(),
|
|
865
|
-
cycles: RetroCycleCountersSchema,
|
|
866
|
-
dataGaps: z.array(boundedText).max(RETRO_ENVELOPE_WIRE_BOUNDS.listMax).default([]),
|
|
867
|
-
gates: z.array(RetroGateLedgerEntrySchema).max(RETRO_ENVELOPE_WIRE_BOUNDS.gatesMax),
|
|
868
|
-
generatedAt: isoTimestamp,
|
|
869
|
-
interventions: z.object({ count: nonNegInt }).strict(),
|
|
870
|
-
joinKeys: RetroJoinKeysSchema,
|
|
871
|
-
kind: z.literal("retro-envelope"),
|
|
872
|
-
outcome: RetroOutcomeSchema.optional(),
|
|
873
|
-
phases: z.array(RetroPhaseMarkSchema).max(RETRO_ENVELOPE_WIRE_BOUNDS.listMax),
|
|
874
|
-
refs: RetroRefsSchema,
|
|
875
|
-
repo: RetroRepoSchema,
|
|
876
|
-
schemaVersion: z.literal(1),
|
|
877
|
-
tokenFamilies: retroTokenFamiliesSchema,
|
|
878
|
-
wallClock: RetroWallClockSchema
|
|
879
|
-
}).strict().refine((envelope) => envelope.refs.epic !== void 0 || envelope.refs.issue !== void 0 || envelope.refs.prNumber !== void 0, { message: "refs must anchor to an epic, issue, or PR" });
|
|
880
|
-
/**
|
|
881
|
-
* Version 1 envelopes written before #170 carried an inert `harness` label.
|
|
882
|
-
* Accept that exact historical field and normalize it away. The current strict
|
|
883
|
-
* schema above neither emits nor exposes it, and all other unknown fields still
|
|
884
|
-
* fail validation.
|
|
885
|
-
*/
|
|
886
|
-
const retroEnvelopeV1ReaderSchema = z.preprocess((candidate) => {
|
|
887
|
-
if (candidate === null || typeof candidate !== "object" || Array.isArray(candidate) || !("harness" in candidate)) return candidate;
|
|
888
|
-
const record = candidate;
|
|
889
|
-
if (!boundedShortName.safeParse(record.harness).success) return candidate;
|
|
890
|
-
const { harness: _historicalHarness, ...current } = record;
|
|
891
|
-
return current;
|
|
892
|
-
}, retroEnvelopeV1Schema);
|
|
893
|
-
/**
|
|
894
|
-
* Versioned payload validators, keyed by schema major. Unknown majors never
|
|
895
|
-
* reach these — {@link parseRetroEnvelope} returns them raw and marked
|
|
896
|
-
* degraded, mirroring HQ's ingest skew posture (stored raw, never dropped).
|
|
897
|
-
*/
|
|
898
|
-
const RETRO_ENVELOPE_VALIDATORS = { [1]: retroEnvelopeV1ReaderSchema };
|
|
899
|
-
const SUPPORTED_RETRO_ENVELOPE_SCHEMA_VERSIONS = Object.keys(RETRO_ENVELOPE_VALIDATORS).map(Number);
|
|
900
|
-
/** Epic anchor for an envelope: refs.epic, else refs.issue, else the PR. */
|
|
901
|
-
const retroEpicReference = (refs) => {
|
|
902
|
-
return (refs.epic ?? refs.issue ?? (refs.prNumber === void 0 ? void 0 : `pr#${refs.prNumber}`))?.replace(/^#/u, "");
|
|
903
|
-
};
|
|
904
648
|
//#endregion
|
|
905
649
|
//#region src/schemas.ts
|
|
906
650
|
const EVIDENCE_CHECK_TYPES = ["review", "verify"];
|
|
@@ -971,81 +715,6 @@ const evidenceEnvelopeSchema = z.object({
|
|
|
971
715
|
});
|
|
972
716
|
}
|
|
973
717
|
});
|
|
974
|
-
const SUPPORTED_PR_VERIFY_SCHEMA_VERSIONS = [
|
|
975
|
-
1,
|
|
976
|
-
2,
|
|
977
|
-
3,
|
|
978
|
-
4
|
|
979
|
-
];
|
|
980
|
-
const executedCommandSchema = z.object({
|
|
981
|
-
command: z.string().min(1),
|
|
982
|
-
counts: z.object({
|
|
983
|
-
testFiles: z.number().nonnegative().optional(),
|
|
984
|
-
tests: z.number().nonnegative().optional()
|
|
985
|
-
}).optional(),
|
|
986
|
-
durationMs: z.number().nonnegative(),
|
|
987
|
-
exitCode: z.number(),
|
|
988
|
-
name: z.string().min(1),
|
|
989
|
-
scope: z.enum([
|
|
990
|
-
"always",
|
|
991
|
-
"docs-only",
|
|
992
|
-
"trivial",
|
|
993
|
-
"full"
|
|
994
|
-
])
|
|
995
|
-
});
|
|
996
|
-
const notRequiredCommandSchema = z.object({
|
|
997
|
-
basis: z.string().min(1),
|
|
998
|
-
impactTarget: z.string().min(1),
|
|
999
|
-
name: z.string().min(1)
|
|
1000
|
-
});
|
|
1001
|
-
const prVerifySchemaVersionSchema = z.number().refine((value) => SUPPORTED_PR_VERIFY_SCHEMA_VERSIONS.includes(value), { message: `schemaVersion must be one of: ${SUPPORTED_PR_VERIFY_SCHEMA_VERSIONS.join(", ")}` });
|
|
1002
|
-
const prVerifyProofSchema = z.object({
|
|
1003
|
-
authoringSession: z.string().trim().min(1).optional(),
|
|
1004
|
-
base: z.string().min(1),
|
|
1005
|
-
baselineFullProofs: z.array(z.object({
|
|
1006
|
-
base: z.string().min(1),
|
|
1007
|
-
changedFiles: z.array(z.string()),
|
|
1008
|
-
headSha: z.string().regex(/^[0-9a-f]{40}$/u),
|
|
1009
|
-
profilePath: z.string().min(1),
|
|
1010
|
-
projectKey: z.string().min(1),
|
|
1011
|
-
repository: z.string().min(1)
|
|
1012
|
-
})).optional(),
|
|
1013
|
-
changedFiles: z.array(z.string()),
|
|
1014
|
-
classification: z.enum(DIFF_CLASSIFICATIONS),
|
|
1015
|
-
classificationReasons: z.array(z.string()),
|
|
1016
|
-
command: z.literal("patronage-factory pr:verify"),
|
|
1017
|
-
durationMs: z.number().nonnegative(),
|
|
1018
|
-
endedAt: z.iso.datetime(),
|
|
1019
|
-
executedCommands: z.array(executedCommandSchema).optional(),
|
|
1020
|
-
headSha: z.string().regex(/^[0-9a-f]{40}$/u),
|
|
1021
|
-
impactStamp: impactStampSchema.optional(),
|
|
1022
|
-
mergeBaseSha: z.string().regex(/^[0-9a-f]{40}$/u).optional(),
|
|
1023
|
-
mode: z.enum([
|
|
1024
|
-
"docs-only",
|
|
1025
|
-
"trivial",
|
|
1026
|
-
"full"
|
|
1027
|
-
]),
|
|
1028
|
-
notRequiredCommands: z.array(notRequiredCommandSchema).min(1).optional(),
|
|
1029
|
-
outcome: z.enum(["aborted", "passed"]).optional(),
|
|
1030
|
-
patchId: z.string().regex(/^[0-9a-f]{40}$/u).optional(),
|
|
1031
|
-
profilePath: z.string().min(1),
|
|
1032
|
-
projectKey: z.string().min(1),
|
|
1033
|
-
repository: z.string().min(1),
|
|
1034
|
-
schemaVersion: prVerifySchemaVersionSchema,
|
|
1035
|
-
startedAt: z.iso.datetime(),
|
|
1036
|
-
verificationCommands: z.array(z.object({
|
|
1037
|
-
command: z.string().min(1),
|
|
1038
|
-
description: z.string().min(1),
|
|
1039
|
-
impactTarget: z.string().min(1).optional(),
|
|
1040
|
-
name: z.string().min(1),
|
|
1041
|
-
scope: z.enum([
|
|
1042
|
-
"always",
|
|
1043
|
-
"docs-only",
|
|
1044
|
-
"trivial",
|
|
1045
|
-
"full"
|
|
1046
|
-
])
|
|
1047
|
-
}))
|
|
1048
|
-
}).superRefine(assertPrVerifyProofRules);
|
|
1049
718
|
const PR_REVIEW_SCHEMA_VERSION = 2;
|
|
1050
719
|
const PR_REVIEW_FINDING_PROVENANCE_VERSION = 1;
|
|
1051
720
|
const nonBlankString = z.string().refine((value) => value.trim().length > 0, { message: "must not be blank" });
|
|
@@ -1519,4 +1188,4 @@ const prReadyProofSchema = z.object({
|
|
|
1519
1188
|
for (const issue of blockedReasonIssues({ ...proof })) context.addIssue(issue);
|
|
1520
1189
|
});
|
|
1521
1190
|
//#endregion
|
|
1522
|
-
export { BLOCKED_REASONS_MAX, BLOCKED_REASON_DETAIL_MAX, BOUNDARY_CHECK_SCHEMA_VERSION, BOUNDARY_REVIEW_PROOF_KIND, EVIDENCE_ENVELOPE_SCHEMA_VERSION, PR_READY_SCHEMA_VERSION, PR_REVIEW_SCHEMA_VERSION,
|
|
1191
|
+
export { BLOCKED_REASONS_MAX, BLOCKED_REASON_DETAIL_MAX, BOUNDARY_CHECK_SCHEMA_VERSION, BOUNDARY_REVIEW_PROOF_KIND, EVIDENCE_ENVELOPE_SCHEMA_VERSION, PR_READY_SCHEMA_VERSION, PR_REVIEW_SCHEMA_VERSION, SUPPORTED_CLOSEOUT_SCHEMA_VERSIONS, SUPPORTED_PR_READY_SCHEMA_VERSIONS, SUPPORTED_PR_VERIFY_SCHEMA_VERSIONS, blockedReasonSchema, blockedReasonsSchema, boundaryCheckProofSchema, boundaryReviewProofSchema, closeoutArtifactSchema, evidenceEnvelopeSchema, prReadyProofSchema, prReviewProofSchema, prVerifyProofSchema, waivedDemandSchema };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patronage/software-factory",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.16",
|
|
4
4
|
"description": "Shared Patronage software factory CLI and project-profile validation tools",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"picomatch": "^4.0.5",
|
|
38
38
|
"yaml": "^2.9.0",
|
|
39
39
|
"zod": "4.4.3",
|
|
40
|
-
"@patronage/factory-ci": "1.0.0-alpha.
|
|
40
|
+
"@patronage/factory-ci": "1.0.0-alpha.16"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "24.13.3",
|
|
@@ -62,7 +62,8 @@
|
|
|
62
62
|
"prebuild": "bash ../scripts/ensure-worktree-bootstrap.sh",
|
|
63
63
|
"build": "tsdown",
|
|
64
64
|
"boundaries": "node scripts/package-boundaries.mjs",
|
|
65
|
-
"api:check": "pnpm build &&
|
|
65
|
+
"api:check": "pnpm build && pnpm api:check:prepared",
|
|
66
|
+
"api:check:prepared": "node scripts/public-api-surface.mjs",
|
|
66
67
|
"precli": "bash ../scripts/ensure-worktree-bootstrap.sh",
|
|
67
68
|
"cli": "tsx src/index.ts",
|
|
68
69
|
"cleanup:check": "knip --files --dependencies",
|