@patronage/software-factory 1.0.0-alpha.0 → 1.0.0-alpha.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/CONTEXT.md CHANGED
@@ -6,12 +6,22 @@ The shared Patronage software factory: typed lifecycle state, proof gates, and o
6
6
 
7
7
  ## Language
8
8
 
9
+ ### Admission
10
+
11
+ **Candidate**: The exact patch under admission — the `headSha` / `patchId` / `mergeBaseSha` identity every piece of evidence binds to. A GitHub draft pull request is not a Candidate; GitHub's draft boolean is the machine lifecycle authority, and ready-for-review is the Candidate state. Experiment and interactive describe human intent only and create no machine states.
12
+
13
+ **Promotion**: The one transition from a draft pull request into a Candidate. `pr:publish` is the promotion verb. Its composed handoff finishes proof while draft, promotes, then evaluates Candidate demands at the unchanged head. Draft open and synchronize events run no substantive hosted Verify battery; `ready_for_review` starts the complete applicable Candidate pass.
14
+
9
15
  ### Proof reuse
10
16
 
11
17
  **Proof gate**: A command that emits a typed proof — the self-timed set is `pr:verify`, `pr:review`, `pr:ready`, `boundary:check`, and `factory:closeout`; one execution is a **gate run**. Typed proof files are the source of readiness truth; prose never substitutes.
12
18
 
13
19
  **Proof-reuse gate**: The decision that consumes trusted verification proof and determines whether hosted CI may skip work already performed. Refusing reuse runs the hosted suite; it does not fail the candidate. Defined by ADR 0022. _Avoid_: proof gate (that produces readiness proof rather than reusing it).
14
20
 
21
+ ### Impact
22
+
23
+ **Impact**: The candidate-bound classification of which declared product targets a changed path or lockfile graph can reach. Path impact is opt-in: a target is affected if and only if a valid changed path matches one of its declared globs. A valid path matched by no target is **unsubscribed** and demands no target-scoped work, while remaining explicit in the stamp basis and human summary. Shared paths affect every target that subscribes to them. Invalid paths and unreadable or malformed required inputs still fail closed. Impact may withdraw target-scoped work; it never certifies a merge target, whose push-triggered verification stays full. Defined by ADR 0027. _Avoid_: affectedness; ownership (the declaration is a subscription, not an implicit filesystem owner).
24
+
15
25
  ### Planning intake
16
26
 
