@sublang/playbook 17.6.0 → 17.8.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.
@@ -1490,6 +1490,158 @@ function carriedPreExistingFacts(
1490
1490
  });
1491
1491
  }
1492
1492
 
1493
+ // DR-089: a turn whose leaf parks in failure still states, as fact, every
1494
+ // commit its steps proved before the failure, so a closing reply never tells
1495
+ // the Boss that nothing was written when a receipt shows otherwise. A commit
1496
+ // the unresolved-effect report already names is not said twice.
1497
+ interface CommitBeforeFailure {
1498
+ readonly baselineHead: string;
1499
+ readonly commitOid: string;
1500
+ /** Every new commit, oldest first, where the step made more than one (DR-088). */
1501
+ readonly commits?: readonly string[];
1502
+ }
1503
+
1504
+ function commitsBeforeFailure(
1505
+ ledger: PlaybookEffectLedger,
1506
+ boundaryPrefixCount: number,
1507
+ unresolvedEffects: readonly PlaybookCaptainUnresolvedEffect[],
1508
+ ): readonly CommitBeforeFailure[] {
1509
+ // An entry names a commit by its proven OID, or — for a receipt listing
1510
+ // several commits, which projects as `multiple-commits` (DR-088 §3) — by
1511
+ // its after HEAD, the last of them.
1512
+ const named = new Set(
1513
+ unresolvedEffects.flatMap(({ classification, afterHead, commitOid }) =>
1514
+ commitOid !== undefined
1515
+ ? [commitOid]
1516
+ : classification === 'multiple-commits' && afterHead !== undefined
1517
+ ? [afterHead]
1518
+ : [],
1519
+ ),
1520
+ );
1521
+ const commits: CommitBeforeFailure[] = [];
1522
+ for (const boundary of ledger.boundaries) {
1523
+ if (boundary.sequence <= boundaryPrefixCount) continue;
1524
+ const receipt = boundary.physicalReceipt;
1525
+ if (
1526
+ receipt?.classification !== 'one-descendant-commit' ||
1527
+ receipt.commitOid === undefined ||
1528
+ named.has(receipt.commitOid)
1529
+ ) {
1530
+ continue;
1531
+ }
1532
+ named.add(receipt.commitOid);
1533
+ commits.push({
1534
+ baselineHead: receipt.baseline.head,
1535
+ commitOid: receipt.commitOid,
1536
+ ...(receipt.commits === undefined ? {} : { commits: receipt.commits }),
1537
+ });
1538
+ }
1539
+ return commits;
1540
+ }
1541
+
1542
+ /** What one step committed, with every commit where it made several (DR-088). */
1543
+ function commitBeforeFailureClause({
1544
+ baselineHead,
1545
+ commitOid,
1546
+ commits,
1547
+ }: CommitBeforeFailure): string {
1548
+ const made =
1549
+ commits === undefined
1550
+ ? 'made one new commit'
1551
+ : `made ${commits.length} new commits, oldest first: ${boundedPathList(commits)}`;
1552
+ return `${made}; HEAD moved from ${baselineHead} to the proven commit ${commitOid}.`;
1553
+ }
1554
+
1555
+ function commitBeforeFailureFact(commit: CommitBeforeFailure): string {
1556
+ return `Before the failure, a step ${commitBeforeFailureClause(commit)}`;
1557
+ }
1558
+
1559
+ function commitsBeforeFailureReport(
1560
+ commits: readonly CommitBeforeFailure[],
1561
+ ): string | undefined {
1562
+ if (commits.length === 0) return undefined;
1563
+ return [
1564
+ 'Repository changes made before the failure:',
1565
+ ...commits.map(
1566
+ (commit) => `- A step ${commitBeforeFailureClause(commit)}`,
1567
+ ),
1568
+ 'The failure does not undo these commits.',
1569
+ ].join('\n');
1570
+ }
1571
+
1572
+ /**
1573
+ * DR-088 §2: whether a final text names one commit as a word, by its full OID
1574
+ * or by an abbreviation of at least seven leading hex digits — the
1575
+ * reconciler's own designation rule, read here to say what it accepted.
1576
+ */
1577
+ function finalTextNamesCommit(finalText: string, oid: string): boolean {
1578
+ for (const [word] of finalText.matchAll(
1579
+ /(?<![0-9A-Za-z_])[0-9A-Fa-f]{7,64}(?![0-9A-Za-z_])/g,
1580
+ )) {
1581
+ if (oid.startsWith(word.toLowerCase())) return true;
1582
+ }
1583
+ return false;
1584
+ }
1585
+
1586
+ /**
1587
+ * DR-088 §3: what the steps completed after `boundaryPrefixCount` committed,
1588
+ * one statement each, in boundary sequence order: every new commit where a
1589
+ * receipt proves more than one, and the commit already at HEAD where a step
1590
+ * whose outcomes all commit or ask named it over an `unchanged` receipt. A
1591
+ * step in a deferred chain reads the chain's cumulative receipt at its last
1592
+ * boundary.
1593
+ */
1594
+ function stepCommitStatements(
1595
+ ledger: PlaybookEffectLedger,
1596
+ boundaryPrefixCount: number,
1597
+ ): readonly string[] {
1598
+ const statements: string[] = [];
1599
+ for (const boundary of ledger.boundaries) {
1600
+ if (boundary.sequence <= boundaryPrefixCount) continue;
1601
+ let receipt = boundary.physicalReceipt;
1602
+ if (boundary.logicalOperationId !== undefined) {
1603
+ const operation = ledger.logicalOperations.find(
1604
+ ({ operationId }) => operationId === boundary.logicalOperationId,
1605
+ );
1606
+ if (operation?.boundaryIds.at(-1) !== boundary.boundaryId) continue;
1607
+ receipt = operation.logicalReceipt;
1608
+ }
1609
+ if (receipt === undefined) continue;
1610
+ if (
1611
+ receipt.classification === 'one-descendant-commit' &&
1612
+ receipt.commits !== undefined
1613
+ ) {
1614
+ statements.push(
1615
+ `The step made ${receipt.commits.length} new commits, oldest first: ${boundedPathList(receipt.commits)}.`,
1616
+ );
1617
+ continue;
1618
+ }
1619
+ const candidate = boundary.semanticCandidate;
1620
+ const guard =
1621
+ candidate !== null &&
1622
+ typeof candidate === 'object' &&
1623
+ !Array.isArray(candidate)
1624
+ ? (candidate as { readonly [key: string]: JsonValue }).guard
1625
+ : undefined;
1626
+ const head = receipt.after?.head;
1627
+ if (
1628
+ receipt.classification === 'unchanged' &&
1629
+ head !== undefined &&
1630
+ !/^0+$/.test(head) &&
1631
+ boundary.dispositions.includes('one-descendant-commit') &&
1632
+ !boundary.dispositions.includes('unchanged') &&
1633
+ typeof guard === 'string' &&
1634
+ guard !== BOSS_QUESTION_OUTCOME &&
1635
+ finalTextNamesCommit(boundary.finalText?.trim() ?? '', head)
1636
+ ) {
1637
+ statements.push(
1638
+ `The step made no new commit; it named ${head}, already at HEAD, as its work.`,
1639
+ );
1640
+ }
1641
+ }
1642
+ return statements;
1643
+ }
1644
+
1493
1645
  // DR-063 §4: the Boss-visible failure report. Every host sees the same
