@sublang/playbook 12.1.0 → 12.2.1
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/playbook-captain.js +10 -0
- package/reference/sdlc/code.playbook/playbook-captain.ts +9 -0
- package/reference/sdlc/decide.playbook/decide.playbook.js +11 -7
- package/reference/sdlc/decide.playbook/decide.playbook.ts +11 -8
- package/slc/gears2fsm.md +3 -0
- package/slc/link.md +16 -1
- package/slc/text2gears.md +12 -0
- package/src/xstate-playbook-runtime.d.ts +15 -0
- package/src/xstate-playbook-runtime.js +127 -22
- package/src/xstate-playbook-runtime.ts +149 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sublang/playbook",
|
|
3
|
-
"version": "12.1
|
|
3
|
+
"version": "12.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Composable XState v5 playbook runtime with compiled Captain, CODE, REVIEW, DECIDE, and DEV workflows driven by GEARS specs.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -64,6 +64,10 @@ const ROLE_ID_PATTERN = /^[a-z][a-z0-9_-]*$/;
|
|
|
64
64
|
const HOST_CAPABILITIES_OPTION_KEY = 'hostCapabilities';
|
|
65
65
|
const UNRESOLVED_EFFECT_RECONCILIATION_ACTION_ID = 'reconcile:unresolved-effect';
|
|
66
66
|
const UNRESOLVED_EFFECT_ABANDONMENT_ACTION_ID = 'abandon:unresolved-effect';
|
|
67
|
+
// The fixed machine-syntax guard of a player's Boss-question suspension
|
|
68
|
+
// (slc/link.md §Boss-reply suspension): the accepted outcome that parks on
|
|
69
|
+
// Boss rather than sparing Boss a relay.
|
|
70
|
+
const BOSS_QUESTION_OUTCOME = 'needsBossReply';
|
|
67
71
|
const RESUMPTION_DUPLICATE_EFFECT_WARNING = 'Warning: resumption may duplicate external effects attempted after the retained boundary; verify the current world before continuing.';
|
|
68
72
|
function parseRegisteredCommand(prompt) {
|
|
69
73
|
const match = /^\/([A-Za-z][A-Za-z0-9_-]*)(?:\s+([\s\S]*))?$/.exec(prompt.trim());
|
|
@@ -2550,6 +2554,12 @@ export function createPlaybookCaptainShell(options, deps = {}) {
|
|
|
2550
2554
|
if (summary.acceptedOutcomeTraceKeys.has(traceKey))
|
|
2551
2555
|
return;
|
|
2552
2556
|
summary.acceptedOutcomeTraceKeys.add(traceKey);
|
|
2557
|
+
// CAPTAIN-19/20: a saved interruption is a player reply Boss did not
|
|
2558
|
+
// have to relay. A Boss-question suspension is the one accepted outcome
|
|
2559
|
+
// that parks on Boss instead, so it saves nothing and counts nothing —
|
|
2560
|
+
// a turn that only parked must not claim it saved an interruption.
|
|
2561
|
+
if (receipt.acceptedOutcome === BOSS_QUESTION_OUTCOME)
|
|
2562
|
+
return;
|
|
2553
2563
|
summary.counts.interruptions++;
|
|
2554
2564
|
if (frame.entry.summaryPolicy?.copyPasteGuardNames.includes(receipt.acceptedOutcome)) {
|
|
2555
2565
|
summary.counts.copyPastes++;
|
|
@@ -554,6 +554,10 @@ const HOST_CAPABILITIES_OPTION_KEY = 'hostCapabilities';
|
|
|
554
554
|
const UNRESOLVED_EFFECT_RECONCILIATION_ACTION_ID =
|
|
555
555
|
'reconcile:unresolved-effect';
|
|
556
556
|
const UNRESOLVED_EFFECT_ABANDONMENT_ACTION_ID = 'abandon:unresolved-effect';
|
|
557
|
+
// The fixed machine-syntax guard of a player's Boss-question suspension
|
|
558
|
+
// (slc/link.md §Boss-reply suspension): the accepted outcome that parks on
|
|
559
|
+
// Boss rather than sparing Boss a relay.
|
|
560
|
+
const BOSS_QUESTION_OUTCOME = 'needsBossReply';
|
|
557
561
|
const RESUMPTION_DUPLICATE_EFFECT_WARNING =
|
|
558
562
|
'Warning: resumption may duplicate external effects attempted after the retained boundary; verify the current world before continuing.';
|
|
559
563
|
|
|
@@ -4293,6 +4297,11 @@ export function createPlaybookCaptainShell(
|
|
|
4293
4297
|
const traceKey = `${frame.sessionId}:${trace.sequence}`;
|
|
4294
4298
|
if (summary.acceptedOutcomeTraceKeys.has(traceKey)) return;
|
|
4295
4299
|
summary.acceptedOutcomeTraceKeys.add(traceKey);
|
|
4300
|
+
// CAPTAIN-19/20: a saved interruption is a player reply Boss did not
|
|
4301
|
+
// have to relay. A Boss-question suspension is the one accepted outcome
|
|
4302
|
+
// that parks on Boss instead, so it saves nothing and counts nothing —
|
|
4303
|
+
// a turn that only parked must not claim it saved an interruption.
|
|
4304
|
+
if (receipt.acceptedOutcome === BOSS_QUESTION_OUTCOME) return;
|
|
4296
4305
|
summary.counts.interruptions++;
|
|
4297
4306
|
if (
|
|
4298
4307
|
frame.entry.summaryPolicy?.copyPasteGuardNames.includes(
|
|
@@ -19,7 +19,7 @@ import { randomUUID } from 'node:crypto';
|
|
|
19
19
|
import PQueue from 'p-queue';
|
|
20
20
|
import { createActor, fromPromise } from 'xstate';
|
|
21
21
|
import { createAcceptedOutcomeConsumer, } from '../../../src/accepted-outcome.js';
|
|
22
|
-
import { assertJsonSafe, assertPlaybookEffectLedger, assertPlaybookRuntimeSnapshot, combineAbortSignals, createNestedPlaybookBridge, detachPersistedMachineSnapshot, normalizeError, normalizePlaybookSnapshot, PlaybookSemanticCandidateStructureError, reconcilePlaybookSemanticEvidence, snapshotJsonValue, snapshotPlaybookSession, validatePlayerResult, waitForPlaybookQuiescence, } from '../../../src/xstate-runtime.js';
|
|
22
|
+
import { assertJsonSafe, assertPlaybookEffectLedger, assertPlaybookRuntimeSnapshot, combineAbortSignals, createNestedPlaybookBridge, detachPersistedMachineSnapshot, normalizeError, normalizePlaybookSnapshot, PlaybookSemanticCandidateStructureError, reconcilePlaybookSemanticEvidence, renderGovernedOutcomeContract, snapshotJsonValue, snapshotPlaybookSession, validatePlayerResult, waitForPlaybookQuiescence, } from '../../../src/xstate-runtime.js';
|
|
23
23
|
import decideMachine from './decide.fsm.js';
|
|
24
24
|
function snapshotDecideRuntimeOptions(value) {
|
|
25
25
|
const captured = snapshotJsonValue(value, 'DECIDE runtime options');
|
|
@@ -429,15 +429,19 @@ function buildAdjudicatorPrompt(input, playerOutput, correction) {
|
|
|
429
429
|
lines.push('"""');
|
|
430
430
|
lines.push('');
|
|
431
431
|
lines.push('Guards (choose exactly one; the descriptions are authoritative and must be applied as written):');
|
|
432
|
+
// The shared engine's renderer (slc/link.md §Captain adjudication): each
|
|
433
|
+
// arm's meaning verbatim, with its `Output shall include` clause — authored
|
|
434
|
+
// for the complete actor output — replaced by the reply contract the
|
|
435
|
+
// outcome authority gives the judge. Rendering the clause verbatim asked
|
|
436
|
+
// the judge for presentation-owned `coderProposal` or `question`, which the
|
|
437
|
+
// reconciler rejects, spending the single correction on a self-inflicted
|
|
438
|
+
// defect.
|
|
432
439
|
for (const [guard, description] of Object.entries(input.result)) {
|
|
433
|
-
|
|
434
|
-
.filter(([, authority]) => authority === 'semantic')
|
|
435
|
-
.map(([field]) => field);
|
|
436
|
-
lines.push(`- ${guard}: semantic fields: ${semanticFields.length === 0 ? '(none)' : semanticFields.join(', ')}; ${description}`);
|
|
440
|
+
lines.push(...renderGovernedOutcomeContract(guard, description, outcomes[guard]));
|
|
437
441
|
}
|
|
438
442
|
lines.push('');
|
|
439
|
-
lines.push(
|
|
440
|
-
lines.push('
|
|
443
|
+
lines.push("Pick exactly one declared `guard` and reply with exactly that outcome's reply shape above: `guard` plus its semantic-owned fields and nothing else.");
|
|
444
|
+
lines.push('A field listed as runtime-supplied is owned by presentation, effect, or runtime evidence; the runtime fills it itself, and a reply that includes one is structurally invalid.');
|
|
441
445
|
if (correction !== undefined) {
|
|
442
446
|
lines.push('');
|
|
443
447
|
lines.push('Your first reply was structurally invalid:');
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
normalizePlaybookSnapshot,
|
|
39
39
|
PlaybookSemanticCandidateStructureError,
|
|
40
40
|
reconcilePlaybookSemanticEvidence,
|
|
41
|
+
renderGovernedOutcomeContract,
|
|
41
42
|
snapshotJsonValue,
|
|
42
43
|
snapshotPlaybookSession,
|
|
43
44
|
validatePlayerResult,
|
|
@@ -660,22 +661,24 @@ function buildAdjudicatorPrompt(
|
|
|
660
661
|
lines.push(
|
|
661
662
|
'Guards (choose exactly one; the descriptions are authoritative and must be applied as written):',
|
|
662
663
|
);
|
|
664
|
+
// The shared engine's renderer (slc/link.md §Captain adjudication): each
|
|
665
|
+
// arm's meaning verbatim, with its `Output shall include` clause — authored
|
|
666
|
+
// for the complete actor output — replaced by the reply contract the
|
|
667
|
+
// outcome authority gives the judge. Rendering the clause verbatim asked
|
|
668
|
+
// the judge for presentation-owned `coderProposal` or `question`, which the
|
|
669
|
+
// reconciler rejects, spending the single correction on a self-inflicted
|
|
670
|
+
// defect.
|
|
663
671
|
for (const [guard, description] of Object.entries(input.result)) {
|
|
664
|
-
const semanticFields = Object.entries(outcomes[guard]?.fields ?? {})
|
|
665
|
-
.filter(([, authority]) => authority === 'semantic')
|
|
666
|
-
.map(([field]) => field);
|
|
667
672
|
lines.push(
|
|
668
|
-
|
|
669
|
-
semanticFields.length === 0 ? '(none)' : semanticFields.join(', ')
|
|
670
|
-
}; ${description}`,
|
|
673
|
+
...renderGovernedOutcomeContract(guard, description, outcomes[guard]),
|
|
671
674
|
);
|
|
672
675
|
}
|
|
673
676
|
lines.push('');
|
|
674
677
|
lines.push(
|
|
675
|
-
|
|
678
|
+
"Pick exactly one declared `guard` and reply with exactly that outcome's reply shape above: `guard` plus its semantic-owned fields and nothing else.",
|
|
676
679
|
);
|
|
677
680
|
lines.push(
|
|
678
|
-
'
|
|
681
|
+
'A field listed as runtime-supplied is owned by presentation, effect, or runtime evidence; the runtime fills it itself, and a reply that includes one is structurally invalid.',
|
|
679
682
|
);
|
|
680
683
|
if (correction !== undefined) {
|
|
681
684
|
lines.push('');
|
package/slc/gears2fsm.md
CHANGED
|
@@ -385,6 +385,9 @@ implementation.
|
|
|
385
385
|
|
|
386
386
|
A literal call shall retain the existing representation: `playbookId` is the
|
|
387
387
|
literal target and `text` is the composed GEARS blockquote.
|
|
388
|
+
Composing that text follows the quoted-relay rules of [link](link.md): a
|
|
389
|
+
relayed value that is empty contributes no line, a multi-line value is quoted
|
|
390
|
+
line by line, and the composer inserts no empty quoted line of its own.
|
|
388
391
|
|
|
389
392
|
A dynamic call written
|
|
390
393
|
``Captain shall call playbook selected by `<target-field>`:`` shall declare the
|
package/slc/link.md
CHANGED
|
@@ -1079,6 +1079,15 @@ runtime-owned payload field; `guard` shall name exactly one outcome declared
|
|
|
1079
1079
|
by both the live result map and `outcomeAuthority`; and every semantic-owned
|
|
1080
1080
|
field shall satisfy the result map's required-field type before any actor
|
|
1081
1081
|
output is delivered.
|
|
1082
|
+
The judge prompt shall render each governed outcome's description with its
|
|
1083
|
+
meaning verbatim and its `Output shall include` clause replaced by that reply
|
|
1084
|
+
contract — exactly `guard` plus the semantic-owned fields, each keeping its
|
|
1085
|
+
authored placeholder or guidance, with every presentation-, effect-, or
|
|
1086
|
+
runtime-owned field named as runtime-supplied to omit — so the judge is never asked for a
|
|
1087
|
+
field it does not own; the artifact's description text stays unchanged.
|
|
1088
|
+
The shared engine shall export that rendering as `renderGovernedOutcomeContract`
|
|
1089
|
+
on `@sublang/playbook/xstate-runtime`, and a bespoke linked runtime shall
|
|
1090
|
+
render its judge prompt through it rather than restate the contract.
|
|
1082
1091
|
The reconciler shall construct the complete actor output rather than accept a
|
|
1083
1092
|
cross-authority object from the judge: every presentation-owned payload field
|
|
1084
1093
|
shall receive the canonical `finalText.trim()` value; every effect-owned
|
|
@@ -1170,7 +1179,9 @@ Two default adjudication strategies, in selection order:
|
|
|
1170
1179
|
to decide only from the supplied actor output and declared outcomes, and
|
|
1171
1180
|
require exactly one JSON object with no prose. The judge prompt shall not
|
|
1172
1181
|
interpret the player's output, paraphrase it, or alter the FSM's `result`
|
|
1173
|
-
text — it carries the description verbatim
|
|
1182
|
+
text — it carries the description verbatim, except that a governed
|
|
1183
|
+
schema-3 outcome's `Output shall include` clause is rendered as the
|
|
1184
|
+
authority-derived reply contract of §Captain adjudication.
|
|
1174
1185
|
- **Marker-parse** (delegated-player alternative): a deterministic parser that
|
|
1175
1186
|
scans the player output for a terminal control line such as
|
|
1176
1187
|
`FSM-RESULT: { "guard": "...", ... }`. Useful when player adapters can
|
|
@@ -2402,6 +2413,10 @@ original template. Replacement strings are literal: placeholder-looking text
|
|
|
2402
2413
|
inside Boss/catalog/plan/result values and JavaScript replacement tokens such
|
|
2403
2414
|
as `$&`, `$$`, dollar-backtick, and `$'` shall not be interpreted or
|
|
2404
2415
|
substituted again.
|
|
2416
|
+
A quoted relay line `> <placeholder>` whose value is empty shall be omitted
|
|
2417
|
+
from the composed text rather than left as an empty quoted line, a multi-line
|
|
2418
|
+
value shall be quoted line by line so every continuation line keeps its `>`
|
|
2419
|
+
marker, and the composer shall insert no empty quoted line of its own.
|
|
2405
2420
|
|
|
2406
2421
|
## Host adaptation (informative, not normative)
|
|
2407
2422
|
|
package/slc/text2gears.md
CHANGED
|
@@ -145,6 +145,11 @@ The guard name shall match the ASCII identifier pattern
|
|
|
145
145
|
The bullet order is authoritative, guard names are unique within the item, and
|
|
146
146
|
the description shall name every required output property with its exact
|
|
147
147
|
case-sensitive identifier.
|
|
148
|
+
An output property name shall match the same ASCII identifier pattern as a
|
|
149
|
+
guard name: a kebab-case Source placeholder such as `<coder-output>` names the
|
|
150
|
+
property `coderOutput` through the canonical kebab-token-to-camel-field mapping
|
|
151
|
+
of [link](link.md), never a quoted kebab-case key, because downstream artifacts
|
|
152
|
+
and calling playbooks consume these properties by name.
|
|
148
153
|
|
|
149
154
|
A produced value consumed later shall have a declared producer: where any
|
|
150
155
|
later item's blockquote reads a value through a `<placeholder>`, the item
|
|
@@ -242,6 +247,12 @@ text2gears shall emit an item whose behavior uses
|
|
|
242
247
|
complete JSON-safe input-text template for that call.
|
|
243
248
|
The literal target id shall be a stable configured playbook id, not a slash
|
|
244
249
|
command or module specifier.
|
|
250
|
+
A nested-call item shall carry no `Results:` label: the child's terminal result
|
|
251
|
+
is its outcome, so Source's continuation after child success, abort, or failure
|
|
252
|
+
stays as prose after the blockquote for
|
|
253
|
+
[gears2fsm](gears2fsm.md#nested-playbook-calls) to route through
|
|
254
|
+
`invoke.onDone` and `invoke.onError`; a `Results:` block on a nested-call item
|
|
255
|
+
is malformed.
|
|
245
256
|
|
|
246
257
|
Example:
|
|
247
258
|
|
|
@@ -323,6 +334,7 @@ exact English form regardless of Source language.
|
|
|
323
334
|
A Source may itself be the normative specification of a transformation — e.g., a compiler phase definition, as when a meta pipeline compiles this file.
|
|
324
335
|
Such a Source declares no roles and prompts none; Captain performs the specified transformation on request.
|
|
325
336
|
Where such a Source carries a `## Compiled execution` section, text2gears shall compile it from that section alone: the section is the Source's complete behavior — its acting item, prompt, and `Results:` contract — and the remaining definition text is relayed content, not behaviors to transcribe, so the composition below applies only to a Source without that section.
|
|
337
|
+
That section's blockquote is complete as authored: text2gears shall emit it verbatim and shall append no relay line to it — the `<definition>` placeholder is its only runtime value, and the undelivered-value rule above does not add `<boss-intent>` or any other placeholder to it.
|
|
326
338
|
Compose Captain-acting spec items for it: when a transformation request names the specification's source and target, Captain shall carry out the transformation as specified.
|
|
327
339
|
Prompts shall carry the specification's normative requirements as instructions to Captain — deduplicated, one point per line — without inventing roles, triggers, or requirements the specification does not state.
|
|
328
340
|
|
|
@@ -412,6 +412,21 @@ export declare function defaultComposeCaptainPrompt(input: PlaybookCaptainInput,
|
|
|
412
412
|
export declare function defaultExtractRequiredFields(description: string): string[];
|
|
413
413
|
/** Default delegated-player adjudicator prompt. */
|
|
414
414
|
export declare function defaultBuildJudgePrompt(input: PlaybookPlayerInput, finalText: string): string;
|
|
415
|
+
/**
|
|
416
|
+
* Judge-facing rendering of one governed outcome (DR-040 §1). The artifact's
|
|
417
|
+
* description is not altered: its meaning is carried through verbatim, while
|
|
418
|
+
* its `Output shall include` clause — authored for the complete actor output
|
|
419
|
+
* — is replaced by the reply contract `outcomeAuthority` gives the judge:
|
|
420
|
+
* exactly `guard` plus the outcome's semantic-owned fields, each keeping the
|
|
421
|
+
* placeholder or guidance the clause authors for it, and every
|
|
422
|
+
* presentation-, effect-, or runtime-owned field named as runtime-supplied
|
|
423
|
+
* so the judge omits it. Rendering the clause verbatim asked the judge for
|
|
424
|
+
* `question`, `planningResult`, or `evaluatedRevision`, which the reconciler
|
|
425
|
+
* rejects as a structural error, spending the single correction on a
|
|
426
|
+
* self-inflicted defect. Exported so a bespoke linked runtime (DECIDE's
|
|
427
|
+
* parallel machinery) renders the identical contract instead of restating it.
|
|
428
|
+
*/
|
|
429
|
+
export declare function renderGovernedOutcomeContract(guard: string, description: string, outcome: XStateGovernedOutcomeSpec | undefined): string[];
|
|
415
430
|
export interface PlayerAdjudicationSpec {
|
|
416
431
|
buildJudgePrompt?: (input: PlaybookPlayerInput, finalText: string) => string;
|
|
417
432
|
extractRequiredFields?: (description: string) => string[];
|
|
@@ -568,6 +568,30 @@ export function defaultComposeCaptainPrompt(input, placeholderFields = {}) {
|
|
|
568
568
|
blocks.push(body);
|
|
569
569
|
return blocks.join('\n\n');
|
|
570
570
|
}
|
|
571
|
+
// A result description's output clause (slc/link.md §Captain adjudication):
|
|
572
|
+
// everything after `Output shall include` / `输出应包含` names the outcome's
|
|
573
|
+
// payload fields, each as a bare backticked name or the annotated
|
|
574
|
+
// `name: <placeholder>` form. The text before the clause is the outcome's
|
|
575
|
+
// meaning.
|
|
576
|
+
const OUTPUT_CLAUSE_MARKERS = [
|
|
577
|
+
'Output shall include',
|
|
578
|
+
'输出应包含',
|
|
579
|
+
];
|
|
580
|
+
const PAYLOAD_FIELD_PATTERN = /`([A-Za-z_$][A-Za-z0-9_$]*)(?::\s*([^`]*))?`/g;
|
|
581
|
+
// Separators between one field's authored segment and the next field token.
|
|
582
|
+
const SEGMENT_SEPARATOR_SUFFIX = /(?:\s|[,;.,、;。]|\band\b)+$/;
|
|
583
|
+
function splitOutputClause(description) {
|
|
584
|
+
for (const marker of OUTPUT_CLAUSE_MARKERS) {
|
|
585
|
+
const idx = description.indexOf(marker);
|
|
586
|
+
if (idx !== -1) {
|
|
587
|
+
return {
|
|
588
|
+
meaning: description.slice(0, idx).trim(),
|
|
589
|
+
clause: description.slice(idx + marker.length),
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return { meaning: description.trim() };
|
|
594
|
+
}
|
|
571
595
|
/**
|
|
572
596
|
* Default required-field extraction (slc/link.md §Captain adjudication).
|
|
573
597
|
* Limited to the description's `Output shall include` / `输出应包含` clause;
|
|
@@ -575,21 +599,11 @@ export function defaultComposeCaptainPrompt(input, placeholderFields = {}) {
|
|
|
575
599
|
* form.
|
|
576
600
|
*/
|
|
577
601
|
export function defaultExtractRequiredFields(description) {
|
|
578
|
-
const
|
|
579
|
-
|
|
580
|
-
for (const marker of markers) {
|
|
581
|
-
const idx = description.indexOf(marker);
|
|
582
|
-
if (idx !== -1) {
|
|
583
|
-
clauseStart = idx + marker.length;
|
|
584
|
-
break;
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
if (clauseStart === -1)
|
|
602
|
+
const { clause } = splitOutputClause(description);
|
|
603
|
+
if (clause === undefined)
|
|
588
604
|
return [];
|
|
589
|
-
const clause = description.slice(clauseStart);
|
|
590
605
|
const fields = [];
|
|
591
|
-
const
|
|
592
|
-
for (const m of clause.matchAll(re))
|
|
606
|
+
for (const m of clause.matchAll(PAYLOAD_FIELD_PATTERN))
|
|
593
607
|
fields.push(m[1]);
|
|
594
608
|
return fields;
|
|
595
609
|
}
|
|
@@ -615,6 +629,71 @@ export function defaultBuildJudgePrompt(input, finalText) {
|
|
|
615
629
|
}
|
|
616
630
|
return lines.join('\n');
|
|
617
631
|
}
|
|
632
|
+
/**
|
|
633
|
+
* Judge-facing rendering of one governed outcome (DR-040 §1). The artifact's
|
|
634
|
+
* description is not altered: its meaning is carried through verbatim, while
|
|
635
|
+
* its `Output shall include` clause — authored for the complete actor output
|
|
636
|
+
* — is replaced by the reply contract `outcomeAuthority` gives the judge:
|
|
637
|
+
* exactly `guard` plus the outcome's semantic-owned fields, each keeping the
|
|
638
|
+
* placeholder or guidance the clause authors for it, and every
|
|
639
|
+
* presentation-, effect-, or runtime-owned field named as runtime-supplied
|
|
640
|
+
* so the judge omits it. Rendering the clause verbatim asked the judge for
|
|
641
|
+
* `question`, `planningResult`, or `evaluatedRevision`, which the reconciler
|
|
642
|
+
* rejects as a structural error, spending the single correction on a
|
|
643
|
+
* self-inflicted defect. Exported so a bespoke linked runtime (DECIDE's
|
|
644
|
+
* parallel machinery) renders the identical contract instead of restating it.
|
|
645
|
+
*/
|
|
646
|
+
export function renderGovernedOutcomeContract(guard, description, outcome) {
|
|
647
|
+
const { meaning, clause } = splitOutputClause(description);
|
|
648
|
+
// Each field's authored segment runs from its token to the next token:
|
|
649
|
+
// the annotated `name: <placeholder>` form, or the bare name followed by
|
|
650
|
+
// its guidance (`` `irNumber` identifying the continued IR ``).
|
|
651
|
+
const authored = new Map();
|
|
652
|
+
if (clause !== undefined) {
|
|
653
|
+
const matches = [...clause.matchAll(PAYLOAD_FIELD_PATTERN)];
|
|
654
|
+
matches.forEach((m, i) => {
|
|
655
|
+
const field = m[1];
|
|
656
|
+
if (authored.has(field))
|
|
657
|
+
return;
|
|
658
|
+
const start = m.index ?? 0;
|
|
659
|
+
const end = matches[i + 1]?.index ?? clause.length;
|
|
660
|
+
const segment = clause
|
|
661
|
+
.slice(start, end)
|
|
662
|
+
.replace(SEGMENT_SEPARATOR_SUFFIX, '');
|
|
663
|
+
const placeholder = m[2]?.trim();
|
|
664
|
+
authored.set(field, {
|
|
665
|
+
segment,
|
|
666
|
+
...(placeholder !== undefined && placeholder.length > 0
|
|
667
|
+
? { placeholder }
|
|
668
|
+
: {}),
|
|
669
|
+
});
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
const replyMembers = [`"guard": ${JSON.stringify(guard)}`];
|
|
673
|
+
const semanticAsAuthored = [];
|
|
674
|
+
const runtimeSupplied = [];
|
|
675
|
+
for (const [field, authority] of Object.entries(outcome?.fields ?? {})) {
|
|
676
|
+
if (authority === 'semantic') {
|
|
677
|
+
const entry = authored.get(field);
|
|
678
|
+
replyMembers.push(`${JSON.stringify(field)}: ${entry?.placeholder ?? '<string>'}`);
|
|
679
|
+
semanticAsAuthored.push(entry?.segment ?? `\`${field}\``);
|
|
680
|
+
}
|
|
681
|
+
else {
|
|
682
|
+
runtimeSupplied.push(`\`${field}\` (${authority}-owned)`);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
const lines = [
|
|
686
|
+
meaning.length === 0 ? `- \`${guard}\`` : `- \`${guard}\` — ${meaning}`,
|
|
687
|
+
` Reply exactly: { ${replyMembers.join(', ')} }`,
|
|
688
|
+
];
|
|
689
|
+
if (semanticAsAuthored.length > 0) {
|
|
690
|
+
lines.push(` Semantic fields as authored: ${semanticAsAuthored.join('; ')}`);
|
|
691
|
+
}
|
|
692
|
+
if (runtimeSupplied.length > 0) {
|
|
693
|
+
lines.push(` Runtime-supplied, do not include: ${runtimeSupplied.join(', ')}`);
|
|
694
|
+
}
|
|
695
|
+
return lines;
|
|
696
|
+
}
|
|
618
697
|
function buildGovernedJudgePrompt(input, finalText, outcomes, correction) {
|
|
619
698
|
const lines = [
|
|
620
699
|
'This is hidden control work. Do not call tools, inspect files, or seek external evidence.',
|
|
@@ -627,17 +706,12 @@ function buildGovernedJudgePrompt(input, finalText, outcomes, correction) {
|
|
|
627
706
|
finalText,
|
|
628
707
|
'```',
|
|
629
708
|
'',
|
|
630
|
-
'Pick exactly one declared `guard
|
|
631
|
-
'
|
|
709
|
+
'Pick exactly one declared `guard` and reply with exactly that outcome\'s reply shape below: `guard` plus its semantic-owned fields and nothing else.',
|
|
710
|
+
'A field listed as runtime-supplied is owned by presentation, effect, or runtime evidence; the runtime fills it itself, and a reply that includes one is structurally invalid.',
|
|
632
711
|
'',
|
|
633
712
|
];
|
|
634
713
|
for (const [guard, description] of Object.entries(input.result)) {
|
|
635
|
-
|
|
636
|
-
.filter(([, authority]) => authority === 'semantic')
|
|
637
|
-
.map(([field]) => field);
|
|
638
|
-
lines.push(`- \`${guard}\` — semantic fields: ${semanticFields.length === 0
|
|
639
|
-
? '(none)'
|
|
640
|
-
: semanticFields.map((field) => `\`${field}\``).join(', ')}; ${description}`);
|
|
714
|
+
lines.push(...renderGovernedOutcomeContract(guard, description, outcomes[guard]));
|
|
641
715
|
}
|
|
642
716
|
if (correction !== undefined) {
|
|
643
717
|
lines.push('', 'Your first reply was structurally invalid:', '', '```', correction.reply, '```', '', `Validation error: ${correction.error}`, 'Correct only that structure using the same player output and outcome schema.');
|
|
@@ -2080,9 +2154,24 @@ export function createXStatePlaybookRuntime(machine, spec) {
|
|
|
2080
2154
|
return undefined;
|
|
2081
2155
|
}
|
|
2082
2156
|
}
|
|
2157
|
+
// DR-040 §4 parks only an envelope whose evidence proves a repository
|
|
2158
|
+
// delta or cannot exclude one. A standalone boundary whose complete
|
|
2159
|
+
// physical receipt is exactly `unchanged` excludes any effect, so missing
|
|
2160
|
+
// or unresolved semantics over it are an ordinary failure — the FSM's
|
|
2161
|
+
// failure state with its fenced retry — never a parked reconciliation:
|
|
2162
|
+
// that state's only exits, reconcile and abandon, project no effect
|
|
2163
|
+
// evidence from an `unchanged` receipt, so parking it deadlocks the
|
|
2164
|
+
// engagement. A boundary inside a deferred chain is judged by the chain's
|
|
2165
|
+
// cumulative receipt from its original baseline, not by its own step.
|
|
2166
|
+
function boundaryExcludesEffect(boundary) {
|
|
2167
|
+
return (boundary.logicalOperationId === undefined &&
|
|
2168
|
+
boundary.physicalReceipt?.classification === 'unchanged');
|
|
2169
|
+
}
|
|
2083
2170
|
function boundaryNeedsSemanticReconciliation(candidate, ledger) {
|
|
2084
2171
|
if (!runtimeBoundaryIsOwned(candidate))
|
|
2085
2172
|
return false;
|
|
2173
|
+
if (boundaryExcludesEffect(candidate))
|
|
2174
|
+
return false;
|
|
2086
2175
|
if (governedOutcomesForBoundary(candidate) === undefined)
|
|
2087
2176
|
return true;
|
|
2088
2177
|
const persisted = persistedBoundaryReconciliation(candidate, ledger);
|
|
@@ -3110,7 +3199,11 @@ export function createXStatePlaybookRuntime(machine, spec) {
|
|
|
3110
3199
|
if (governedSettlement !== undefined) {
|
|
3111
3200
|
governedSettlementsByBoundaryId.delete(boundaryId);
|
|
3112
3201
|
governedPlayerSettlements.set(result, governedSettlement);
|
|
3113
|
-
|
|
3202
|
+
// An unresolved settlement over an `unchanged` standalone receipt
|
|
3203
|
+
// still throws into the failure state through the player bridge; it
|
|
3204
|
+
// just never becomes an effect-possible envelope.
|
|
3205
|
+
if (governedSettlement.status === 'unresolved' &&
|
|
3206
|
+
!boundaryExcludesEffect(completed)) {
|
|
3114
3207
|
unresolvedSemanticBoundaryIds.add(boundaryId);
|
|
3115
3208
|
}
|
|
3116
3209
|
else {
|
|
@@ -4830,6 +4923,10 @@ export function createXStatePlaybookRuntime(machine, spec) {
|
|
|
4830
4923
|
delivery: deferredValue(),
|
|
4831
4924
|
};
|
|
4832
4925
|
activeDeferredContinuation = continuation;
|
|
4926
|
+
// Emissions are buffered only until the host durably starts the
|
|
4927
|
+
// continuation: an exit that starts no player (checkpoint mismatch,
|
|
4928
|
+
// ineligible operation) publishes nothing, so the bound wait it
|
|
4929
|
+
// preserves is never contradicted by a classification line.
|
|
4833
4930
|
deferInspectionEmissions = true;
|
|
4834
4931
|
let continuationStarted = false;
|
|
4835
4932
|
let deliverySettled = false;
|
|
@@ -4858,6 +4955,14 @@ export function createXStatePlaybookRuntime(machine, spec) {
|
|
|
4858
4955
|
continuation.playerContinuation = selectedContinuation;
|
|
4859
4956
|
continuationStarted = true;
|
|
4860
4957
|
actor.send(event);
|
|
4958
|
+
// The host has durably started this boundary and the FSM has
|
|
4959
|
+
// moved: publish the buffered classification line and the
|
|
4960
|
+
// authored transition now, and let every later transition,
|
|
4961
|
+
// accepted outcome, and status emit inline. Holding them until
|
|
4962
|
+
// the operation settled put the cause after its effects — the
|
|
4963
|
+
// player call, the nested call a target state starts, and their
|
|
4964
|
+
// finishes were traced and sequenced first (PBRT-37).
|
|
4965
|
+
settleDeferredInspectionBuffer(true);
|
|
4861
4966
|
// The invoked player remains gated inside boundary.callPlayer.
|
|
4862
4967
|
// Return to the host only after the raw player call settles so it
|
|
4863
4968
|
// can capture and persist the receipt before any actor output or
|
|
@@ -1304,6 +1304,36 @@ export function defaultComposeCaptainPrompt(
|
|
|
1304
1304
|
return blocks.join('\n\n');
|
|
1305
1305
|
}
|
|
1306
1306
|
|
|
1307
|
+
// A result description's output clause (slc/link.md §Captain adjudication):
|
|
1308
|
+
// everything after `Output shall include` / `输出应包含` names the outcome's
|
|
1309
|
+
// payload fields, each as a bare backticked name or the annotated
|
|
1310
|
+
// `name: <placeholder>` form. The text before the clause is the outcome's
|
|
1311
|
+
// meaning.
|
|
1312
|
+
const OUTPUT_CLAUSE_MARKERS: readonly string[] = [
|
|
1313
|
+
'Output shall include',
|
|
1314
|
+
'输出应包含',
|
|
1315
|
+
];
|
|
1316
|
+
const PAYLOAD_FIELD_PATTERN =
|
|
1317
|
+
/`([A-Za-z_$][A-Za-z0-9_$]*)(?::\s*([^`]*))?`/g;
|
|
1318
|
+
// Separators between one field's authored segment and the next field token.
|
|
1319
|
+
const SEGMENT_SEPARATOR_SUFFIX = /(?:\s|[,;.,、;。]|\band\b)+$/;
|
|
1320
|
+
|
|
1321
|
+
function splitOutputClause(description: string): {
|
|
1322
|
+
readonly meaning: string;
|
|
1323
|
+
readonly clause?: string;
|
|
1324
|
+
} {
|
|
1325
|
+
for (const marker of OUTPUT_CLAUSE_MARKERS) {
|
|
1326
|
+
const idx = description.indexOf(marker);
|
|
1327
|
+
if (idx !== -1) {
|
|
1328
|
+
return {
|
|
1329
|
+
meaning: description.slice(0, idx).trim(),
|
|
1330
|
+
clause: description.slice(idx + marker.length),
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
return { meaning: description.trim() };
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1307
1337
|
/**
|
|
1308
1338
|
* Default required-field extraction (slc/link.md §Captain adjudication).
|
|
1309
1339
|
* Limited to the description's `Output shall include` / `输出应包含` clause;
|
|
@@ -1311,20 +1341,10 @@ export function defaultComposeCaptainPrompt(
|
|
|
1311
1341
|
* form.
|
|
1312
1342
|
*/
|
|
1313
1343
|
export function defaultExtractRequiredFields(description: string): string[] {
|
|
1314
|
-
const
|
|
1315
|
-
|
|
1316
|
-
for (const marker of markers) {
|
|
1317
|
-
const idx = description.indexOf(marker);
|
|
1318
|
-
if (idx !== -1) {
|
|
1319
|
-
clauseStart = idx + marker.length;
|
|
1320
|
-
break;
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
if (clauseStart === -1) return [];
|
|
1324
|
-
const clause = description.slice(clauseStart);
|
|
1344
|
+
const { clause } = splitOutputClause(description);
|
|
1345
|
+
if (clause === undefined) return [];
|
|
1325
1346
|
const fields: string[] = [];
|
|
1326
|
-
const
|
|
1327
|
-
for (const m of clause.matchAll(re)) fields.push(m[1]);
|
|
1347
|
+
for (const m of clause.matchAll(PAYLOAD_FIELD_PATTERN)) fields.push(m[1]);
|
|
1328
1348
|
return fields;
|
|
1329
1349
|
}
|
|
1330
1350
|
|
|
@@ -1358,6 +1378,83 @@ export function defaultBuildJudgePrompt(
|
|
|
1358
1378
|
return lines.join('\n');
|
|
1359
1379
|
}
|
|
1360
1380
|
|
|
1381
|
+
/**
|
|
1382
|
+
* Judge-facing rendering of one governed outcome (DR-040 §1). The artifact's
|
|
1383
|
+
* description is not altered: its meaning is carried through verbatim, while
|
|
1384
|
+
* its `Output shall include` clause — authored for the complete actor output
|
|
1385
|
+
* — is replaced by the reply contract `outcomeAuthority` gives the judge:
|
|
1386
|
+
* exactly `guard` plus the outcome's semantic-owned fields, each keeping the
|
|
1387
|
+
* placeholder or guidance the clause authors for it, and every
|
|
1388
|
+
* presentation-, effect-, or runtime-owned field named as runtime-supplied
|
|
1389
|
+
* so the judge omits it. Rendering the clause verbatim asked the judge for
|
|
1390
|
+
* `question`, `planningResult`, or `evaluatedRevision`, which the reconciler
|
|
1391
|
+
* rejects as a structural error, spending the single correction on a
|
|
1392
|
+
* self-inflicted defect. Exported so a bespoke linked runtime (DECIDE's
|
|
1393
|
+
* parallel machinery) renders the identical contract instead of restating it.
|
|
1394
|
+
*/
|
|
1395
|
+
export function renderGovernedOutcomeContract(
|
|
1396
|
+
guard: string,
|
|
1397
|
+
description: string,
|
|
1398
|
+
outcome: XStateGovernedOutcomeSpec | undefined,
|
|
1399
|
+
): string[] {
|
|
1400
|
+
const { meaning, clause } = splitOutputClause(description);
|
|
1401
|
+
// Each field's authored segment runs from its token to the next token:
|
|
1402
|
+
// the annotated `name: <placeholder>` form, or the bare name followed by
|
|
1403
|
+
// its guidance (`` `irNumber` identifying the continued IR ``).
|
|
1404
|
+
const authored = new Map<
|
|
1405
|
+
string,
|
|
1406
|
+
{ readonly segment: string; readonly placeholder?: string }
|
|
1407
|
+
>();
|
|
1408
|
+
if (clause !== undefined) {
|
|
1409
|
+
const matches = [...clause.matchAll(PAYLOAD_FIELD_PATTERN)];
|
|
1410
|
+
matches.forEach((m, i) => {
|
|
1411
|
+
const field = m[1];
|
|
1412
|
+
if (authored.has(field)) return;
|
|
1413
|
+
const start = m.index ?? 0;
|
|
1414
|
+
const end = matches[i + 1]?.index ?? clause.length;
|
|
1415
|
+
const segment = clause
|
|
1416
|
+
.slice(start, end)
|
|
1417
|
+
.replace(SEGMENT_SEPARATOR_SUFFIX, '');
|
|
1418
|
+
const placeholder = m[2]?.trim();
|
|
1419
|
+
authored.set(field, {
|
|
1420
|
+
segment,
|
|
1421
|
+
...(placeholder !== undefined && placeholder.length > 0
|
|
1422
|
+
? { placeholder }
|
|
1423
|
+
: {}),
|
|
1424
|
+
});
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
const replyMembers = [`"guard": ${JSON.stringify(guard)}`];
|
|
1428
|
+
const semanticAsAuthored: string[] = [];
|
|
1429
|
+
const runtimeSupplied: string[] = [];
|
|
1430
|
+
for (const [field, authority] of Object.entries(outcome?.fields ?? {})) {
|
|
1431
|
+
if (authority === 'semantic') {
|
|
1432
|
+
const entry = authored.get(field);
|
|
1433
|
+
replyMembers.push(
|
|
1434
|
+
`${JSON.stringify(field)}: ${entry?.placeholder ?? '<string>'}`,
|
|
1435
|
+
);
|
|
1436
|
+
semanticAsAuthored.push(entry?.segment ?? `\`${field}\``);
|
|
1437
|
+
} else {
|
|
1438
|
+
runtimeSupplied.push(`\`${field}\` (${authority}-owned)`);
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
const lines = [
|
|
1442
|
+
meaning.length === 0 ? `- \`${guard}\`` : `- \`${guard}\` — ${meaning}`,
|
|
1443
|
+
` Reply exactly: { ${replyMembers.join(', ')} }`,
|
|
1444
|
+
];
|
|
1445
|
+
if (semanticAsAuthored.length > 0) {
|
|
1446
|
+
lines.push(
|
|
1447
|
+
` Semantic fields as authored: ${semanticAsAuthored.join('; ')}`,
|
|
1448
|
+
);
|
|
1449
|
+
}
|
|
1450
|
+
if (runtimeSupplied.length > 0) {
|
|
1451
|
+
lines.push(
|
|
1452
|
+
` Runtime-supplied, do not include: ${runtimeSupplied.join(', ')}`,
|
|
1453
|
+
);
|
|
1454
|
+
}
|
|
1455
|
+
return lines;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1361
1458
|
function buildGovernedJudgePrompt(
|
|
1362
1459
|
input: PlaybookPlayerInput,
|
|
1363
1460
|
finalText: string,
|
|
@@ -1375,20 +1472,13 @@ function buildGovernedJudgePrompt(
|
|
|
1375
1472
|
finalText,
|
|
1376
1473
|
'```',
|
|
1377
1474
|
'',
|
|
1378
|
-
'Pick exactly one declared `guard
|
|
1379
|
-
'
|
|
1475
|
+
'Pick exactly one declared `guard` and reply with exactly that outcome\'s reply shape below: `guard` plus its semantic-owned fields and nothing else.',
|
|
1476
|
+
'A field listed as runtime-supplied is owned by presentation, effect, or runtime evidence; the runtime fills it itself, and a reply that includes one is structurally invalid.',
|
|
1380
1477
|
'',
|
|
1381
1478
|
];
|
|
1382
1479
|
for (const [guard, description] of Object.entries(input.result)) {
|
|
1383
|
-
const semanticFields = Object.entries(outcomes[guard]?.fields ?? {})
|
|
1384
|
-
.filter(([, authority]) => authority === 'semantic')
|
|
1385
|
-
.map(([field]) => field);
|
|
1386
1480
|
lines.push(
|
|
1387
|
-
|
|
1388
|
-
semanticFields.length === 0
|
|
1389
|
-
? '(none)'
|
|
1390
|
-
: semanticFields.map((field) => `\`${field}\``).join(', ')
|
|
1391
|
-
}; ${description}`,
|
|
1481
|
+
...renderGovernedOutcomeContract(guard, description, outcomes[guard]),
|
|
1392
1482
|
);
|
|
1393
1483
|
}
|
|
1394
1484
|
if (correction !== undefined) {
|
|
@@ -3480,11 +3570,28 @@ export function createXStatePlaybookRuntime<
|
|
|
3480
3570
|
}
|
|
3481
3571
|
}
|
|
3482
3572
|
|
|
3573
|
+
// DR-040 §4 parks only an envelope whose evidence proves a repository
|
|
3574
|
+
// delta or cannot exclude one. A standalone boundary whose complete
|
|
3575
|
+
// physical receipt is exactly `unchanged` excludes any effect, so missing
|
|
3576
|
+
// or unresolved semantics over it are an ordinary failure — the FSM's
|
|
3577
|
+
// failure state with its fenced retry — never a parked reconciliation:
|
|
3578
|
+
// that state's only exits, reconcile and abandon, project no effect
|
|
3579
|
+
// evidence from an `unchanged` receipt, so parking it deadlocks the
|
|
3580
|
+
// engagement. A boundary inside a deferred chain is judged by the chain's
|
|
3581
|
+
// cumulative receipt from its original baseline, not by its own step.
|
|
3582
|
+
function boundaryExcludesEffect(boundary: PlaybookEffectBoundary): boolean {
|
|
3583
|
+
return (
|
|
3584
|
+
boundary.logicalOperationId === undefined &&
|
|
3585
|
+
boundary.physicalReceipt?.classification === 'unchanged'
|
|
3586
|
+
);
|
|
3587
|
+
}
|
|
3588
|
+
|
|
3483
3589
|
function boundaryNeedsSemanticReconciliation(
|
|
3484
3590
|
candidate: PlaybookEffectBoundary,
|
|
3485
3591
|
ledger: PlaybookEffectLedger,
|
|
3486
3592
|
): boolean {
|
|
3487
3593
|
if (!runtimeBoundaryIsOwned(candidate)) return false;
|
|
3594
|
+
if (boundaryExcludesEffect(candidate)) return false;
|
|
3488
3595
|
if (governedOutcomesForBoundary(candidate) === undefined) return true;
|
|
3489
3596
|
const persisted = persistedBoundaryReconciliation(candidate, ledger);
|
|
3490
3597
|
if (persisted !== undefined) {
|
|
@@ -5009,7 +5116,13 @@ export function createXStatePlaybookRuntime<
|
|
|
5009
5116
|
if (governedSettlement !== undefined) {
|
|
5010
5117
|
governedSettlementsByBoundaryId.delete(boundaryId);
|
|
5011
5118
|
governedPlayerSettlements.set(result, governedSettlement);
|
|
5012
|
-
|
|
5119
|
+
// An unresolved settlement over an `unchanged` standalone receipt
|
|
5120
|
+
// still throws into the failure state through the player bridge; it
|
|
5121
|
+
// just never becomes an effect-possible envelope.
|
|
5122
|
+
if (
|
|
5123
|
+
governedSettlement.status === 'unresolved' &&
|
|
5124
|
+
!boundaryExcludesEffect(completed)
|
|
5125
|
+
) {
|
|
5013
5126
|
unresolvedSemanticBoundaryIds.add(boundaryId);
|
|
5014
5127
|
} else {
|
|
5015
5128
|
unresolvedSemanticBoundaryIds.delete(boundaryId);
|
|
@@ -7145,6 +7258,10 @@ export function createXStatePlaybookRuntime<
|
|
|
7145
7258
|
delivery: deferredValue<PlayerResult>(),
|
|
7146
7259
|
};
|
|
7147
7260
|
activeDeferredContinuation = continuation;
|
|
7261
|
+
// Emissions are buffered only until the host durably starts the
|
|
7262
|
+
// continuation: an exit that starts no player (checkpoint mismatch,
|
|
7263
|
+
// ineligible operation) publishes nothing, so the bound wait it
|
|
7264
|
+
// preserves is never contradicted by a classification line.
|
|
7148
7265
|
deferInspectionEmissions = true;
|
|
7149
7266
|
let continuationStarted = false;
|
|
7150
7267
|
let deliverySettled = false;
|
|
@@ -7181,6 +7298,14 @@ export function createXStatePlaybookRuntime<
|
|
|
7181
7298
|
continuation.playerContinuation = selectedContinuation;
|
|
7182
7299
|
continuationStarted = true;
|
|
7183
7300
|
actor!.send(event);
|
|
7301
|
+
// The host has durably started this boundary and the FSM has
|
|
7302
|
+
// moved: publish the buffered classification line and the
|
|
7303
|
+
// authored transition now, and let every later transition,
|
|
7304
|
+
// accepted outcome, and status emit inline. Holding them until
|
|
7305
|
+
// the operation settled put the cause after its effects — the
|
|
7306
|
+
// player call, the nested call a target state starts, and their
|
|
7307
|
+
// finishes were traced and sequenced first (PBRT-37).
|
|
7308
|
+
settleDeferredInspectionBuffer(true);
|
|
7184
7309
|
// The invoked player remains gated inside boundary.callPlayer.
|
|
7185
7310
|
// Return to the host only after the raw player call settles so it
|
|
7186
7311
|
// can capture and persist the receipt before any actor output or
|