17
27
  **Spec**: The single through-line planning artifact — a technical, non-technical, or blended description of the work to be built, produced by `to-spec`. The term supersedes "PRD"; older artifacts titled "PRD:" (e.g. #232) are specs by another name. _Avoid_: PRD (retired), requirements doc.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  The factory admits a pull request from evidence bound to its candidate. It validates policy and proof; it does not schedule workers, choose models, or run reviews. Those are jobs for the harness or operator.
6
6
 
7
- Admission is fail closed. A clean-session review and required verification produce typed proof, then readiness decides whether the candidate may proceed. Evidence stays current when the candidate patch is unchanged; the push-triggered Verify workflow covers integration risk on every factory merge target and freezes further merges into a target when that target goes red. Epic policy, including review minimums and auto-merge authority, lives in the boundary manifest. HQ observes this work; it never blocks a gate.
7
+ Admission is fail closed. A clean-session review and required verification produce typed proof, then readiness decides whether the candidate may proceed. Evidence stays current when the candidate patch is unchanged; the push-triggered Verify workflow covers integration risk on every factory merge target — `main` and the `epic/**` integration branches. When a target goes red, the factory refuses new readiness decisions and authorized arming against that target; candidates armed before the target went red can still land. Epic policy, including review minimums and auto-merge authority, lives in the boundary manifest. HQ observes this work; it never blocks a gate.
8
8
 
9
9
  ## Develop the package
10
10
 
@@ -30,18 +30,24 @@ This is the release checklist. It lives here and nowhere else; the repository ru
30
30
 
31
31
  Releases are attended and hand-cut. Nothing in the factory publishes, tags, or releases on its own, and no step below may be automated as a drive-by.
32
32
 
33
- 1. Bump `package.json` to valid SemVer. The assertion derives its expected stable or prerelease channel from the version; it accepts no channel flag.
34
- 2. Publish to npm per the [repository operations runbook](../docs/repository-operations.md#publishing) (attended, hand-cut, `pnpm publish` only). A stable version uses `pnpm --filter @patronage/software-factory publish`, which assigns npm `latest`. A prerelease must use `pnpm --filter @patronage/software-factory publish --tag next`; bare `pnpm publish` defaults to `latest` and would move it before the post-hoc assertion can reject the release.
33
+ The factory family has one version posture (ADR 0026, `docs/adr/0026-factory-family-version-lines-move-in-lockstep.md` in the source repository): `@patronage/factory-ci` moves in lockstep majors with this CLI. From factory-ci's first release in the CLI's current series onward, majors and prerelease series move together while patch and prerelease increments stay independent per package. A consumer pins a factory-ci from the same major/prerelease series as its CLI; there is no compatibility matrix. Until that first lockstep release, factory-ci's published 0.x line is the named pre-adoption state and pairs with any CLI version. `@patronage/alchemy-d1-state` keeps independent SemVer. Lockstep is a rule the maintainer follows when cutting releases, never automation.
34
+
35
+ A stacked release train has one terminal release-owning slice. Interior slices do not bump `package.json` or cut a checkpoint unless the boundary explicitly intends a separately published release at that slice: a lower-slice bump consumes the release window before the terminal slice lands, and the train must then advance the version and re-earn its terminal evidence.
36
+
37
+ A stable cut has two phases. Phase 1 is the verified package candidate: steps 1 through 4 below, ending in the attended npm publish, tag, and GitHub release. Phase 2 is post-publication stable-doc convergence: steps 5 and 6, a normal admitted docs PR followed by the read-only convergence assertion from a clean `main`. Stable docs must never claim an unpublished version, so they cannot land in the phase-1 change. Prerelease behavior is unchanged: prerelease notes may land before publish while stable docs keep naming the prior stable version.
38
+
39
+ 1. Bump `package.json` to valid SemVer. The assertion derives its expected stable or prerelease channel from the version; it accepts no channel flag. Before publishing, prove production HQ accepts this candidate's proof schema. The manifest bump triggers the CI-only `HQ Deploy` workflow because HQ bundles this package's public schemas. From a clean `main`, record `RELEASE_SHA="$(git rev-parse HEAD)"`, find the `HQ Deploy` run with `gh run list --workflow "HQ Deploy" --commit "$RELEASE_SHA"`, and use `gh run watch <run-id> --exit-status`; an older green deployment does not count. Then, outside a sandbox, run `psf hq:flush --cwd <candidate-worktree>` against the candidate's retained `pr-verify-proof`. The release may proceed only when the current proof exists, the exact-SHA deployment passed, and the drain reports that proof `delivered` or `duplicate` (or no pending spool because its original delivery already succeeded), with `rejected 0` and no event remaining. This is an attended release-compatibility gate; it does not make HQ authoritative for ordinary admission.
40
+ 2. Build and review one pnpm tarball, then publish that exact tarball per the [repository operations runbook](../docs/repository-operations.md#publishing) (attended and hand-cut). Publishing the workspace directory is not allowed because it rebuilds during `prepack` after the artifact review. **Always pass `--tag` explicitly, on every publish, prerelease or stable**: `pnpm publish "$SOFTWARE_FACTORY_TARBALL" --tag next --access public` for a prerelease, `--tag latest` for a stable cut. A filtered directory publish takes pnpm's recursive path, which passes its own `--tag` (defaulting to `latest`) to npm and therefore ignores the `publishConfig.tag: next` pin both factory packages carry; that is how 1.0.0-alpha.6 briefly took `latest` (#658). The pins stay as belt and braces, but they are not the control. Omitting the flag is not a safe failure: it moves `latest`, which the convergence assertion can only report after the fact. The attended token path does not promise an npm provenance attestation; the source SHA and reviewed tarball SRI are its operational provenance.
35
41
  3. Cut the `software-factory-vX.Y.Z` tag and push it.
36
42
  4. Cut the GitHub release for that tag, with notes covering features, deletions, breaking changes, and closed issues. Mark it as a prerelease exactly when its SemVer version has a prerelease component. When the release changes the profile `schemaVersion`, state it in the notes — the profile schema version is a separate axis from the package version.
37
- 5. Update affected `software-factory-docs/` pages. Stable docs must state the current stable version. Prerelease docs may record the candidate and its migration notes, but must not represent it as the current stable release.
38
- 6. Confirm the five facts converge:
43
+ 5. Converge the stable docs after publication. Update affected `software-factory-docs/` pages through a normal admitted docs PR. Stable docs must state the current stable version. Prerelease docs may record the candidate and its migration notes, but must not represent it as the current stable release.
44
+ 6. After the docs PR merges, confirm the six facts converge from a clean checkout of `main`:
39
45
 
40
46
  ```bash
41
47
  pnpm --filter @patronage/software-factory exec tsx scripts/assert-publish-manifest.ts --release .
42
48
  ```
43
49
 
44
- It reads what you just did and derives the channel from the manifest version. A stable release must converge exactly among manifest, npm `latest`, non-prerelease GitHub release, tag, and stable docs. A prerelease must exist on npm, own npm `next`, leave npm `latest` at the stable docs version, and have its prerelease GitHub release and tag. It publishes nothing. Step 6 is the only step a machine performs, and it performs it after you.
50
+ It reads what you just did and derives the channel from the manifest version. A stable release must converge exactly among manifest, npm `latest`, non-prerelease GitHub release, tag, and stable docs. A prerelease must exist on npm, own npm `next`, leave npm `latest` at the stable docs version, and have its prerelease GitHub release and tag. The sixth fact is the factory-ci pairing above: the assertion passes while every observed published `@patronage/factory-ci` version is pre-adoption 0.x and names that state, fails once a lockstep factory-ci release exists whose newest published version is outside the CLI's major/prerelease series, and fails when the registry read yields no readable factory-ci versions or contains an entry that does not parse — missing or unreadable data is an unsatisfied fact, not pre-adoption. It publishes nothing. Step 6 is the only step a machine performs, and it performs it after you.
45
51
 
46
52
  ## Use it in a project
47
53
 
@@ -54,6 +60,8 @@ pnpm exec patronage-factory --help # or: psf --help
54
60
 
55
61
  The repository profile declares its admission policy. Operator-local settings and HQ credentials are documented separately, so they do not become committed project configuration.
56
62
 
63
+ Generated production workflows may call `psf production:impact --before <sha> --after <sha> --github-output <path>`. The command recomputes the profile's opt-in impact subscriptions from those exact real commits and emits stable demanded/withdrawn target outputs, the changed-file decision basis, and unsubscribed paths. It only withdraws work: missing, zero, malformed, unreachable, non-ancestral, or otherwise unclassifiable identities leave every known target demanded. The factory owns this decision seam; each consumer owns its deploy jobs, commands, credentials, topology, and post-deploy convergence/no-op proof. Merge-target Verify remains full and does not consult this command.
64
+
57
65
  For factory workflow, install the four canonical skills from the repository:
58
66
 
59
67
  ```bash
package/dist/index.d.ts CHANGED
@@ -145,8 +145,6 @@ interface HqIngestDependencies {
145
145
  * `timeoutMs`: see the note there.
146
146
  */
147
147
  journalFlushBudgetMs?: number;
148
- /** @deprecated Legacy sidecars are now streamed directly; retained for API compatibility. */
149
- sidecarReclaimMs?: number;
150
148
  /**
151
149
  * Setup budget for callers that own the process lifetime — honored AS GIVEN,
152
150
  * above the `DEFAULT_HQ_INGEST_TIMEOUT_MS` ceiling that clamps `timeoutMs`.
@@ -184,12 +182,6 @@ interface HqSpoolEntryOutcome {
184
182
  kind: string;
185
183
  spool: string;
186
184
  /**
187
- * `migrated` belongs to the legacy JSONL journal only: the row was moved
188
- * into the current spool without being delivered. The spool pass that runs
189
- * after it in the same drain supersedes that line with a real outcome when
190
- * it gets to the row; a `migrated` line that survives the run means the row
191
- * is still waiting.
192
- *
193
185
  * `undeliverable` is the one terminal verdict (#445). Every other status
194
186
  * describes a moment: HQ was unreachable, HQ refused this content today, the
195
187
  * row moved. Retrying is meaningful for all of them. A wrong-origin entry is
@@ -198,13 +190,11 @@ interface HqSpoolEntryOutcome {
198
190
  * run. Leaving it spooled asks the operator to retry something that provably
199
191
  * cannot succeed, and the count it inflates is the one doctor goes red on.
200
192
  */
201
- status: "delivered" | "duplicate" | "migrated" | "rejected" | "undeliverable" | "unreachable";
193
+ status: "delivered" | "duplicate" | "rejected" | "undeliverable" | "unreachable";
202
194
  }
203
195
  interface HqSpoolFlushInput {
204
196
  clientId: string;
205
197
  clientSecret: string;
206
- /** Repository root whose legacy `.factory-memory` spool is also drained. */
207
- cwd: string;
208
198
  /** The profile's HQ origin; entries recorded against another are refused. */
209
199
  endpoint: string;
210
200
  /** Operator-named spool directories; replaces the default two locations. */
@@ -239,13 +229,13 @@ interface HqSpoolFlushSummary {
239
229
  interface HqSpoolWorkCount {
240
230
  /**
241
231
  * The earliest moment learned across pending spool files (their own write
242
- * time) and legacy journal rows (their own `failedAt`). Absent only when
232
+ * time). Absent only when
243
233
  * `pending` is `0`, or when every timestamp source was unreadable within
244
234
  * budget — an estimate for doctor's remediation message (#394), never a
245
235
  * precise audit trail.
246
236
  */
247
237
  oldestQueuedAt?: string;
248
- /** Spooled events and replayable journals waiting in the locations below. */
238
+ /** Spooled events waiting in the locations below. */
249
239
  pending: number;
250
240
  /** Locations that exist and hold spooled work. */
251
241
  spools: string[];
@@ -266,7 +256,7 @@ interface HqSpoolWorkCount {
266
256
  * same locations `flushHqSpool` drains are inspected here, read-only: no
267
257
  * directory is created, nothing is secured, and nothing is delivered.
268
258
  */
269
- declare function countHqSpoolWork(input: Pick<HqSpoolFlushInput, "cwd" | "explicitDirectories" | "repository">, dependencies?: {
259
+ declare function countHqSpoolWork(input: Pick<HqSpoolFlushInput, "explicitDirectories" | "repository">, dependencies?: {
270
260
  budgetMs?: number;
271
261
  env?: NodeJS.ProcessEnv;
272
262
  }): Promise<HqSpoolWorkCount>;
@@ -275,7 +265,7 @@ interface HqSpoolOrphan {
275
265
  /** The `hq-retry-spool` directory itself, ready to pass to `--dir`. */
276
266
  directory: string;
277
267
  oldestQueuedAt?: string;
278
- /** Spooled events and journal rows waiting there. */
268
+ /** Spooled events waiting there. */
279
269
  pending: number;
280
270
  /**
281
271
  * The location exists but could not be listed. As everywhere else in this
@@ -537,6 +527,7 @@ declare const factoryProjectProfileSchema: z.ZodObject<{
537
527
  targets: z.ZodArray<z.ZodObject<{
538
528
  importers: z.ZodArray<z.ZodString>;
539
529
  name: z.ZodString;
530
+ paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
540
531
  }, z.core.$strict>>;
541
532
  }, z.core.$strict>>;
542
533
  proof: z.ZodObject<{
@@ -752,8 +743,8 @@ declare const mergeFreezeStateSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
752
743
  generationId: z.ZodNumber;
753
744
  headSha: z.ZodString;
754
745
  outcome: z.ZodEnum<{
755
- active: "active";
756
746
  stale: "stale";
747
+ active: "active";
757
748
  }>;
758
749
  reason: z.ZodString;
759
750
  recordedAt: z.ZodISODateTime;
@@ -1252,8 +1243,8 @@ declare const managedReadinessLedgerSchema: z.ZodObject<{
1252
1243
  status: z.ZodEnum<{
1253
1244
  blocked: "blocked";
1254
1245
  "not-required": "not-required";
1255
- stale: "stale";
1256
1246
  current: "current";
1247
+ stale: "stale";
1257
1248
  missing: "missing";
1258
1249
  }>;
1259
1250
  }, z.core.$strip>;
@@ -1264,8 +1255,8 @@ declare const managedReadinessLedgerSchema: z.ZodObject<{
1264
1255
  status: z.ZodEnum<{
1265
1256
  blocked: "blocked";
1266
1257
  "not-required": "not-required";
1267
- stale: "stale";
1268
1258
  current: "current";
1259
+ stale: "stale";
1269
1260
  missing: "missing";
1270
1261
  }>;
1271
1262
  }, z.core.$strip>>;
@@ -1276,8 +1267,8 @@ declare const managedReadinessLedgerSchema: z.ZodObject<{
1276
1267
  docsOnlyDeltaAccepted: z.ZodOptional<z.ZodBoolean>;
1277
1268
  docsOnlyVerifiedHeadSha: z.ZodOptional<z.ZodString>;
1278
1269
  prVerify: z.ZodEnum<{
1279
- stale: "stale";
1280
1270
  passed: "passed";
1271
+ stale: "stale";
1281
1272
  missing: "missing";
1282
1273
  }>;
1283
1274
  trivialDeltaAccepted: z.ZodOptional<z.ZodBoolean>;
@@ -1371,6 +1362,7 @@ interface GitHubPullRequest {
1371
1362
  baseRefName: string;
1372
1363
  baseRefOid: string;
1373
1364
  body: string;
1365
+ headRefName?: string;
1374
1366
  headRefOid: string;
1375
1367
  isDraft: boolean;
1376
1368
  mergeStateStatus: string;
@@ -1522,11 +1514,11 @@ declare function runPrReady(args: PrReadyArgs, dependencies?: PrReadyDependencie
1522
1514
  */
1523
1515
  declare const impactStampSchema: z.ZodObject<{
1524
1516
  basis: z.ZodEnum<{
1525
- "lockfile-scoped": "lockfile-scoped";
1517
+ "target-scoped": "target-scoped";
1526
1518
  conservative: "conservative";
1527
1519
  }>;
1528
1520
  reasons: z.ZodArray<z.ZodString>;
1529
- stampVersion: z.ZodLiteral<1>;
1521
+ stampVersion: z.ZodLiteral<3>;
1530
1522
  targets: z.ZodArray<z.ZodObject<{
1531
1523
  basis: z.ZodString;
1532
1524
  impact: z.ZodEnum<{
@@ -1535,6 +1527,7 @@ declare const impactStampSchema: z.ZodObject<{
1535
1527
  }>;
1536
1528
  name: z.ZodString;
1537
1529
  }, z.core.$strip>>;
1530
+ unsubscribedPaths: z.ZodArray<z.ZodString>;
1538
1531
  }, z.core.$strip>;
1539
1532
  type ImpactStamp = z.infer<typeof impactStampSchema>;
1540
1533
  /**
@@ -1560,7 +1553,7 @@ interface ImpactScopeDecision {
1560
1553
  * external proof demand (#542 wave 2), a verification-battery command, or a
1561
1554
  * preview (Alchemy) lifecycle stack (#430 wave 3).
1562
1555
  *
1563
- * The only release path is a `lockfile-scoped` stamp of exactly this build's
1556
+ * The only release path is a `target-scoped` stamp of exactly this build's
1564
1557
  * stamp version whose target entry for exactly this name is provably
1565
1558
  * `not-affected`. Every other input — no trusted stamp, an unknown stamp
1566
1559
  * version, a conservative stamp, a name the stamp does not classify, or an
@@ -1779,8 +1772,8 @@ declare const REVIEW_STATUS_VALUES: readonly ["not-required", "current", "stale"
1779
1772
  declare const reviewStatusSchema: z.ZodEnum<{
1780
1773
  blocked: "blocked";
1781
1774
  "not-required": "not-required";
1782
- stale: "stale";
1783
1775
  current: "current";
1776
+ stale: "stale";
1784
1777
  missing: "missing";
1785
1778
  }>;
1786
1779
  type ReviewStatus = z.infer<typeof reviewStatusSchema>;
@@ -2186,9 +2179,9 @@ declare const retroEnvelopeV1Schema: z.ZodObject<{
2186
2179
  kind: z.ZodLiteral<"retro-envelope">;
2187
2180
  outcome: z.ZodOptional<z.ZodObject<{
2188
2181
  status: z.ZodEnum<{
2189
- blocked: "blocked";
2190
2182
  success: "success";
2191
2183
  fail: "fail";
2184
+ blocked: "blocked";
2192
2185
  "ship-with-followups": "ship-with-followups";
2193
2186
  }>;
2194
2187
  verdict: z.ZodOptional<z.ZodString>;
@@ -2335,7 +2328,9 @@ type BoundaryCheckProofRecord = z.infer<typeof boundaryCheckProofSchema>;
2335
2328
  //#endregion
2336
2329
  //#region src/boundary-check.d.ts
2337
2330
  interface BoundaryPullRequestRef {
2331
+ baseRefName?: string;
2338
2332
  headRefOid?: string;
2333
+ headRefName?: string;
2339
2334
  mergeCommitOid?: string;
2340
2335
  number: number;
2341
2336
  state: "OPEN" | "CLOSED" | "MERGED";
@@ -3929,12 +3924,14 @@ declare const boundaryManifestBaseSchema: z.ZodObject<{
3929
3924
  }>;
3930
3925
  }, z.core.$loose>>;
3931
3926
  declaredBy: z.ZodString;
3927
+ integrationBranch: z.ZodOptional<z.ZodString>;
3932
3928
  prs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
3933
3929
  schemaVersion: z.ZodLiteral<1>;
3934
3930
  topology: z.ZodEnum<{
3935
3931
  flagged: "flagged";
3936
3932
  "each-to-main": "each-to-main";
3937
3933
  stacked: "stacked";
3934
+ "each-to-epic": "each-to-epic";
3938
3935
  }>;
3939
3936
  waves: z.ZodArray<z.ZodObject<{
3940
3937
  autoMerge: z.ZodOptional<z.ZodBoolean>;
@@ -3957,12 +3954,14 @@ declare const boundaryManifestSchema: z.ZodObject<{
3957
3954
  }>;
3958
3955
  }, z.core.$loose>>;
3959
3956
  declaredBy: z.ZodString;
3957
+ integrationBranch: z.ZodOptional<z.ZodString>;
3960
3958
  prs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
3961
3959
  schemaVersion: z.ZodLiteral<1>;
3962
3960
  topology: z.ZodEnum<{
3963
3961
  flagged: "flagged";
3964
3962
  "each-to-main": "each-to-main";
3965
3963
  stacked: "stacked";
3964
+ "each-to-epic": "each-to-epic";
3966
3965
  }>;
3967
3966
  waves: z.ZodArray<z.ZodObject<{
3968
3967
  autoMerge: z.ZodOptional<z.ZodBoolean>;
@@ -4075,13 +4074,13 @@ declare const evaluateReadiness: (input: EvaluationInput) => {
4075
4074
  reviews: {
4076
4075
  correctness: {
4077
4076
  required: boolean;
4078
- status: "blocked" | "not-required" | "stale" | "current" | "missing";
4077
+ status: "blocked" | "not-required" | "current" | "stale" | "missing";
4079
4078
  reviewedHeadSha?: string | undefined;
4080
4079
  reviewedPatchId?: string | undefined;
4081
4080
  };
4082
4081
  security?: {
4083
4082
  required: boolean;
4084
- status: "blocked" | "not-required" | "stale" | "current" | "missing";
4083
+ status: "blocked" | "not-required" | "current" | "stale" | "missing";
4085
4084
  reviewedHeadSha?: string | undefined;
4086
4085
  reviewedPatchId?: string | undefined;
4087
4086
  } | undefined;
@@ -4089,7 +4088,7 @@ declare const evaluateReadiness: (input: EvaluationInput) => {
4089
4088
  schemaVersion: 1;
4090
4089
  verification: {
4091
4090
  command: "patronage-factory pr:verify";
4092
- prVerify: "stale" | "passed" | "missing";
4091
+ prVerify: "passed" | "stale" | "missing";
4093
4092
  docsOnlyDeltaAccepted?: boolean | undefined;
4094
4093
  docsOnlyVerifiedHeadSha?: string | undefined;
4095
4094
  trivialDeltaAccepted?: boolean | undefined;