@patronage/factory-ci 0.2.1 → 1.0.0-alpha.13

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/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { StdioOptions } from "node:child_process";
2
+
1
3
  //#region src/actions.d.ts
2
4
  /**
3
5
  * The GitHub Actions this family's workflow generators use, pinned to full
@@ -52,6 +54,21 @@ declare const NODE_PNPM_ACTION_FAMILY_NODE24: {
52
54
  };
53
55
  };
54
56
  //#endregion
57
+ //#region src/candidate-lifecycle.d.ts
58
+ /**
59
+ * Pull-request events that can create or refresh a Factory candidate run.
60
+ *
61
+ * GitHub's draft boolean remains the lifecycle authority: these triggers make
62
+ * a run visible, while `factoryCandidateOrPushCondition` keeps substantive
63
+ * jobs idle until the pull request is a candidate.
64
+ */
65
+ declare const FACTORY_CANDIDATE_PULL_REQUEST_TYPES: readonly ["opened", "synchronize", "reopened", "ready_for_review"];
66
+ /**
67
+ * Build a GitHub Actions job condition for candidate PRs and merge-target
68
+ * pushes. The caller owns triggers, jobs, runners, permissions, and topology.
69
+ */
70
+ declare const factoryCandidateOrPushCondition: (candidateCondition?: string) => string;
71
+ //#endregion
55
72
  //#region src/bundle-alchemy-entry.d.ts