1494
1646
  // sentences because the shell composes them from the structured cause instead
1495
1647
  // of asking a model to describe facts it may drop. The report exposes no file
@@ -4250,6 +4402,15 @@ const unresolvedEffectFromReceipt = (
4250
4402
  baselineHead = receipt.baseline.head,
4251
4403
  ): PlaybookCaptainUnresolvedEffect | undefined => {
4252
4404
  if (receipt.classification === 'unchanged') return undefined;
4405
+ // DR-088 §3: a receipt listing more than one new commit reads as
4406
+ // `multiple-commits`, whose entry names more than one commit and no OID.
4407
+ if (receipt.commits !== undefined) {
4408
+ return {
4409
+ classification: 'multiple-commits',
4410
+ baselineHead,
4411
+ ...(receipt.after === undefined ? {} : { afterHead: receipt.after.head }),
4412
+ };
4413
+ }
4253
4414
  return {
4254
4415
  classification: receipt.classification,
4255
4416
  baselineHead,
@@ -4352,12 +4513,18 @@ const cumulativeOpenLogicalEffect = (
4352
4513
  receipt.classification === 'one-descendant-commit' &&
4353
4514
  (sameProjection || receiptAccountsFromOriginal)
4354
4515
  ) {
4355
- return {
4356
- classification: 'one-descendant-commit',
4357
- baselineHead: original.head,
4358
- afterHead: after.head,
4359
- commitOid: after.head,
4360
- };
4516
+ return receipt.commits === undefined
4517
+ ? {
4518
+ classification: 'one-descendant-commit',
4519
+ baselineHead: original.head,
4520
+ afterHead: after.head,
4521
+ commitOid: after.head,
4522
+ }
4523
+ : {
4524
+ classification: 'multiple-commits',
4525
+ baselineHead: original.head,
4526
+ afterHead: after.head,
4527
+ };
4361
4528
  }
4362
4529
  return {
4363
4530
  classification: 'observation-ambiguous',
@@ -4772,19 +4939,33 @@ export function createPlaybookCaptainShell(
4772
4939
  const reportCarriedPreExistingChanges = (turn: ActiveTurn): void => {
4773
4940
  if (turn.carriedEffectPrefix !== undefined) return;
4774
4941
  let carried: readonly CarriedPreExistingChanges[];
4942
+ let commitStatements: readonly string[];
4775
4943
  try {
4776
4944
  const ledger = assertPlaybookEffectLedger(currentEffectLedger());
4777
4945
  carried = carriedPreExistingChanges(ledger, presentedEffectPrefix);
4946
+ commitStatements = stepCommitStatements(ledger, presentedEffectPrefix);
4778
4947
  turn.carriedEffectPrefix = ledger.boundaries.length;
4779
4948
  } catch {
4780
4949
  return;
4781
4950
  }
4782
4951
  const report = carriedPreExistingReport(carried);
4783
- if (report === undefined) return;
4784
- const facts = carriedPreExistingFacts(carried);
4785
- appendMandatoryPresentationSuffix(turn, report, facts);
4786
- for (const fact of facts) {
4787
- joinSettlementFact(turn, fact);
4952
+ if (report !== undefined) {
4953
+ const facts = carriedPreExistingFacts(carried);
4954
+ appendMandatoryPresentationSuffix(turn, report, facts);
4955
+ for (const fact of facts) {
4956
+ joinSettlementFact(turn, fact);
4957
+ }
4958
+ }
4959
+ // DR-088 §3: the commits each step took, read from the same boundaries.
4960
+ if (commitStatements.length > 0) {
4961
+ appendMandatoryPresentationSuffix(
4962
+ turn,
4963
+ commitStatements.join('\n'),
4964
+ commitStatements,
4965
+ );
4966
+ for (const fact of commitStatements) {
4967
+ joinSettlementFact(turn, fact);
4968
+ }
4788
4969
  }
4789
4970
  };
4790
4971
 
@@ -8965,6 +9146,31 @@ export function createPlaybookCaptainShell(
8965
9146
  controls: failureControlLines(view, fenced),
8966
9147
  };
8967
9148
  joinSettlementFact(turn, fact, statement?.fact, speak);
9149
+ reportCommitsBeforeFailure(turn, unresolvedEffects);
9150
+ };
9151
+
9152
+ // DR-089: the commits the turn's receipts proved before its leaf parked,
9153
+ // stated beside the failure report and joined to the settlement facts the
9154
+ // closing-reply prompt reads.
9155
+ const reportCommitsBeforeFailure = (
9156
+ turn: ActiveTurn,
9157
+ unresolvedEffects: readonly PlaybookCaptainUnresolvedEffect[],
9158
+ ): void => {
9159
+ let commits: readonly CommitBeforeFailure[];
9160
+ try {
9161
+ commits = commitsBeforeFailure(
9162
+ assertPlaybookEffectLedger(currentEffectLedger()),
9163
+ presentedEffectPrefix,
9164
+ unresolvedEffects,
9165
+ );
9166
+ } catch {
9167
+ return;
9168
+ }
9169
+ const report = commitsBeforeFailureReport(commits);
9170
+ if (report === undefined) return;
9171
+ const facts = commits.map(commitBeforeFailureFact);
9172
+ appendMandatoryPresentationSuffix(turn, report, facts);
9173
+ for (const fact of facts) joinSettlementFact(turn, fact);
8968
9174
  };
8969
9175
 
8970
9176
  // The runtimes own each frame's position. The shell only joins their stack.
@@ -87,9 +87,15 @@ export function createAssetStore(options) {
87
87
  const directory = resolve(options.directory);
88
88
  const maxAssetBytes = options.maxAssetBytes ?? DEFAULT_MAX_ASSET_BYTES;
89
89
  if (!Number.isSafeInteger(maxAssetBytes) || maxAssetBytes <= 0) throw new TypeError('maximum asset bytes must be a positive safe integer');
90
+ // A session store forbidding creation of its sessions directory creates
91
+ // only this leaf inside it (session-storage-11).
92
+ const createAncestors = options.createAncestors ?? true;
90
93
  let importTail = Promise.resolve();
91
94
  const ensureDirectory = async (create) => {
92
- if (create) await mkdir(directory, { recursive: true, mode: 0o700 });
95
+ if (create) {
96
+ try { await mkdir(directory, { recursive: createAncestors, mode: 0o700 }); }
97
+ catch (error) { if (createAncestors || error.code !== 'EEXIST') throw error; }
98
+ }
93
99
  assertDirectory(await lstat(directory));
94
100
  };
95
101
  const prepare = async () => {
@@ -285,7 +285,19 @@ export interface SharedSessionStore {
285
285
  migrateLegacyDefault(options?: { env?: Readonly<Record<string, string | undefined>>; homeDir?: string }): Promise<{ sourceDir: string; migrated: readonly string[]; skipped: readonly { sessionId: string; reason: string }[] }>;
286
286
  migrate(sessionId: string, options?: { sourcePath?: string; cwd?: string; backupDir?: string }): Promise<{ manifest: SessionManifest; migrated: boolean; reasons: readonly string[] }>;
287
287
  }
288
- export declare function createSessionStore(options?: { maxAssetBytes?: number; sessionsDir?: string; env?: Readonly<Record<string, string | undefined>>; homeDir?: string; [key: string]: any }): SharedSessionStore;
288
+ export declare function createSessionStore(options?: {
289
+ maxAssetBytes?: number;
290
+ sessionsDir?: string;
291
+ /**
292
+ * Default `true`. `false` refuses, rather than creates, a missing sessions directory or ancestor when preparing
293
+ * session payload, lease, asset or migration directories. Machine-identity provisioning keeps its own rule and may
294
+ * still create its directory, including an ancestor it shares with the sessions directory.
295
+ */
296
+ createSessionsDir?: boolean;
297
+ env?: Readonly<Record<string, string | undefined>>;
298
+ homeDir?: string;
299
+ [key: string]: any;
300
+ }): SharedSessionStore;
289
301
  export declare function validateSessionManifest(value: unknown): SessionManifest;
290
302
  export declare function validateSessionContext(value: unknown): SessionContext;
291
303
  export declare function projectCaptainSessionStructure(value: SessionExecutionProjection): SessionStructuralProjection;
@@ -21,9 +21,19 @@ export function openSessionStore(sessionsDir) {
21
21
  }
22
22
 
23
23
  function wrapStore(store) {
24
+ // Preparation is kept once it succeeds; a refusal is not, so the next
25
+ // operation prepares again once the directory is repaired
26
+ // (session-storage-1).
24
27
  let preparation;
25
28
  const prepared = async (operation) => {
26
- await (preparation ??= store.prepare());
29
+ if (preparation === undefined) {
30
+ const attempt = store.prepare();
31
+ preparation = attempt;
32
+ attempt.catch(() => {
33
+ if (preparation === attempt) preparation = undefined;
34
+ });
35
+ }
36
+ await preparation;
27
37
  return operation();
28
38
  };
29
39
  return Object.freeze({
package/slc/link.md CHANGED
@@ -341,6 +341,7 @@ interface PlaybookRepositoryReceipt {
341
341
  readonly baseline: PlaybookRepositoryObservation;
342
342
  readonly after?: PlaybookRepositoryObservation;
343
343
  readonly commitOid?: string;
344
+ readonly commits?: readonly string[];
344
345
  }
345
346
 
346
347
  type PlaybookRepositoryDisposition =
@@ -1286,6 +1287,12 @@ runtime-owned payload field; `guard` shall name exactly one outcome declared
1286
1287
  by both the live result map and `outcomeAuthority`; and every semantic-owned
1287
1288
  field shall satisfy the result map's required-field type before any actor
1288
1289
  output is delivered.
1290
+ That type is the value shape the compiled FSM's own validator admits for the
1291
+ field: the shared engine reads it once at construction by presenting each
1292
+ governed outcome's accepting arm with sample values of a closed set of shapes,
1293
+ a field whose shape the FSM admits as a string or does not reveal stays a
1294
+ string, and the judge prompt and validation error name every non-string shape
1295
+ ([DR-089](../specs/decisions/089-judge-replies-take-the-machines-shapes.md)).
1289
1296
  The judge prompt shall render each governed outcome's description with its
1290
1297
  meaning verbatim and its `Output shall include` clause replaced by that reply
1291
1298
  contract — exactly `guard` plus the semantic-owned fields, each keeping its
@@ -1320,10 +1327,16 @@ physical receipt, or the complete cumulative logical receipt of a deferred
1320
1327
  operation, whose classification is exactly the outcome's declared
1321
1328
  `unchanged` or `one-descendant-commit` disposition; a `one-descendant-commit`
1322
1329
  receipt shall carry exactly the after-HEAD OID used for the arm's effect-owned
1323
- fields, while an `unchanged` receipt's complete validated observation supplies
1324
- its observed HEAD OID for them, and a receipt that cannot prove that observed
1325
- HEAD shall leave the envelope unresolved rather than inject a fabricated
1326
- value.
1330
+ fields, and lists every new commit in `commits` where HEAD advanced by more
1331
+ than one, while an `unchanged` receipt's complete validated observation
1332
+ supplies its observed HEAD OID for them, and a receipt that cannot prove that
1333
+ observed HEAD shall leave the envelope unresolved rather than inject a
1334
+ fabricated value.
1335
+ A `one-descendant-commit` outcome shall also resolve over an `unchanged`
1336
+ receipt whose HEAD names a commit when no outcome of that call declares
1337
+ `unchanged` and the trimmed `finalText` names that commit as a word, by its
1338
+ full OID or an abbreviation of at least seven leading hex digits; its
1339
+ effect-owned fields then take that HEAD OID.
1327
1340
  A `deferred` candidate shall be admissible only for its already-validated
1328
1341
  effect-authorized `needsBossReply` outcome and only from a complete after
1329
1342
  observation whose HEAD equals the logical operation's original baseline HEAD
@@ -1373,10 +1386,16 @@ player or judge call.
1373
1386
  The adjudicator shall use the same document-order tolerant JSON recovery as
1374
1387
  the Boss classifier. Unlike invalid classification, a reply from which no
1375
1388
  object can be recovered, an undeclared guard, or a missing required field is a
1376
- control-plane error for nongoverned delegated-player and direct-Captain adjudication and
1389
+ control-plane error for nongoverned delegated-player adjudication and
1377
1390
  shall throw after the invocation reaches its FSM error path and ordered
1378
1391
  emissions drain; schema-3 governed adjudication follows the bounded
1379
1392
  reconciliation contract above instead.
1393
+ Direct-Captain adjudication treats such a reply, or one carrying an undeclared
1394
+ field, as a structural error: it makes exactly one corrective adjudication over
1395
+ the same visible output that restates the reply and its validation error, and
1396
+ a second structural error routes the invocation to its FSM failure state as a
1397
+ recoverable `judge-failed` failure rather than a control-plane error
1398
+ ([DR-089](../specs/decisions/089-judge-replies-take-the-machines-shapes.md)).
1380
1399
 
1381
1400
  Two default adjudication strategies, in selection order:
1382
1401
 
@@ -1389,7 +1408,11 @@ Two default adjudication strategies, in selection order:
1389
1408
  `{ guard, …semanticOwnedPayloadFields }` and explicitly forbids every other
1390
1409
  payload field. The governed form also permits the blocked report above when no outcome matches.
1391
1410
  Both forms exclude the runtime-injected direct-Captain
1392
- `question` and `response` fields above. The prompt shall identify hidden control work,
1411
+ `question` and `response` fields above. A direct-Captain prompt keeps each
1412
+ description and states under it the exact reply that key takes — `guard`
1413
+ plus the structural fields its `Output shall include` clause names, each with
1414
+ its authored placeholder — naming `question` and `response` as
1415
+ runtime-supplied. The prompt shall identify hidden control work,
1393
1416
  prohibit tool use, file inspection, and external evidence, direct the judge
1394
1417
  to decide only from the supplied actor output and declared outcomes, and
1395
1418
  require exactly one JSON object with no prose. The judge prompt shall not
package/slc/text2gears.md CHANGED
@@ -35,7 +35,8 @@ E.g.:
35
35
  - Coder
36
36
  - Reviewer
37
37
 
38
- Capitalize English role names (e.g., `Writer`); quote non-English names (e.g., `作者`) when needed to distinguish from prose.
38
+ Each role name shall be one capitalized word — an uppercase letter, or a letter of a script without case, followed only by letters and digits — so a two-word role is written as one word, e.g. `RiskOfficer`, never `Risk Officer`.
39
+ Quote non-English names (e.g., `作者`) when needed to distinguish from prose.
39
40
 
40
41
  ## Behaviors
41
42
 
@@ -67,14 +68,15 @@ Boss-visible contract and is nonconformant.
67
68
 
68
69
  ### Authored prompt fragments
69
70
 
70
- Source may compose one acting prompt from authored Markdown instruction blocks and runtime context that it explicitly says to relay in quotes (`>`).
71
+ Source may compose one acting prompt from authored Markdown instruction blocks and runtime context that it explicitly marks as relayed in quotes.
72
+ The relay marker is a backticked `>` in ASCII or full-width parentheses, `` (`>`) `` or `` (`>`) ``, in the prose introducing the blockquote; the words around it, such as "in quotes" or "以引用形式", are prose in the Source's language.
71
73
  A fenced `markdown` block introduced as an instruction or prompt is an authored static prompt fragment: its fence delimiters are Source syntax, while every interior line and blank line is prompt content preserved after documented Markdown unescaping.
72
74
 
73
75
  An instruction fence and a relayed-context fragment that apply to one behavior shall appear in the target blockquote in their Source order.
74
76
  Distinct non-empty fragments shall be separated by one blank prompt line unless Source explicitly supplies a different boundary.
75
77
  text2gears shall not move a shared instruction ahead of behavior-specific context, move quoted evidence after an instruction that Source says follows the evidence, or otherwise regroup fragments for convenience.
76
78
 
77
- Where Source says that a runtime value is relayed in quotes, the leading `>` is prompt content rather than Source-only blockquote syntax.
79
+ Where Source marks a runtime value as relayed in quotes, the leading `>` is prompt content rather than Source-only blockquote syntax.
78
80
  If Source supplies a blockquoted template for that relay, text2gears shall keep one literal leading `>` on every quoted line; the target GEARS line therefore uses its outer blockquote marker followed by the literal marker, such as `> > Coder output: <coder-output>`.
79
81
  If Source names the relayed value but supplies no template, text2gears shall emit a bare quoted prompt-content line, exactly `> <token>`, written as `> > <token>` in the GEARS file because the first marker encloses the prompt and the second is literal content, without an added label or surrounding prose, and shall not summarize, paraphrase, or invent the relayed value.
80
82
  An ordinary Source blockquote that specifies a complete acting prompt without requiring quoted relay retains the existing rule above: its one leading marker is Source syntax and is not prompt content.
package/src/runtime.d.ts CHANGED
@@ -255,6 +255,12 @@ export interface PlaybookRepositoryReceipt {
255
255
  readonly baseline: PlaybookRepositoryObservation;
256
256
  readonly after?: PlaybookRepositoryObservation;
257
257
  readonly commitOid?: string;
258
+ /**
259
+ * Every new commit, oldest first and ending at `commitOid`, present exactly
260
+ * when a `one-descendant-commit` receipt's HEAD advanced by more than one
261
+ * (DR-088).
262
+ */
263
+ readonly commits?: readonly string[];
258
264
  readonly preExisting?: PlaybookRepositoryPreExistingChanges;
259
265
  }
260
266
  /** One durably ordered physical governed-player boundary (DR-040). */
package/src/runtime.ts CHANGED
@@ -618,6 +618,12 @@ export interface PlaybookRepositoryReceipt {
618
618
  readonly baseline: PlaybookRepositoryObservation;
619
619
  readonly after?: PlaybookRepositoryObservation;
620
620
  readonly commitOid?: string;
621
+ /**
622
+ * Every new commit, oldest first and ending at `commitOid`, present exactly
623
+ * when a `one-descendant-commit` receipt's HEAD advanced by more than one
624
+ * (DR-088).
625
+ */
626
+ readonly commits?: readonly string[];
621
627
  readonly preExisting?: PlaybookRepositoryPreExistingChanges;
622
628
  }
623
629
 
@@ -1,4 +1,5 @@
1
1
  import type { AnyStateMachine, EventObject, PromiseActorLogic } from 'xstate';
2
+ import { type PlaybookSemanticValueShape, type PlaybookSemanticValueShapes } from './xstate-runtime.js';
2
3
  import type { CaptainResult, JsonValue, PlaybookEffectBoundary, PlaybookEffectBoundaryStart, PlaybookEffectLedger, PlaybookEffectLedgerCapability, NormalizedError, PlaybookFailureCause, PlaybookPendingBossQuestion, PlaybookPorts, PlaybookRepositoryReceipt, PlaybookRuntimeFactory, PlaybookSession, PlaybookState, PlayerResult } from './runtime.js';
3
4
  export interface PlaybookPendingBossQuestionContext {
4
5
  questionId: string;
@@ -494,7 +495,7 @@ export declare function defaultBuildJudgePrompt(input: PlaybookPlayerInput, fina
494
495
  * self-inflicted defect. Exported so linked machinery of an artifact's own
495
496
  * renders the identical contract instead of restating it.
496
497
  */
497
- export declare function renderGovernedOutcomeContract(guard: string, description: string, outcome: XStateGovernedOutcomeSpec | undefined): string[];
498
+ export declare function renderGovernedOutcomeContract(guard: string, description: string, outcome: XStateGovernedOutcomeSpec | undefined, shapes?: Readonly<Record<string, PlaybookSemanticValueShape>>): string[];
498
499
  export interface PlayerAdjudicationSpec {
499
500
  buildJudgePrompt?: (input: PlaybookPlayerInput, finalText: string) => string;
500
501
  extractRequiredFields?: (description: string) => string[];
@@ -522,13 +523,22 @@ export declare function createPlayerBridge(spec: PlayerBridgeSpec, ports: Playbo
522
523
  /**
523
524
  * Default direct-Captain adjudicator prompt (DR-025). The single statement of
524
525
  * the `{ guard, …structuralPayloadFields }` reply contract, shared with the
525
- * compiled default Captain artifact so the wording cannot drift.
526
+ * compiled default Captain artifact so the wording cannot drift. Each declared
527
+ * key keeps its description verbatim and gains the exact reply it takes
528
+ * (DR-089): `guard` plus the structural fields its `Output shall include`
529
+ * clause names, each with its authored placeholder, while the visible
530
+ * `question` and `response` fields are named as runtime-supplied. A
531
+ * `correction` restates a structurally invalid first reply and its
532
+ * validation error for the one corrective adjudication.
526
533
  */
527
534
  export declare function defaultBuildCaptainJudgePrompt(input: {
528
535
  readonly stateId: string;
529
536
  readonly sourceItem: string;
530
537
  readonly result: Readonly<Record<string, string>>;
531
- }, finalText: string): string;
538
+ }, finalText: string, correction?: {
539
+ readonly reply: string;
540
+ readonly error: string;
541
+ }): string;
532
542
  /**
533
543
  * Targets of the FSM's `awaitBossReply` BOSS_REPLY transitions, plus — for
534
544
  * the parallel shape gears2fsm compiles (DR-067) — the targets of each
@@ -554,6 +564,13 @@ export declare function stateDescriptionsFromMachine(machine: AnyStateMachine):
554
564
  * other than `success` or `failure`, is a malformed artifact and throws.
555
565
  */
556
566
  export declare function terminalOutcomesFromMachine(machine: AnyStateMachine, label?: string): ReadonlyMap<string, 'success' | 'failure'>;
567
+ /**
568
+ * DR-089: the non-string JSON value shape the compiled machine admits for
569
+ * each semantic-owned field of each governed outcome, keyed by state id and
570
+ * then by outcome. Exported so linked machinery of an artifact's own reads
571
+ * the same contract the shared factory enforces.
572
+ */
573
+ export declare function inferSemanticValueShapes(machine: AnyStateMachine, authority: XStateOutcomeAuthoritySpec): ReadonlyMap<string, PlaybookSemanticValueShapes>;
557
574
  /**
558
575
  * Build a `PlaybookRuntimeFactory` that interprets the given FSM artifact
559
576
  * under the slc/link.md contract. The factory provides every actor kind the