@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.
- package/package.json +1 -1
- package/reference/sdlc/code.playbook/bin/machine-identity.js +18 -0
- package/reference/sdlc/code.playbook/bin/repository-effects.js +41 -23
- package/reference/sdlc/code.playbook/bin/session-store.js +73 -23
- package/reference/sdlc/code.playbook/host-capabilities.d.ts +2 -0
- package/reference/sdlc/code.playbook/playbook-captain.js +162 -12
- package/reference/sdlc/code.playbook/playbook-captain.ts +217 -11
- package/reference/sdlc/code.playbook/session-assets.js +7 -1
- package/reference/sdlc/code.playbook/session-store.d.ts +13 -1
- package/reference/sdlc/code.playbook/session-store.js +11 -1
- package/slc/link.md +29 -6
- package/slc/text2gears.md +5 -3
- package/src/runtime.d.ts +6 -0
- package/src/runtime.ts +6 -0
- package/src/xstate-playbook-runtime.d.ts +20 -3
- package/src/xstate-playbook-runtime.js +267 -18
- package/src/xstate-playbook-runtime.ts +375 -21
- package/src/xstate-runtime.d.ts +23 -3
- package/src/xstate-runtime.js +97 -13
- package/src/xstate-runtime.ts +159 -19
|
@@ -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
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
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
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
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)
|
|
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?: {
|
|
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
|
-
|
|
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,
|
|
1324
|
-
|
|
1325
|
-
HEAD
|
|
1326
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|