56
73
  interface BundleAlchemyEntryOptions {
57
74
  /** The Alchemy entry to bundle, e.g. `alchemy.run.ts`. */
@@ -60,6 +77,18 @@ interface BundleAlchemyEntryOptions {
60
77
  readonly outfile: string;
61
78
  /** esbuild's working directory; also the base for relative paths. */
62
79
  readonly absWorkingDir?: string;
80
+ /**
81
+ * Import specifiers to rewrite before resolution, passed straight to
82
+ * esbuild's `alias`. Substitution happens before the `packages` and
83
+ * `external` decisions, so an aliased bare specifier is inlined even under
84
+ * `packages: "external"`. Values are resolved the way esbuild resolves any
85
+ * import, so give absolute paths or package names — which aliases a repo
86
+ * needs is the consumer's policy and this package bakes in none. A key on
87
+ * `alchemy`, `effect`, or a subpath of either is rejected outright, and the
88
+ * finished bundle is checked for files from those packages however they were
89
+ * reached.
90
+ */
91
+ readonly alias?: Readonly<Record<string, string>>;
63
92
  /**
64
93
  * `"external"` (default) leaves every bare import outside the entry's own
65
94
  * source graph to Node's resolver at run time — the entry's TypeScript is
@@ -134,12 +163,17 @@ declare const localPreviewStage: (options: LocalPreviewStageOptions) => LocalPre
134
163
  //#endregion
135
164
  //#region src/factory-workflow.d.ts
136
165
  interface WorkflowStep {
166
+ readonly continueOnError?: boolean;
167
+ readonly env?: Readonly<Record<string, string>>;
168
+ readonly id?: string;
169
+ readonly if?: string;
137
170
  readonly name: string;
138
171
  readonly uses?: string;
139
172
  readonly with?: Readonly<Record<string, string>>;
140
173
  readonly run?: string;
141
174
  }
142
175
  interface CheckoutStepOptions {
176
+ readonly fetchDepth?: number;
143
177
  readonly name?: string;
144
178
  readonly ref?: string;
145
179
  }
@@ -194,6 +228,59 @@ interface FactoryWorkflowArtifact<Additional extends Readonly<Record<string, Pin
194
228
  */
195
229
  declare const factoryWorkflow: <const Additional extends Readonly<Record<string, PinnedAction>> = Record<never, never>>(options: FactoryWorkflowOptions<Additional>) => FactoryWorkflowArtifact<Additional>;
196
230
  //#endregion
231
+ //#region src/github-app-token.d.ts
232
+ /**
233
+ * What a consumer must know to mint: the app id, where the private key is, and
234
+ * — when it has been recorded — which installation to mint against.
235
+ *
236
+ * `installationId` is optional because the installation is discoverable from
237
+ * the repository. `privateKeyPath` is a path rather than key material so no
238
+ * consumer has to hold a secret in memory to call this, and so the key-path
239
+ * convention stays the consumer's.
240
+ */
241
+ interface GithubAppCredentials {
242
+ appId: number | string;
243
+ installationId?: number;
244
+ privateKeyPath: string;
245
+ }
246
+ /** Carries the HTTP status so a caller can tell a retryable failure apart. */
247
+ declare class GitHubApiError extends Error {
248
+ readonly status: number;
249
+ constructor(status: number, statusText: string);
250
+ }
251
+ interface GithubAppTokenOptions {
252
+ /** Injectable `fetch` (tests, or a caller with its own instrumented one). */
253
+ fetch?: typeof fetch;
254
+ /** Wall clock in milliseconds; only the JWT's validity window uses it. */
255
+ now?: () => number;
256
+ /** Injectable key read, so a caller can hold the PEM itself if it must. */
257
+ readPrivateKey?: (privateKeyPath: string) => Buffer | string;
258
+ /** Per-request timeout; defaults to five seconds. */
259
+ timeoutMs?: number;
260
+ }
261
+ /**
262
+ * The signed app JWT GitHub accepts as `Authorization: Bearer` for the App
263
+ * endpoints. `iss` is stringified because GitHub accepts either spelling and a
264
+ * numeric app id must not depend on JSON's number formatting.
265
+ *
266
+ * The signature is produced from the key on disk and returned; the key
267
+ * material itself never leaves this call.
268
+ */
269
+ declare const githubAppJwt: (credentials: GithubAppCredentials, options?: Pick<GithubAppTokenOptions, "now" | "readPrivateKey">) => string;
270
+ /**
271
+ * Mint an installation access token for one repository.
272
+ *
273
+ * When the credentials omit `installationId`, the installation is discovered
274
+ * from the repository first — the same call every consumer had written for
275
+ * itself. Nothing is cached: the token is returned to the caller and this
276
+ * module keeps no copy.
277
+ */
278
+ declare const mintInstallationToken: (input: {
279
+ credentials: GithubAppCredentials;
280
+ owner: string;
281
+ repo: string;
282
+ }, options?: GithubAppTokenOptions) => Promise<string>;
283
+ //#endregion
197
284
  //#region src/execute-alchemy-entry.d.ts
198
285
  interface ExecuteAlchemyEntryOptions {
199
286
  /**
@@ -257,6 +344,13 @@ declare const FACTORY_PROOF_GATE_CHECK_NAME = "patronage-factory/pr-verify";
257
344
  declare const FACTORY_PROOF_GATE_APP_ID = "4314840";
258
345
  /** Step id the guard condition refers to. */
259
346
  declare const FACTORY_PROOF_GATE_STEP_ID = "factory-proof";
347
+ /**
348
+ * Human-visible name of the gate step as the Actions jobs API serves it. A
349
+ * read-only run analyzer (`psf ci:analyze`, #647) matches on this name to
350
+ * classify a run as proof-reuse versus full fallback, so it is exported from
351
+ * exactly the module that writes it — matching on a re-typed copy would drift.
352
+ */
353
+ declare const FACTORY_PROOF_GATE_STEP_NAME = "Check for factory proof of this head";
260
354
  /**
261
355
  * The shell the gate runs under, and it is a correctness requirement rather
262
356
  * than a preference.
@@ -294,6 +388,8 @@ declare const FACTORY_PROOF_GATE_REASON_OUTPUT = "reason";
294
388
  * proof that executed every command the surface requires is reusable.
295
389
  */
296
390
  declare const FACTORY_PROOF_GATE_MODE_OUTPUT = "mode";
391
+ /** Exact Checks API URL of the proof generation selected by the gate. */
392
+ declare const FACTORY_PROOF_GATE_SOURCE_URL_OUTPUT = "source-check-url";
297
393
  /**
298
394
  * Guard for every step the gate protects. Deliberately `!= 'true'` and not
299
395
  * `== 'false'`: an unset, empty, or garbled output must run the suite.
@@ -314,7 +410,8 @@ declare const FACTORY_PROOF_GATE_IF = "github.event_name == 'pull_request'";
314
410
  * - `pending` the newest generation had not completed when this job read it
315
411
  * - `failed` the newest generation records no pass
316
412
  * - `unreadable` it passed but carries no binding for this repository and head
317
- * - `incomplete` it passed but did not execute every required command
413
+ * - `incomplete` its executed plus stamp-authorized released commands do not
414
+ * cover every required command
318
415
  * - `ambiguous` two newest generations share the greatest start time
319
416
  * - `error` the gate could not reach a decision (fail open)
320
417
  *
@@ -350,6 +447,34 @@ interface ProofReuseCommand {
350
447
  * must refuse instead.
351
448
  */
352
449
  declare const proofReuseRequiredCommands: (commands: readonly ProofReuseCommand[]) => readonly string[] | undefined;
450
+ /**
451
+ * Resolve command *identities* to their `ProofReuseCommand` objects (ADR
452
+ * 0021).
453
+ *
454
+ * Selection by name is deliberately a consumer decision (proof-surfaces.ts in
455
+ * software-factory-hq, the `paitronage:verify` filter in paitronage's
456
+ * `verify.ts`): only the consumer knows which named commands a guarded
457
+ * surface requires. What both of those implementations independently
458
+ * hand-rolled is the same lookup — find each name in the profile's command
459
+ * catalog, and refuse to silently shrink the required set when a name has no
460
+ * entry. That lookup is what this function is: the mechanic, not the
461
+ * selection.
462
+ *
463
+ * Throwing at generation time (rather than returning `undefined` or an empty
464
+ * array) is deliberate: a name with no catalog entry is a mistake in the
465
+ * generator source, not a runtime condition a consumer should have to check
466
+ * for, and a required set that quietly loses an entry is exactly what makes a
467
+ * passing proof trivially "covering".
468
+ *
469
+ * `selectionLabel` names the failure, nothing else: it is not part of the
470
+ * selection this function resolves, only prose a consumer supplies for its
471
+ * own thrown error (e.g. HQ's surface name, "core" or "docs"). The message
472
+ * deliberately says "the profile's command catalog" rather than naming
473
+ * `software-factory.profile.json`: a fleet-generic library must not assume
474
+ * every consumer's catalog is that exact file, so this wording differs
475
+ * on purpose from the HQ-local message it replaced.
476
+ */
477
+ declare const resolveProofReuseCommands: (catalog: readonly ProofReuseCommand[], names: readonly string[], selectionLabel?: string) => readonly ProofReuseCommand[];
353
478
  interface FactoryProofGateOptions {
354
479
  /**
355
480
  * The profile command objects this guarded surface selected. A consumer with
@@ -445,4 +570,399 @@ declare const proofReuseCoverage: ({
445
570
  /** `proofReuseCoverage`, as a build failure. */
446
571
  declare const assertProofReuseCoverage: (input: ProofReuseCoverageInput) => ProofReuseCoverageReport;
447
572
  //#endregion
448
- export { type BundleAlchemyEntryOptions, type CheckoutStepOptions, type ExecuteAlchemyEntryOptions, type ExecuteAlchemyEntryResult, FACTORY_PROOF_GATE_APP_ID, FACTORY_PROOF_GATE_CHECK_NAME, FACTORY_PROOF_GATE_GUARD, FACTORY_PROOF_GATE_IF, FACTORY_PROOF_GATE_MODE_OUTPUT, FACTORY_PROOF_GATE_OUTPUT, FACTORY_PROOF_GATE_REASONS, FACTORY_PROOF_GATE_REASON_OUTPUT, FACTORY_PROOF_GATE_SHELL, FACTORY_PROOF_GATE_STEP_ID, type FactoryProofGateOptions, type FactoryProofGateReason, type FactoryProofGateStep, type FactoryWorkflowArtifact, type FactoryWorkflowOptions, type FactoryWorkflowSetupOptions, type InstallStepOptions, type LocalPreviewStage, type LocalPreviewStageOptions, NODE_PNPM_ACTION_FAMILY_NODE24, type NodePnpmActionFamily, type ParseLocalPreviewStageExpected, type ParsedLocalPreviewStage, type PinnedAction, type ProofReuseCommand, type ProofReuseCoverageInput, type ProofReuseCoverageReport, type SetupNodeStepOptions, type WorkflowStep, assertProofReuseCoverage, bundleAlchemyEntry, executeAlchemyEntry, factoryProofGateScript, factoryProofGateStep, factoryWorkflow, isLocalPreviewStage, localPreviewStage, parseLocalPreviewStage, proofReuseCoverage, proofReuseRequiredCommands };
573
+ //#region src/proof-reuse-presentation.d.ts
574
+ /** Generic timing and presentation steps around the proof-reuse gate (#652). */
575
+ declare const FACTORY_PROOF_TIMING_STEP_ID = "ci-timing";
576
+ declare const FACTORY_PROOF_TIMING_START_STEP_NAME = "Start CI timing";
577
+ declare const FACTORY_PROOF_TIMING_SUMMARY_STEP_NAME = "Record proof-reuse timing";
578
+ interface FactoryProofReusePresentationOptions {
579
+ /** Human-readable name of the guarded suite. */
580
+ readonly surface: string;
581
+ }
582
+ interface FactoryProofTimingStartStep {
583
+ readonly continueOnError: true;
584
+ readonly id: typeof FACTORY_PROOF_TIMING_STEP_ID;
585
+ readonly name: typeof FACTORY_PROOF_TIMING_START_STEP_NAME;
586
+ readonly run: string;
587
+ }
588
+ declare const factoryProofTimingStartStep: () => FactoryProofTimingStartStep;
589
+ declare const factoryProofReuseSummaryScript: ({
590
+ surface
591
+ }: FactoryProofReusePresentationOptions) => string;
592
+ interface FactoryProofReuseSummaryStep {
593
+ readonly continueOnError: true;
594
+ readonly env: Readonly<Record<string, string>>;
595
+ readonly if: "always()";
596
+ readonly name: typeof FACTORY_PROOF_TIMING_SUMMARY_STEP_NAME;
597
+ readonly run: string;
598
+ }
599
+ declare const factoryProofReuseSummaryStep: (options: FactoryProofReusePresentationOptions) => FactoryProofReuseSummaryStep;
600
+ //#endregion
601
+ //#region src/production-impact-workflow.d.ts
602
+ declare const FACTORY_PRODUCTION_IMPACT_STEP_ID = "production_impact";
603
+ declare const FACTORY_PRODUCTION_IMPACT_DECISION_OUTPUT = "decision";
604
+ declare const FACTORY_PRODUCTION_IMPACT_BASIS_OUTPUT = "basis";
605
+ declare const FACTORY_PRODUCTION_IMPACT_UNSUBSCRIBED_OUTPUT = "unsubscribed_paths";
606
+ /** Stable GitHub-output key for one declared target. */
607
+ declare const productionImpactTargetOutput: (targetName: string) => string;
608
+ interface FactoryProductionImpactWorkflowOptions {
609
+ readonly after?: string;
610
+ readonly before?: string;
611
+ readonly cli?: string;
612
+ readonly profilePath?: string;
613
+ readonly targets: readonly string[];
614
+ }
615
+ interface FactoryProductionImpactWorkflow {
616
+ /** Outputs for a caller-owned decision job that subsequent jobs may consume. */
617
+ readonly decisionJobOutputs: Readonly<Record<string, string>>;
618
+ readonly decisionStep: WorkflowStep;
619
+ /** A fail-open condition: only an explicit usable withdrawal skips work. */
620
+ readonly demandedIf: (targetName: string, decisionJob?: string) => string;
621
+ readonly targetOutputs: Readonly<Record<string, string>>;
622
+ }
623
+ /**
624
+ * Generate the small factory-owned decision seam for a consumer production
625
+ * workflow. Consumers retain jobs, deploy commands, credentials, topology,
626
+ * and convergence checks; this artifact supplies only the decision step and
627
+ * per-target withdrawal conditions.
628
+ */
629
+ declare const factoryProductionImpactWorkflow: (options: FactoryProductionImpactWorkflowOptions) => FactoryProductionImpactWorkflow;
630
+ //#endregion
631
+ //#region src/push-identity-workflow.d.ts
632
+ declare const FACTORY_PUSH_IDENTITY_SCHEMA_VERSION = 1;
633
+ declare const FACTORY_PUSH_IDENTITY_ARTIFACT_PREFIX = "factory-push-identity";
634
+ declare const FACTORY_PUSH_IDENTITY_RECORD_STEP_ID = "factory_push_identity_record";
635
+ declare const FACTORY_PUSH_IDENTITY_LOOKUP_STEP_ID = "factory_push_identity_lookup";
636
+ declare const FACTORY_PUSH_IDENTITY_DOWNLOAD_STEP_ID = "factory_push_identity_download";
637
+ declare const FACTORY_PUSH_IDENTITY_CHECKOUT_STEP_ID = "factory_push_identity_checkout";
638
+ declare const FACTORY_PUSH_IDENTITY_VALIDATE_STEP_ID = "factory_push_identity";
639
+ /** Versioned document uploaded by a push-triggered verification run. */
640
+ interface FactoryPushIdentityEnvelope {
641
+ readonly after: string;
642
+ readonly before: string;
643
+ readonly repository: string;
644
+ readonly runId: string;
645
+ readonly schemaVersion: typeof FACTORY_PUSH_IDENTITY_SCHEMA_VERSION;
646
+ }
647
+ type FactoryPushIdentityDisposition = "refused" | "usable";
648
+ interface FactoryPushIdentityProducerOptions {
649
+ /** Explicit caller-owned upload-artifact pin from its workflow artifact. */
650
+ readonly uploadArtifact: PinnedAction;
651
+ /** Optional consumer trigger policy combined with the required push event. */
652
+ readonly if?: string;
653
+ }
654
+ interface FactoryPushIdentityProducer {
655
+ readonly artifactName: string;
656
+ readonly steps: readonly WorkflowStep[];
657
+ }
658
+ /**
659
+ * Record and upload the exact push-event identity without changing the Verify
660
+ * result when transport is unavailable. Place these steps after verification.
661
+ */
662
+ declare const factoryPushIdentityProducer: (options: FactoryPushIdentityProducerOptions) => FactoryPushIdentityProducer;
663
+ interface FactoryPushIdentityConsumer {
664
+ /** Job outputs suitable for a caller-owned decision job and telemetry. */
665
+ readonly outputs: Readonly<Record<"after" | "before" | "disposition" | "provenance" | "reason", string>>;
666
+ readonly requiredPermissions: Readonly<{
667
+ actions: "read";
668
+ contents: "read";
669
+ }>;
670
+ readonly steps: readonly WorkflowStep[];
671
+ /** Run classification only when exact identity transport is usable. */
672
+ readonly usableIf: string;
673
+ }
674
+ interface FactoryPushIdentityConsumerOptions {
675
+ /** Explicit caller-owned checkout pin from its action family. */
676
+ readonly checkout: PinnedAction;
677
+ /** Explicit caller-owned download-artifact pin from its workflow artifact. */
678
+ readonly downloadArtifact: PinnedAction;
679
+ }
680
+ /**
681
+ * Download and validate identity from exactly the triggering workflow run in
682
+ * the current repository. Every failure becomes a typed refusal; consumers
683
+ * retain deploy policy, credentials, commands, topology, and convergence.
684
+ */
685
+ declare const factoryPushIdentityConsumer: (options: FactoryPushIdentityConsumerOptions) => FactoryPushIdentityConsumer;
686
+ //#endregion
687
+ //#region src/vitest-profile.d.ts
688
+ /** Schema version of the emitted profile document. */
689
+ declare const VITEST_PROFILE_SCHEMA_VERSION = 1;
690
+ /** `tool` discriminator every emitted profile carries. */
691
+ declare const VITEST_PROFILE_TOOL = "factory-ci-vitest-profile";
692
+ /** Per-assertion status as Vitest's JSON reporter spells it. */
693
+ type VitestTestStatus = "disabled" | "failed" | "passed" | "pending" | "skipped" | "todo";
694
+ /** The subset of Vitest's `--reporter=json` document this module reads. */
695
+ interface VitestJsonReport {
696
+ numFailedTests: number;
697
+ numPassedTests: number;
698
+ numPendingTests: number;
699
+ numTodoTests: number;
700
+ numTotalTests: number;
701
+ numTotalTestSuites: number;
702
+ success: boolean;
703
+ testResults: {
704
+ assertionResults: {
705
+ duration?: number | null;
706
+ fullName: string;
707
+ status: VitestTestStatus;
708
+ }[];
709
+ endTime: number;
710
+ name: string;
711
+ startTime: number;
712
+ status: "failed" | "passed";
713
+ }[];
714
+ }
715
+ /** Duration statistics over one population of samples, in milliseconds. */
716
+ interface VitestProfileDurationSummary {
717
+ maximum: number;
718
+ mean: number;
719
+ median: number;
720
+ minimum: number;
721
+ }
722
+ /**
723
+ * The machine a sample ran on, plus the commit it measured. `cpuModel` is the
724
+ * field that makes two runs comparable at all: hosted runner pools mix silicon
725
+ * behind one label.
726
+ */
727
+ interface VitestProfileEnvironment {
728
+ arch: string;
729
+ availableParallelism: number;
730
+ cpuCount: number;
731
+ cpuModel: string | null;
732
+ gitDirty: boolean | null;
733
+ gitHead: string | null;
734
+ node: string;
735
+ osRelease: string;
736
+ platform: string;
737
+ totalMemoryBytes: number;
738
+ vitest: string;
739
+ }
740
+ /** One complete Vitest run inside a profile. */
741
+ interface VitestProfileSample {
742
+ counts: {
743
+ failed: number;
744
+ passed: number;
745
+ pending: number;
746
+ suites: number;
747
+ tests: number;
748
+ todo: number;
749
+ } | null;
750
+ durationMs: number;
751
+ endedAt: string;
752
+ exitCode: number;
753
+ failure: string | null;
754
+ files: {
755
+ durationMs: number;
756
+ path: string;
757
+ status: "failed" | "passed";
758
+ }[];
759
+ reportAvailable: boolean;
760
+ sample: number;
761
+ startedAt: string;
762
+ tests: {
763
+ durationMs: number;
764
+ file: string;
765
+ name: string;
766
+ status: VitestTestStatus;
767
+ }[];
768
+ }
769
+ /** The emitted profile document. */
770
+ interface VitestProfile {
771
+ command: string[];
772
+ endedAt: string;
773
+ environment: VitestProfileEnvironment;
774
+ options: {
775
+ maxWorkers: number;
776
+ samples: number;
777
+ slowLimit: number;
778
+ };
779
+ rawReportDirectory: string;
780
+ runs: VitestProfileSample[];
781
+ schemaVersion: typeof VITEST_PROFILE_SCHEMA_VERSION;
782
+ startedAt: string;
783
+ summary: {
784
+ durationMs: VitestProfileDurationSummary;
785
+ slowFiles: {
786
+ durationMs: VitestProfileDurationSummary;
787
+ path: string;
788
+ samples: number;
789
+ }[];
790
+ slowTests: {
791
+ durationMs: VitestProfileDurationSummary;
792
+ file: string;
793
+ name: string;
794
+ samples: number;
795
+ }[];
796
+ };
797
+ tool: typeof VITEST_PROFILE_TOOL;
798
+ }
799
+ /** What a caller must decide before a profile can run. */
800
+ interface VitestProfileOptions {
801
+ /** Directory Vitest runs in; file paths are recorded relative to it. */
802
+ cwd: string;
803
+ /** Directory whose Git state is recorded. Defaults to `cwd`. */
804
+ gitDirectory?: string;
805
+ /** Vitest `--maxWorkers` for every sample. */
806
+ maxWorkers: number;
807
+ /** Absolute path of the profile document to write. */
808
+ outputPath: string;
809
+ /** Called before each sample starts. */
810
+ onSampleStart?: (input: {
811
+ maxWorkers: number;
812
+ sample: number;
813
+ samples: number;
814
+ }) => void;
815
+ /** Called after each sample is normalized and persisted. */
816
+ onSampleComplete?: (input: {
817
+ result: VitestProfileSample;
818
+ sample: number;
819
+ samples: number;
820
+ }) => void;
821
+ /** How many serial runs to take. */
822
+ samples: number;
823
+ /** How many slow files and slow tests to keep in the summary. */
824
+ slowLimit: number;
825
+ /**
826
+ * `stdio` for the Vitest child. Defaults to `"inherit"`, which is what a
827
+ * caller printing progress to a terminal wants; a caller whose own stdout is
828
+ * structured passes `"ignore"` to silence the run.
829
+ */
830
+ stdio?: StdioOptions;
831
+ }
832
+ /** Outcome of one Vitest invocation, as `runSample` reports it. */
833
+ interface VitestProfileSampleExecution {
834
+ durationMs: number;
835
+ exitCode: number;
836
+ failure?: string | null;
837
+ report: VitestJsonReport | null;
838
+ }
839
+ /** Injectable seams; production passes none of them. */
840
+ interface VitestProfileDependencies {
841
+ now?: () => Date;
842
+ runSample?: (input: {
843
+ cwd: string;
844
+ maxWorkers: number;
845
+ reportPath: string;
846
+ sample: number;
847
+ stdio: StdioOptions;
848
+ }) => Promise<VitestProfileSampleExecution>;
849
+ writeResult?: (outputPath: string, profile: VitestProfile) => Promise<void>;
850
+ }
851
+ /**
852
+ * A sample failed. The partial profile is already on disk; `exitCode` is the
853
+ * status a caller should exit with.
854
+ */
855
+ declare class VitestProfileError extends Error {
856
+ readonly exitCode: number;
857
+ constructor(message: string, exitCode: number);
858
+ }
859
+ /**
860
+ * Fold one Vitest JSON report into a profile sample: file and test timings,
861
+ * both sorted slowest first, plus the run's counts. A missing report (crash,
862
+ * timeout, unwritable output) yields a sample with `reportAvailable: false`
863
+ * rather than nothing at all.
864
+ */
865
+ declare const normalizeVitestProfileSample: (report: VitestJsonReport | null, input: {
866
+ cwd: string;
867
+ durationMs: number;
868
+ endedAt: Date;
869
+ exitCode: number;
870
+ failure?: string | null;
871
+ sample: number;
872
+ startedAt: Date;
873
+ }) => VitestProfileSample;
874
+ /**
875
+ * Record the machine and commit a profile was taken on. Vitest's version is
876
+ * resolved from `cwd`, so it is the consumer's Vitest and not this package's.
877
+ * Git failures degrade to `null` — an artifact from a tarball checkout is still
878
+ * a usable measurement.
879
+ */
880
+ declare const captureVitestProfileEnvironment: (options: {
881
+ cwd: string;
882
+ gitDirectory?: string;
883
+ }) => Promise<VitestProfileEnvironment>;
884
+ /**
885
+ * Write a profile document atomically: a partial file must never be readable
886
+ * as a complete measurement, and the profile is rewritten after every sample.
887
+ */
888
+ declare const writeVitestProfile: (outputPath: string, profile: VitestProfile) => Promise<void>;
889
+ /**
890
+ * Take `samples` serial Vitest runs at one worker count and persist the profile
891
+ * after each one. Samples never overlap: concurrent runs would measure CPU and
892
+ * I/O contention instead of the worker count under test. A failing sample
893
+ * throws `VitestProfileError` with the partial profile already written.
894
+ */
895
+ declare const runVitestProfile: (options: VitestProfileOptions, dependencies?: VitestProfileDependencies) => Promise<VitestProfile>;
896
+ //#endregion
897
+ //#region src/vitest-profile-reader.d.ts
898
+ /**
899
+ * Outcome of reading one candidate document. `unrecognized` carries the first
900
+ * reason the document failed — an analyzer reports it verbatim rather than
901
+ * treating an unreadable profile as an empty one.
902
+ */
903
+ type VitestProfileReadResult = {
904
+ kind: "profile";
905
+ profile: VitestProfile;
906
+ } | {
907
+ kind: "unrecognized";
908
+ reason: string;
909
+ };
910
+ /**
911
+ * Check one parsed JSON document against the profile contract the writer
912
+ * emits. Returns the typed profile on success and the first mismatch reason
913
+ * otherwise — never a partially-usable value.
914
+ */
915
+ declare const readVitestProfileDocument: (value: unknown) => VitestProfileReadResult;
916
+ //#endregion
917
+ //#region src/workflow-shell-lint.d.ts
918
+ /**
919
+ * Parse-check the shell embedded in generated workflow YAML (#376).
920
+ *
921
+ * The generated-workflow lint validates YAML shape. It never parses the shell
922
+ * inside a `run:` block, so a script that cannot execute at all — a stray
923
+ * `fi`, an unclosed quote, a `then` with no `if` — passes every local check
924
+ * and only fails when the runner reaches it. That is not hypothetical:
925
+ * paitronage#1090 shipped a stray `fi` into a generated workflow and made
926
+ * every automated preview destroy a parse-time no-op for five days.
927
+ *
928
+ * `bash -n` is the whole control: it parses without executing. It lives here,
929
+ * once, rather than as a contract test in each consumer, because a guard
930
+ * copied per repository is a guard that exists in some of them.
931
+ */
932
+ /** One `run:` block that bash refuses to parse. */
933
+ interface WorkflowShellParseFailure {
934
+ /** The step's `name:` when the YAML carried one. */
935
+ readonly step?: string;
936
+ /** The script as bash saw it, expressions already neutralized. */
937
+ readonly script: string;
938
+ /** What bash said. */
939
+ readonly stderr: string;
940
+ }
941
+ /** A `run:` block lifted out of generated YAML, with its step's `shell:`. */
942
+ interface RunBlock {
943
+ readonly script: string;
944
+ readonly shell?: string;
945
+ readonly step?: string;
946
+ }
947
+ /**
948
+ * Every `run:` block in a generated workflow, paired with the `shell:` its
949
+ * step declares.
950
+ *
951
+ * Deliberately a scanner over the emitted text and not a YAML parse: this
952
+ * package takes no dependency it does not need, and the emitted shape is one
953
+ * generator's output, not arbitrary YAML. It reads both block scalars
954
+ * (`run: |-`) and inline scripts.
955
+ */
956
+ declare const workflowRunBlocks: (yaml: string) => RunBlock[];
957
+ /** Every `run:` block its interpreter refuses to parse. Empty means sound. */
958
+ declare const workflowShellParseFailures: (yaml: string) => WorkflowShellParseFailure[];
959
+ /**
960
+ * Fail the generated-workflow lint when any embedded `run:` block is not
961
+ * parseable bash. Call it on the YAML a generator is about to write, so the
962
+ * defect is caught at generation rather than by the runner.
963
+ */
964
+ declare const assertWorkflowShellParses: (yaml: string, options: {
965
+ readonly source: string;
966
+ }) => void;
967
+ //#endregion
968
+ export { type BundleAlchemyEntryOptions, type CheckoutStepOptions, type ExecuteAlchemyEntryOptions, type ExecuteAlchemyEntryResult, FACTORY_CANDIDATE_PULL_REQUEST_TYPES, FACTORY_PRODUCTION_IMPACT_BASIS_OUTPUT, FACTORY_PRODUCTION_IMPACT_DECISION_OUTPUT, FACTORY_PRODUCTION_IMPACT_STEP_ID, FACTORY_PRODUCTION_IMPACT_UNSUBSCRIBED_OUTPUT, FACTORY_PROOF_GATE_APP_ID, FACTORY_PROOF_GATE_CHECK_NAME, FACTORY_PROOF_GATE_GUARD, FACTORY_PROOF_GATE_IF, FACTORY_PROOF_GATE_MODE_OUTPUT, FACTORY_PROOF_GATE_OUTPUT, FACTORY_PROOF_GATE_REASONS, FACTORY_PROOF_GATE_REASON_OUTPUT, FACTORY_PROOF_GATE_SHELL, FACTORY_PROOF_GATE_SOURCE_URL_OUTPUT, FACTORY_PROOF_GATE_STEP_ID, FACTORY_PROOF_GATE_STEP_NAME, FACTORY_PROOF_TIMING_START_STEP_NAME, FACTORY_PROOF_TIMING_STEP_ID, FACTORY_PROOF_TIMING_SUMMARY_STEP_NAME, FACTORY_PUSH_IDENTITY_ARTIFACT_PREFIX, FACTORY_PUSH_IDENTITY_CHECKOUT_STEP_ID, FACTORY_PUSH_IDENTITY_DOWNLOAD_STEP_ID, FACTORY_PUSH_IDENTITY_LOOKUP_STEP_ID, FACTORY_PUSH_IDENTITY_RECORD_STEP_ID, FACTORY_PUSH_IDENTITY_SCHEMA_VERSION, FACTORY_PUSH_IDENTITY_VALIDATE_STEP_ID, type FactoryProductionImpactWorkflow, type FactoryProductionImpactWorkflowOptions, type FactoryProofGateOptions, type FactoryProofGateReason, type FactoryProofGateStep, type FactoryProofReusePresentationOptions, type FactoryProofReuseSummaryStep, type FactoryProofTimingStartStep, type FactoryPushIdentityConsumer, type FactoryPushIdentityConsumerOptions, type FactoryPushIdentityDisposition, type FactoryPushIdentityEnvelope, type FactoryPushIdentityProducer, type FactoryPushIdentityProducerOptions, type FactoryWorkflowArtifact, type FactoryWorkflowOptions, type FactoryWorkflowSetupOptions, GitHubApiError, type GithubAppCredentials, type GithubAppTokenOptions, type InstallStepOptions, type LocalPreviewStage, type LocalPreviewStageOptions, NODE_PNPM_ACTION_FAMILY_NODE24, type NodePnpmActionFamily, type ParseLocalPreviewStageExpected, type ParsedLocalPreviewStage, type PinnedAction, type ProofReuseCommand, type ProofReuseCoverageInput, type ProofReuseCoverageReport, type SetupNodeStepOptions, VITEST_PROFILE_SCHEMA_VERSION, VITEST_PROFILE_TOOL, type VitestJsonReport, type VitestProfile, type VitestProfileDependencies, type VitestProfileDurationSummary, type VitestProfileEnvironment, VitestProfileError, type VitestProfileOptions, type VitestProfileReadResult, type VitestProfileSample, type VitestProfileSampleExecution, type VitestTestStatus, type WorkflowShellParseFailure, type WorkflowStep, assertProofReuseCoverage, assertWorkflowShellParses, bundleAlchemyEntry, captureVitestProfileEnvironment, executeAlchemyEntry, factoryCandidateOrPushCondition, factoryProductionImpactWorkflow, factoryProofGateScript, factoryProofGateStep, factoryProofReuseSummaryScript, factoryProofReuseSummaryStep, factoryProofTimingStartStep, factoryPushIdentityConsumer, factoryPushIdentityProducer, factoryWorkflow, githubAppJwt, isLocalPreviewStage, localPreviewStage, mintInstallationToken, normalizeVitestProfileSample, parseLocalPreviewStage, productionImpactTargetOutput, proofReuseCoverage, proofReuseRequiredCommands, readVitestProfileDocument, resolveProofReuseCommands, runVitestProfile, workflowRunBlocks, workflowShellParseFailures, writeVitestProfile };