@patronage/software-factory 0.23.0 → 0.25.0
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 +321 -15
- package/dist/index.js +2775 -1075
- package/dist/schemas.d.ts +48 -4
- package/dist/schemas.js +230 -13
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync, rmSync } from "node:fs";
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
4
5
|
|
|
5
6
|
//#region src/github-issue-comments.d.ts
|
|
6
7
|
interface GithubIssueCommentApi {
|
|
@@ -166,6 +167,195 @@ interface HqIngestDependencies {
|
|
|
166
167
|
timeoutMs?: number;
|
|
167
168
|
transportTimeoutMs?: number;
|
|
168
169
|
}
|
|
170
|
+
/** The repository whose undelivered evidence a spool holds. */
|
|
171
|
+
interface HqSpoolRepository {
|
|
172
|
+
owner: string;
|
|
173
|
+
repo: string;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* What became of one spooled event. `delivered` and `duplicate` both mean HQ
|
|
177
|
+
* holds it (dedup is by content-addressed eventId), so the entry is removed;
|
|
178
|
+
* `rejected` and `unreachable` leave it spooled.
|
|
179
|
+
*/
|
|
180
|
+
interface HqSpoolEntryOutcome {
|
|
181
|
+
detail?: string;
|
|
182
|
+
eventId: string;
|
|
183
|
+
kind: string;
|
|
184
|
+
spool: string;
|
|
185
|
+
/**
|
|
186
|
+
* `migrated` belongs to the legacy JSONL journal only: the row was moved
|
|
187
|
+
* into the current spool without being delivered. The spool pass that runs
|
|
188
|
+
* after it in the same drain supersedes that line with a real outcome when
|
|
189
|
+
* it gets to the row; a `migrated` line that survives the run means the row
|
|
190
|
+
* is still waiting.
|
|
191
|
+
*
|
|
192
|
+
* `undeliverable` is the one terminal verdict (#445). Every other status
|
|
193
|
+
* describes a moment: HQ was unreachable, HQ refused this content today, the
|
|
194
|
+
* row moved. Retrying is meaningful for all of them. A wrong-origin entry is
|
|
195
|
+
* different in kind — it is refused here, from the entry's own bytes, with no
|
|
196
|
+
* request made, and the same bytes produce the same verdict on every future
|
|
197
|
+
* run. Leaving it spooled asks the operator to retry something that provably
|
|
198
|
+
* cannot succeed, and the count it inflates is the one doctor goes red on.
|
|
199
|
+
*/
|
|
200
|
+
status: "delivered" | "duplicate" | "migrated" | "rejected" | "undeliverable" | "unreachable";
|
|
201
|
+
}
|
|
202
|
+
interface HqSpoolFlushInput {
|
|
203
|
+
clientId: string;
|
|
204
|
+
clientSecret: string;
|
|
205
|
+
/** Repository root whose legacy `.factory-memory` spool is also drained. */
|
|
206
|
+
cwd: string;
|
|
207
|
+
/** The profile's HQ origin; entries recorded against another are refused. */
|
|
208
|
+
endpoint: string;
|
|
209
|
+
/** Operator-named spool directories; replaces the default two locations. */
|
|
210
|
+
explicitDirectories?: string[];
|
|
211
|
+
repository: HqSpoolRepository;
|
|
212
|
+
}
|
|
213
|
+
interface HqSpoolFlushSummary {
|
|
214
|
+
delivered: number;
|
|
215
|
+
duplicate: number;
|
|
216
|
+
/**
|
|
217
|
+
* The drain did not finish: the budget elapsed, or events beyond the
|
|
218
|
+
* rejected ones are still spooled. Never report an incomplete pass as a
|
|
219
|
+
* clean drain — a recovery run reads this to know whether to run again.
|
|
220
|
+
*/
|
|
221
|
+
incomplete: boolean;
|
|
222
|
+
outcomes: HqSpoolEntryOutcome[];
|
|
223
|
+
rejected: number;
|
|
224
|
+
/** Events still in the drained spools when the pass ended. */
|
|
225
|
+
remaining: number;
|
|
226
|
+
/** Locations that were read; a missing one is simply absent from the list. */
|
|
227
|
+
spools: string[];
|
|
228
|
+
/**
|
|
229
|
+
* Events dispositioned as permanently undeliverable this pass (#445). They
|
|
230
|
+
* are gone from `remaining` — that is the point — so this is the only place
|
|
231
|
+
* the run says they existed.
|
|
232
|
+
*/
|
|
233
|
+
undeliverable: number;
|
|
234
|
+
unreachable: number;
|
|
235
|
+
/** Files retained by a transport failure, counted per file. */
|
|
236
|
+
unreachableFiles: number;
|
|
237
|
+
}
|
|
238
|
+
interface HqSpoolWorkCount {
|
|
239
|
+
/**
|
|
240
|
+
* The earliest moment learned across pending spool files (their own write
|
|
241
|
+
* time) and legacy journal rows (their own `failedAt`). Absent only when
|
|
242
|
+
* `pending` is `0`, or when every timestamp source was unreadable within
|
|
243
|
+
* budget — an estimate for doctor's remediation message (#394), never a
|
|
244
|
+
* precise audit trail.
|
|
245
|
+
*/
|
|
246
|
+
oldestQueuedAt?: string;
|
|
247
|
+
/** Spooled events and replayable journals waiting in the locations below. */
|
|
248
|
+
pending: number;
|
|
249
|
+
/** Locations that exist and hold spooled work. */
|
|
250
|
+
spools: string[];
|
|
251
|
+
/**
|
|
252
|
+
* A location existed but could not be listed. The count above saw nothing
|
|
253
|
+
* there, so a caller deciding whether the drain is worth doing must treat a
|
|
254
|
+
* non-zero value as "work may be waiting" — never as an empty spool.
|
|
255
|
+
*/
|
|
256
|
+
unlistable: number;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Counts spooled work for a repository without draining it or touching a
|
|
260
|
+
* credential (#414).
|
|
261
|
+
*
|
|
262
|
+
* `hq:flush` used to resolve the HQ Access token before it ever looked at the
|
|
263
|
+
* spool, so a lane with nothing to send still paid a secret-manager round trip
|
|
264
|
+
* — and still failed, opaquely, in a sandbox that has no keychain access. The
|
|
265
|
+
* same locations `flushHqSpool` drains are inspected here, read-only: no
|
|
266
|
+
* directory is created, nothing is secured, and nothing is delivered.
|
|
267
|
+
*/
|
|
268
|
+
declare function countHqSpoolWork(input: Pick<HqSpoolFlushInput, "cwd" | "explicitDirectories" | "repository">, dependencies?: {
|
|
269
|
+
budgetMs?: number;
|
|
270
|
+
env?: NodeJS.ProcessEnv;
|
|
271
|
+
}): Promise<HqSpoolWorkCount>;
|
|
272
|
+
/** A spool under an earlier key for this repository, not the current one. */
|
|
273
|
+
interface HqSpoolOrphan {
|
|
274
|
+
/** The `hq-retry-spool` directory itself, ready to pass to `--dir`. */
|
|
275
|
+
directory: string;
|
|
276
|
+
oldestQueuedAt?: string;
|
|
277
|
+
/** Spooled events and journal rows waiting there. */
|
|
278
|
+
pending: number;
|
|
279
|
+
/**
|
|
280
|
+
* The location exists but could not be listed. As everywhere else in this
|
|
281
|
+
* inspection, unknown counts as work: a swept location nobody could read is
|
|
282
|
+
* reported, never quietly dropped as empty.
|
|
283
|
+
*/
|
|
284
|
+
unlistable: number;
|
|
285
|
+
}
|
|
286
|
+
interface HqSpoolOrphanSweep {
|
|
287
|
+
/** Only locations holding work; an empty orphan spool is not a finding. */
|
|
288
|
+
orphans: HqSpoolOrphan[];
|
|
289
|
+
root: string;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Reports spooled evidence sitting under an earlier key for *this* repository
|
|
293
|
+
* (#420).
|
|
294
|
+
*
|
|
295
|
+
* The spool is keyed by owner and repo, so a change to the segment encoding
|
|
296
|
+
* itself — which happened during #390's own development — moves the address
|
|
297
|
+
* without moving the evidence. Both readers of the spool resolve exactly one
|
|
298
|
+
* key, so the events under the old one become invisible: the drain reports
|
|
299
|
+
* success, doctor reports empty, and three real proofs sat unread until an
|
|
300
|
+
* attended recovery enumerated the tree by hand.
|
|
301
|
+
*
|
|
302
|
+
* This sweep only reports. Draining another key's events is a decision this
|
|
303
|
+
* does not make — the operator gets the location and the count, and
|
|
304
|
+
* `hq:flush --dir` remains the recovery path.
|
|
305
|
+
*
|
|
306
|
+
* **Why candidate keys and not a walk of the root (#446).** The root is shared
|
|
307
|
+
* by every factory repository on the machine, so enumerating it and calling
|
|
308
|
+
* everything that is not the current key an orphan describes another
|
|
309
|
+
* repository's ordinary, current, correct spool exactly as well as it describes
|
|
310
|
+
* this repository's obsolete one. That made doctor red in one checkout because
|
|
311
|
+
* a different repository had pending work, and told the operator to drain it —
|
|
312
|
+
* confidently prescribing the wrong action. Nothing on disk distinguishes the
|
|
313
|
+
* two cases: an unrecognised key carries no statement about who wrote it.
|
|
314
|
+
*
|
|
315
|
+
* So discovery is scoped to the keys *this* repository could plausibly have
|
|
316
|
+
* produced — the current scheme plus the earlier ones listed in
|
|
317
|
+
* `hqSpoolCandidateKeys` — and a key outside that set is never this
|
|
318
|
+
* repository's business. The #420 incident is inside it: the joined
|
|
319
|
+
* single-segment key is one of the candidates.
|
|
320
|
+
*
|
|
321
|
+
* **And a marked walk beside them (#447).** Derivation's other blind spot is a
|
|
322
|
+
* key whose *encoding* this checkout no longer produces but whose owner and
|
|
323
|
+
* repo are unchanged — the retired non-injective era being the live example.
|
|
324
|
+
* That era cannot be derived safely, because a candidate built from it can
|
|
325
|
+
* equal a different repository's current key, so #446 dropped it rather than
|
|
326
|
+
* risk the cross-repository claim again.
|
|
327
|
+
*
|
|
328
|
+
* `SPOOL_REPOSITORY_MARKER` supplies the proof that derivation could not. Every
|
|
329
|
+
* enqueue stamps its spool with the repository writing it, so the root can be
|
|
330
|
+
* walked again: a directory whose marker names *this* repository is this
|
|
331
|
+
* repository's, whatever key encoding it sits under, and a directory whose
|
|
332
|
+
* marker names another repository is never reported here. That is the
|
|
333
|
+
* discriminator #446 correctly said did not exist — it exists now because
|
|
334
|
+
* something writes it down.
|
|
335
|
+
*
|
|
336
|
+
* The two discoveries are complements, not alternatives. The walk sees only
|
|
337
|
+
* what was stamped; directories written before this shipped have no marker, and
|
|
338
|
+
* candidate keys still find those. An unmarked directory is still never
|
|
339
|
+
* reported, because it still carries no statement about who wrote it.
|
|
340
|
+
*
|
|
341
|
+
* **What this does NOT close, despite being the marker's obvious use: renames
|
|
342
|
+
* and owner changes.** A marker records the identity that was current when the
|
|
343
|
+
* directory was written, so after `patronage/old` becomes `patronage/new` the
|
|
344
|
+
* stranded directory is stamped `patronage/old` — and matching is equality
|
|
345
|
+
* against the checkout's *present* identity, which rejects it. Making that work
|
|
346
|
+
* needs an identifier that survives a rename, which neither the profile nor the
|
|
347
|
+
* marker carries today; accepting a non-matching marker instead would be
|
|
348
|
+
* guessing, which is the #446 defect wearing a new hat. #447 stays open for it.
|
|
349
|
+
*
|
|
350
|
+
* Also outside the sweep, by construction: evidence under a *different state
|
|
351
|
+
* root*, if `XDG_STATE_HOME` moves. No walk of this root can reach another one.
|
|
352
|
+
*/
|
|
353
|
+
declare function sweepHqSpoolOrphans(input: {
|
|
354
|
+
repository: HqSpoolRepository;
|
|
355
|
+
}, dependencies?: {
|
|
356
|
+
budgetMs?: number;
|
|
357
|
+
env?: NodeJS.ProcessEnv;
|
|
358
|
+
}): Promise<HqSpoolOrphanSweep>;
|
|
169
359
|
//#endregion
|
|
170
360
|
//#region src/demand-waiver.d.ts
|
|
171
361
|
declare const DEFAULT_DEMAND_WAIVER_PATH = ".factory-memory/demand-waivers.json";
|
|
@@ -264,6 +454,13 @@ declare const authorizeDemandWaiver: ({
|
|
|
264
454
|
session: string | undefined;
|
|
265
455
|
}) => AuthorizeDemandWaiverResult;
|
|
266
456
|
//#endregion
|
|
457
|
+
//#region src/blocked-reasons.d.ts
|
|
458
|
+
declare const blockedReasonSchema: z.ZodObject<{
|
|
459
|
+
code: z.ZodString;
|
|
460
|
+
detail: z.ZodString;
|
|
461
|
+
}, z.core.$strip>;
|
|
462
|
+
type BlockedReason = z.infer<typeof blockedReasonSchema>;
|
|
463
|
+
//#endregion
|
|
267
464
|
//#region src/checkout-repository.d.ts
|
|
268
465
|
interface CheckoutRepository {
|
|
269
466
|
name: string;
|
|
@@ -321,8 +518,8 @@ declare const mergeFreezeStateSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
321
518
|
generationId: z.ZodNumber;
|
|
322
519
|
headSha: z.ZodString;
|
|
323
520
|
outcome: z.ZodEnum<{
|
|
324
|
-
stale: "stale";
|
|
325
521
|
active: "active";
|
|
522
|
+
stale: "stale";
|
|
326
523
|
}>;
|
|
327
524
|
reason: z.ZodString;
|
|
328
525
|
recordedAt: z.ZodISODateTime;
|
|
@@ -1399,8 +1596,8 @@ declare const managedReadinessLedgerSchema: z.ZodObject<{
|
|
|
1399
1596
|
status: z.ZodEnum<{
|
|
1400
1597
|
blocked: "blocked";
|
|
1401
1598
|
"not-required": "not-required";
|
|
1402
|
-
current: "current";
|
|
1403
1599
|
stale: "stale";
|
|
1600
|
+
current: "current";
|
|
1404
1601
|
missing: "missing";
|
|
1405
1602
|
}>;
|
|
1406
1603
|
}, z.core.$strip>;
|
|
@@ -1412,8 +1609,8 @@ declare const managedReadinessLedgerSchema: z.ZodObject<{
|
|
|
1412
1609
|
status: z.ZodEnum<{
|
|
1413
1610
|
blocked: "blocked";
|
|
1414
1611
|
"not-required": "not-required";
|
|
1415
|
-
current: "current";
|
|
1416
1612
|
stale: "stale";
|
|
1613
|
+
current: "current";
|
|
1417
1614
|
missing: "missing";
|
|
1418
1615
|
}>;
|
|
1419
1616
|
}, z.core.$strip>>;
|
|
@@ -1430,8 +1627,8 @@ declare const managedReadinessLedgerSchema: z.ZodObject<{
|
|
|
1430
1627
|
docsOnlyDeltaAccepted: z.ZodOptional<z.ZodBoolean>;
|
|
1431
1628
|
docsOnlyVerifiedHeadSha: z.ZodOptional<z.ZodString>;
|
|
1432
1629
|
prVerify: z.ZodEnum<{
|
|
1433
|
-
passed: "passed";
|
|
1434
1630
|
stale: "stale";
|
|
1631
|
+
passed: "passed";
|
|
1435
1632
|
missing: "missing";
|
|
1436
1633
|
}>;
|
|
1437
1634
|
trivialDeltaAccepted: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -1516,7 +1713,15 @@ interface GitHubPullRequest {
|
|
|
1516
1713
|
url: string;
|
|
1517
1714
|
}
|
|
1518
1715
|
interface PrReadyProof {
|
|
1519
|
-
schemaVersion:
|
|
1716
|
+
schemaVersion: 2;
|
|
1717
|
+
/**
|
|
1718
|
+
* Why this run blocked, one entry per refusing demand (#391): `code` is the
|
|
1719
|
+
* demand key from the resolver's vocabulary, `detail` the one-sentence
|
|
1720
|
+
* refusal. Same refusals as `blockingReasons`, in the same order — the
|
|
1721
|
+
* analyzable projection of a flat string list, so a wall of blocked proofs
|
|
1722
|
+
* on one PR can be counted by cause. Absent when nothing blocked.
|
|
1723
|
+
*/
|
|
1724
|
+
blockedReasons?: BlockedReason[];
|
|
1520
1725
|
blockingReasons: string[];
|
|
1521
1726
|
humanBlockingReasons: string[];
|
|
1522
1727
|
command: "patronage-factory pr:ready";
|
|
@@ -1779,8 +1984,8 @@ declare const REVIEW_STATUS_VALUES: readonly ["not-required", "current", "stale"
|
|
|
1779
1984
|
declare const reviewStatusSchema: z.ZodEnum<{
|
|
1780
1985
|
blocked: "blocked";
|
|
1781
1986
|
"not-required": "not-required";
|
|
1782
|
-
current: "current";
|
|
1783
1987
|
stale: "stale";
|
|
1988
|
+
current: "current";
|
|
1784
1989
|
missing: "missing";
|
|
1785
1990
|
}>;
|
|
1786
1991
|
type ReviewStatus = z.infer<typeof reviewStatusSchema>;
|
|
@@ -2369,6 +2574,55 @@ type DemandWaiveAction = (args: DemandWaiveArgs) => DemandWaiver;
|
|
|
2369
2574
|
//#region src/commands/pr-merge-check.d.ts
|
|
2370
2575
|
type PrMergeCheckAction = (args: PrMergeCheckArgs) => PrMergeCheckProof;
|
|
2371
2576
|
//#endregion
|
|
2577
|
+
//#region src/hq-credentials.d.ts
|
|
2578
|
+
/**
|
|
2579
|
+
* Why a reference did not resolve. Each value names a different remedy, and
|
|
2580
|
+
* none of them can be inferred from a value-or-nothing result:
|
|
2581
|
+
*
|
|
2582
|
+
* - `resolver-missing` — no secret-manager binary on PATH.
|
|
2583
|
+
* - `resolver-blocked` — a binary exists but this session may not execute it
|
|
2584
|
+
* (a sandbox denying exec). The command belongs outside the sandbox.
|
|
2585
|
+
* - `resolver-timeout` — the probe expired: a desktop agent waiting on an
|
|
2586
|
+
* approval nobody can give here.
|
|
2587
|
+
* - `resolver-refused` — the binary ran and produced no value. Its store is
|
|
2588
|
+
* unreachable from this session (a sandbox with no keychain access) or the
|
|
2589
|
+
* reference is not readable. These two stay one status on purpose: telling
|
|
2590
|
+
* them apart would mean reading resolver output.
|
|
2591
|
+
*/
|
|
2592
|
+
type SecretResolutionFailure = "resolver-blocked" | "resolver-missing" | "resolver-refused" | "resolver-timeout";
|
|
2593
|
+
/** A structured resolution outcome. The value travels only when resolved. */
|
|
2594
|
+
type SecretResolution = {
|
|
2595
|
+
status: "resolved";
|
|
2596
|
+
value: string;
|
|
2597
|
+
} | {
|
|
2598
|
+
status: SecretResolutionFailure;
|
|
2599
|
+
};
|
|
2600
|
+
/** Resolves a secret reference. Injected so tests stay offline. */
|
|
2601
|
+
type SecretReferenceResolver = (reference: string) => SecretResolution;
|
|
2602
|
+
//#endregion
|
|
2603
|
+
//#region src/hq-flush.d.ts
|
|
2604
|
+
/**
|
|
2605
|
+
* Spool locations this run did not drain because they sit under an earlier
|
|
2606
|
+
* key for this repository. Reported, never drained: draining an older key's
|
|
2607
|
+
* events is an operator decision, made with `--dir`.
|
|
2608
|
+
*/
|
|
2609
|
+
interface HqFlushOrphans {
|
|
2610
|
+
orphans: HqSpoolOrphan[];
|
|
2611
|
+
}
|
|
2612
|
+
type HqFlushResult = (HqFlushOrphans & {
|
|
2613
|
+
reason: string;
|
|
2614
|
+
/**
|
|
2615
|
+
* Events still spooled when the command gave up. Non-zero means the
|
|
2616
|
+
* skip retained work: the exit status says so, and no caller may read
|
|
2617
|
+
* the skip as "there was nothing to do".
|
|
2618
|
+
*/
|
|
2619
|
+
retained: number;
|
|
2620
|
+
status: "skipped";
|
|
2621
|
+
}) | (HqFlushOrphans & HqSpoolFlushSummary & {
|
|
2622
|
+
endpoint: string;
|
|
2623
|
+
status: "flushed";
|
|
2624
|
+
});
|
|
2625
|
+
//#endregion
|
|
2372
2626
|
//#region src/pr-review.d.ts
|
|
2373
2627
|
interface PrReviewArgs extends LoadProjectProfileInput {
|
|
2374
2628
|
base: string;
|
|
@@ -2514,6 +2768,15 @@ interface PrPublishDependencies extends PrReadyDependencies {
|
|
|
2514
2768
|
runFollowUp?: FollowUpRunner;
|
|
2515
2769
|
runPrReady?: typeof runPrReady;
|
|
2516
2770
|
runPrReview?: (args: PrReviewArgs) => Promise<PrReviewProof>;
|
|
2771
|
+
/** Settles asynchronously scheduled sink work before the handoff drain. */
|
|
2772
|
+
awaitPendingIngest?: () => Promise<void>;
|
|
2773
|
+
/**
|
|
2774
|
+
* The handoff drain (#390). Advisory everywhere: publish reports what it
|
|
2775
|
+
* found and never changes its verdict or exit status on the result.
|
|
2776
|
+
*/
|
|
2777
|
+
flushHqSpool?: (args: {
|
|
2778
|
+
cwd: string;
|
|
2779
|
+
}) => Promise<HqFlushResult>;
|
|
2517
2780
|
runPrVerify?: (args: PrVerifyArgs) => Promise<PrVerifyProof>;
|
|
2518
2781
|
/**
|
|
2519
2782
|
* Injectable delay for the bounded hosted-run await (#348; tests only —
|
|
@@ -3225,8 +3488,8 @@ declare const FACTORY_TRACE_EVENT_DEFINITIONS: readonly [DefinedTraceEvent<"revi
|
|
|
3225
3488
|
interiorCycle: z.ZodOptional<z.ZodNumber>;
|
|
3226
3489
|
observedAt: z.ZodString;
|
|
3227
3490
|
purpose: z.ZodEnum<{
|
|
3228
|
-
code: "code";
|
|
3229
3491
|
review: "review";
|
|
3492
|
+
code: "code";
|
|
3230
3493
|
}>;
|
|
3231
3494
|
slotResolution: z.ZodObject<{
|
|
3232
3495
|
effort: z.ZodEnum<{
|
|
@@ -3304,8 +3567,8 @@ declare const FACTORY_TRACE_EVENT_DEFINITIONS: readonly [DefinedTraceEvent<"revi
|
|
|
3304
3567
|
interiorCycle: z.ZodOptional<z.ZodNumber>;
|
|
3305
3568
|
observedAt: z.ZodString;
|
|
3306
3569
|
purpose: z.ZodEnum<{
|
|
3307
|
-
code: "code";
|
|
3308
3570
|
review: "review";
|
|
3571
|
+
code: "code";
|
|
3309
3572
|
}>;
|
|
3310
3573
|
slotResolution: z.ZodObject<{
|
|
3311
3574
|
effort: z.ZodEnum<{
|
|
@@ -3374,8 +3637,8 @@ declare const FACTORY_TRACE_EVENT_DEFINITIONS: readonly [DefinedTraceEvent<"revi
|
|
|
3374
3637
|
interiorCycle: z.ZodOptional<z.ZodNumber>;
|
|
3375
3638
|
observedAt: z.ZodString;
|
|
3376
3639
|
purpose: z.ZodEnum<{
|
|
3377
|
-
code: "code";
|
|
3378
3640
|
review: "review";
|
|
3641
|
+
code: "code";
|
|
3379
3642
|
}>;
|
|
3380
3643
|
slotResolution: z.ZodObject<{
|
|
3381
3644
|
effort: z.ZodEnum<{
|
|
@@ -3447,7 +3710,7 @@ declare const FACTORY_TRACE_EVENT_DEFINITIONS: readonly [DefinedTraceEvent<"revi
|
|
|
3447
3710
|
exitCode: number | null;
|
|
3448
3711
|
harness: string;
|
|
3449
3712
|
observedAt: string;
|
|
3450
|
-
purpose: "
|
|
3713
|
+
purpose: "review" | "code";
|
|
3451
3714
|
slotResolution: {
|
|
3452
3715
|
effort: "high" | "low" | "medium" | "xhigh";
|
|
3453
3716
|
engine: string;
|
|
@@ -3666,13 +3929,39 @@ declare const isBotLogin: (login?: string | undefined) => boolean;
|
|
|
3666
3929
|
declare const SHA_MATCH_MIN_LENGTH = 7;
|
|
3667
3930
|
declare const sameHeadSha: (left: string, right: string) => boolean;
|
|
3668
3931
|
//#endregion
|
|
3669
|
-
//#region src/doctor.d.ts
|
|
3932
|
+
//#region src/doctor-hq-checks.d.ts
|
|
3933
|
+
/**
|
|
3934
|
+
* Two `doctor` checks that make silent HQ delivery failure loud (#394,
|
|
3935
|
+
* epic #389 wave 2).
|
|
3936
|
+
*
|
|
3937
|
+
* Both consume #414's structured credential resolution rather than inventing
|
|
3938
|
+
* a second classification: a sandboxed session that cannot reach the
|
|
3939
|
+
* keychain is reported as "cannot resolve credentials here", never as
|
|
3940
|
+
* "credentials are wrong" or a silent pass. Both name `psf hq:flush` and
|
|
3941
|
+
* `psf pr:publish` as the commands that need a trusted local session.
|
|
3942
|
+
*
|
|
3943
|
+
* Advisory stays advisory: neither check can block anything but doctor's own
|
|
3944
|
+
* exit status (epic #389 design decision 4). Absent credentials make the
|
|
3945
|
+
* remote check a skip with a printed reason, never a silent pass.
|
|
3946
|
+
*/
|
|
3670
3947
|
type DoctorCheckStatus = "error" | "ok" | "warning";
|
|
3671
3948
|
interface DoctorCheck {
|
|
3672
3949
|
message: string;
|
|
3673
3950
|
name: string;
|
|
3674
3951
|
status: DoctorCheckStatus;
|
|
3675
3952
|
}
|
|
3953
|
+
interface HqSpoolCheckDependencies {
|
|
3954
|
+
countSpool?: typeof countHqSpoolWork;
|
|
3955
|
+
sweepOrphans?: typeof sweepHqSpoolOrphans;
|
|
3956
|
+
}
|
|
3957
|
+
interface HqRetroReadbackCheckDependencies {
|
|
3958
|
+
fetch?: typeof fetch;
|
|
3959
|
+
readFile?: typeof readFile;
|
|
3960
|
+
resolve?: SecretReferenceResolver;
|
|
3961
|
+
timeoutMs?: number;
|
|
3962
|
+
}
|
|
3963
|
+
//#endregion
|
|
3964
|
+
//#region src/doctor.d.ts
|
|
3676
3965
|
interface DoctorReport {
|
|
3677
3966
|
checks: DoctorCheck[];
|
|
3678
3967
|
ok: boolean;
|
|
@@ -3689,6 +3978,10 @@ interface DoctorProjectProfileInput extends LoadProjectProfileInput {
|
|
|
3689
3978
|
/** Diff base for the admission preflight; ignored unless `preflight`. */
|
|
3690
3979
|
base?: string;
|
|
3691
3980
|
env?: NodeJS.ProcessEnv;
|
|
3981
|
+
/** Test seam for the local HQ spool check (#394); production never sets this. */
|
|
3982
|
+
hqRetroReadbackDependencies?: HqRetroReadbackCheckDependencies;
|
|
3983
|
+
/** Test seam for the remote HQ retro-envelope read-back check (#394). */
|
|
3984
|
+
hqSpoolDependencies?: HqSpoolCheckDependencies;
|
|
3692
3985
|
/**
|
|
3693
3986
|
* Append the read-only admission checklist (#292): every requirement this
|
|
3694
3987
|
* candidate must satisfy before merge, named in one pass. Preflight checks
|
|
@@ -3697,7 +3990,7 @@ interface DoctorProjectProfileInput extends LoadProjectProfileInput {
|
|
|
3697
3990
|
preflight?: boolean;
|
|
3698
3991
|
userConfig?: LoadUserConfigResult;
|
|
3699
3992
|
}
|
|
3700
|
-
declare function doctorProjectProfile(input?: DoctorProjectProfileInput): DoctorReport
|
|
3993
|
+
declare function doctorProjectProfile(input?: DoctorProjectProfileInput): Promise<DoctorReport>;
|
|
3701
3994
|
//#endregion
|
|
3702
3995
|
//#region src/pr-review-gate-trace.d.ts
|
|
3703
3996
|
interface ReviewGateTraceIdentity {
|
|
@@ -4026,6 +4319,10 @@ interface EvaluationInput {
|
|
|
4026
4319
|
waveReviewDemand?: Pick<WaveReviewDemand, "review" | "wave">;
|
|
4027
4320
|
}
|
|
4028
4321
|
declare const evaluateReadiness: (input: EvaluationInput) => {
|
|
4322
|
+
blockedReasons: {
|
|
4323
|
+
code: string;
|
|
4324
|
+
detail: string;
|
|
4325
|
+
}[];
|
|
4029
4326
|
blockingReasons: string[];
|
|
4030
4327
|
humanBlockingReasons: string[];
|
|
4031
4328
|
ledger: {
|
|
@@ -4053,14 +4350,14 @@ declare const evaluateReadiness: (input: EvaluationInput) => {
|
|
|
4053
4350
|
reviews: {
|
|
4054
4351
|
correctness: {
|
|
4055
4352
|
required: boolean;
|
|
4056
|
-
status: "blocked" | "not-required" | "
|
|
4353
|
+
status: "blocked" | "not-required" | "stale" | "current" | "missing";
|
|
4057
4354
|
docsOnlyDeltaAccepted?: boolean | undefined;
|
|
4058
4355
|
reviewedHeadSha?: string | undefined;
|
|
4059
4356
|
reviewedPatchId?: string | undefined;
|
|
4060
4357
|
};
|
|
4061
4358
|
security?: {
|
|
4062
4359
|
required: boolean;
|
|
4063
|
-
status: "blocked" | "not-required" | "
|
|
4360
|
+
status: "blocked" | "not-required" | "stale" | "current" | "missing";
|
|
4064
4361
|
docsOnlyDeltaAccepted?: boolean | undefined;
|
|
4065
4362
|
reviewedHeadSha?: string | undefined;
|
|
4066
4363
|
reviewedPatchId?: string | undefined;
|
|
@@ -4070,7 +4367,7 @@ declare const evaluateReadiness: (input: EvaluationInput) => {
|
|
|
4070
4367
|
stackRole: "slice" | "single" | "rollup" | "merge-gate prerequisite";
|
|
4071
4368
|
verification: {
|
|
4072
4369
|
command: "patronage-factory pr:verify";
|
|
4073
|
-
prVerify: "
|
|
4370
|
+
prVerify: "stale" | "passed" | "missing";
|
|
4074
4371
|
docsOnlyDeltaAccepted?: boolean | undefined;
|
|
4075
4372
|
docsOnlyVerifiedHeadSha?: string | undefined;
|
|
4076
4373
|
trivialDeltaAccepted?: boolean | undefined;
|
|
@@ -4356,6 +4653,7 @@ type FetchLike = (input: string, init?: {
|
|
|
4356
4653
|
headers?: Record<string, string>;
|
|
4357
4654
|
method?: string;
|
|
4358
4655
|
redirect?: "error";
|
|
4656
|
+
signal?: AbortSignal;
|
|
4359
4657
|
}) => Promise<{
|
|
4360
4658
|
json: () => Promise<unknown>;
|
|
4361
4659
|
status: number;
|
|
@@ -4370,6 +4668,14 @@ interface PublishEpicStructureArgs {
|
|
|
4370
4668
|
event: EpicStructureEvent;
|
|
4371
4669
|
url: string;
|
|
4372
4670
|
fetchImpl?: FetchLike;
|
|
4671
|
+
/**
|
|
4672
|
+
* Optional caller-owned abort signal. A caller that bounds this call with
|
|
4673
|
+
* its own deadline (e.g. `factory:closeout`'s advisory re-emission, #392)
|
|
4674
|
+
* can abort the in-flight request itself instead of merely abandoning the
|
|
4675
|
+
* `await` — leaving the outbound socket alive past the caller's own
|
|
4676
|
+
* declared timeout.
|
|
4677
|
+
*/
|
|
4678
|
+
signal?: AbortSignal;
|
|
4373
4679
|
}
|
|
4374
4680
|
interface PublishEpicStructureResult {
|
|
4375
4681
|
duplicate: boolean;
|