@sublang/playbook 14.1.0 → 15.1.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/README.md +2 -2
- package/docs/configuration.md +15 -9
- package/docs/embedding.md +7 -5
- package/package.json +3 -1
- package/reference/sdlc/code.md +1 -1
- package/reference/sdlc/code.playbook/bin/launch-config.js +55 -16
- package/reference/sdlc/code.playbook/bin/playbook.js +14 -9
- package/reference/sdlc/code.playbook/bin/repository-effects.js +54 -13
- package/reference/sdlc/code.playbook/bin/run.js +12 -8
- package/reference/sdlc/code.playbook/code.fsm.js +1 -1
- package/reference/sdlc/code.playbook/code.fsm.ts +1 -1
- package/reference/sdlc/code.playbook/code.gears.md +2 -2
- package/reference/sdlc/decide.md +1 -1
- package/reference/sdlc/decide.playbook/decide.fsm.js +1 -1
- package/reference/sdlc/decide.playbook/decide.fsm.ts +1 -1
- package/reference/sdlc/decide.playbook/decide.gears.md +1 -1
- package/reference/sdlc/decide.playbook/decide.playbook.d.ts +1 -9
- package/reference/sdlc/decide.playbook/decide.playbook.js +158 -32
- package/reference/sdlc/decide.playbook/decide.playbook.ts +229 -46
- package/reference/sdlc/review.md +1 -1
- package/reference/sdlc/review.playbook/review.fsm.js +1 -1
- package/reference/sdlc/review.playbook/review.fsm.ts +1 -1
- package/reference/sdlc/review.playbook/review.gears.md +1 -1
- package/slc/prefix-prompts.mjs +190 -0
- package/slc/prefix.md +200 -0
- package/slc/slc.pin-inputs.json +1 -0
- package/src/xstate-playbook-runtime.d.ts +35 -1
- package/src/xstate-playbook-runtime.js +82 -47
- package/src/xstate-playbook-runtime.ts +107 -60
- package/src/xstate-runtime.d.ts +3 -1
- package/src/xstate-runtime.js +9 -2
- package/src/xstate-runtime.ts +12 -2
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
// SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
|
|
4
|
+
|
|
5
|
+
// Deterministic realization of the prompt-prefix pass (slc/prefix.md): every
|
|
6
|
+
// standalone relay block of an eligible item moves after its last instruction
|
|
7
|
+
// line, byte-for-byte, and one `## Prefixed prompts` section at the end lists
|
|
8
|
+
// the items this and every earlier application rewrote. `--keep <ITEM-ID>`
|
|
9
|
+
// excludes an item the pass judged ineligible; the tool never modifies the
|
|
10
|
+
// source.
|
|
11
|
+
|
|
12
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
13
|
+
import { resolve } from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
|
|
16
|
+
const HEADING = /^#{1,3}\s/;
|
|
17
|
+
const ITEM_HEADING = /^###\s+([^\s]+)\s*$/;
|
|
18
|
+
const PROMPT_LINE = /^>\s?(.*)$/;
|
|
19
|
+
const SECTION = '## Prefixed prompts';
|
|
20
|
+
const SECTION_HEADING = /^##\s+Prefixed prompts\s*$/;
|
|
21
|
+
const LISTED = /^-\s+([^\s:]+):\s+relays → tail\s*$/;
|
|
22
|
+
const bullet = (id) => `- ${id}: relays → tail`;
|
|
23
|
+
|
|
24
|
+
/** Classify one prompt line by its content inside the outer blockquote marker. */
|
|
25
|
+
function classify(line) {
|
|
26
|
+
const inner = PROMPT_LINE.exec(line)[1];
|
|
27
|
+
if (inner === '') return 'blank';
|
|
28
|
+
return inner.startsWith('>') ? 'relay' : 'instruction';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Rewrite one blockquote (its raw `> …` file lines). Returns the new lines, or
|
|
33
|
+
* null when no relay block precedes an instruction line.
|
|
34
|
+
*/
|
|
35
|
+
function prefixBlockquote(quote) {
|
|
36
|
+
const kinds = quote.map(classify);
|
|
37
|
+
// A relay block is a maximal run of relay lines bounded by blank lines or
|
|
38
|
+
// the blockquote edges; a relay line adjacent to an instruction line is
|
|
39
|
+
// authored content of that instruction and stays with it.
|
|
40
|
+
const inBlock = new Array(quote.length).fill(false);
|
|
41
|
+
for (let start = 0; start < quote.length; ) {
|
|
42
|
+
if (kinds[start] !== 'relay') {
|
|
43
|
+
start++;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
let end = start;
|
|
47
|
+
while (end < quote.length && kinds[end] === 'relay') end++;
|
|
48
|
+
const boundedBefore = start === 0 || kinds[start - 1] === 'blank';
|
|
49
|
+
const boundedAfter = end === quote.length || kinds[end] === 'blank';
|
|
50
|
+
if (boundedBefore && boundedAfter) inBlock.fill(true, start, end);
|
|
51
|
+
start = end;
|
|
52
|
+
}
|
|
53
|
+
const firstBlock = inBlock.indexOf(true);
|
|
54
|
+
if (firstBlock === -1) return null;
|
|
55
|
+
const lastInstruction = Math.max(
|
|
56
|
+
...kinds.map((kind, index) =>
|
|
57
|
+
kind === 'blank' || inBlock[index] ? -1 : index,
|
|
58
|
+
),
|
|
59
|
+
);
|
|
60
|
+
if (lastInstruction < firstBlock) return null; // relays already trail
|
|
61
|
+
|
|
62
|
+
// Instruction lines keep their order and their authored blank lines; a
|
|
63
|
+
// removed block takes the blank lines bounding it and leaves the first of
|
|
64
|
+
// them where it separated two remaining lines, and none leads or trails the
|
|
65
|
+
// region.
|
|
66
|
+
const removed = [...inBlock];
|
|
67
|
+
inBlock.forEach((inside, index) => {
|
|
68
|
+
if (!inside) return;
|
|
69
|
+
for (let at = index - 1; at >= 0 && kinds[at] === 'blank'; at--) removed[at] = true;
|
|
70
|
+
for (let at = index + 1; at < quote.length && kinds[at] === 'blank'; at++) removed[at] = true;
|
|
71
|
+
});
|
|
72
|
+
const statics = [];
|
|
73
|
+
let separator;
|
|
74
|
+
for (let index = 0; index < quote.length; index++) {
|
|
75
|
+
if (removed[index]) {
|
|
76
|
+
separator ??= quote[index];
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (statics.length === 0 && kinds[index] === 'blank') continue;
|
|
80
|
+
if (separator !== undefined && statics.length > 0) statics.push(separator);
|
|
81
|
+
separator = undefined;
|
|
82
|
+
statics.push(quote[index]);
|
|
83
|
+
}
|
|
84
|
+
while (statics.length > 0 && classify(statics[statics.length - 1]) === 'blank') {
|
|
85
|
+
statics.pop();
|
|
86
|
+
}
|
|
87
|
+
const blocks = [];
|
|
88
|
+
for (let index = 0; index < quote.length; index++) {
|
|
89
|
+
if (!inBlock[index]) continue;
|
|
90
|
+
if (index === 0 || !inBlock[index - 1]) blocks.push([]);
|
|
91
|
+
blocks[blocks.length - 1].push(quote[index]);
|
|
92
|
+
}
|
|
93
|
+
const relays = blocks.flatMap((block, index) =>
|
|
94
|
+
index === 0 ? block : ['>', ...block],
|
|
95
|
+
);
|
|
96
|
+
return [...statics, '>', ...relays];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Rewrite every eligible item of a GEARS package not named by `keep`.
|
|
101
|
+
* Returns the rewritten text with its provenance section, or the source text
|
|
102
|
+
* unchanged when no item was eligible, plus the rewritten item ids.
|
|
103
|
+
*/
|
|
104
|
+
export function prefixPrompts(text, { keep = [] } = {}) {
|
|
105
|
+
// Keep the source's own line-ending convention so untouched lines stay bytes.
|
|
106
|
+
const newline = text.includes('\r\n') ? '\r\n' : '\n';
|
|
107
|
+
const lines = text.split(/\r?\n/);
|
|
108
|
+
const starts = [];
|
|
109
|
+
lines.forEach((line, index) => {
|
|
110
|
+
const match = ITEM_HEADING.exec(line);
|
|
111
|
+
if (match !== null) starts.push({ index, id: match[1] });
|
|
112
|
+
});
|
|
113
|
+
const out = [];
|
|
114
|
+
let cursor = 0;
|
|
115
|
+
const rewritten = [];
|
|
116
|
+
for (const start of starts) {
|
|
117
|
+
let end = start.index + 1;
|
|
118
|
+
while (end < lines.length && !HEADING.test(lines[end])) end++;
|
|
119
|
+
let quoteStart = start.index + 1;
|
|
120
|
+
while (quoteStart < end && !PROMPT_LINE.test(lines[quoteStart])) quoteStart++;
|
|
121
|
+
if (quoteStart === end) continue;
|
|
122
|
+
let quoteEnd = quoteStart;
|
|
123
|
+
while (quoteEnd < end && PROMPT_LINE.test(lines[quoteEnd])) quoteEnd++;
|
|
124
|
+
// A script item's blockquote is shell text, not a prompt (optimize.md).
|
|
125
|
+
const acting = lines.slice(start.index + 1, quoteStart).join(' ');
|
|
126
|
+
if (/\bCaptain shall run:/.test(acting) || keep.includes(start.id)) continue;
|
|
127
|
+
const replaced = prefixBlockquote(lines.slice(quoteStart, quoteEnd));
|
|
128
|
+
if (replaced === null) continue;
|
|
129
|
+
out.push(...lines.slice(cursor, quoteStart), ...replaced);
|
|
130
|
+
cursor = quoteEnd;
|
|
131
|
+
rewritten.push(start.id);
|
|
132
|
+
}
|
|
133
|
+
if (rewritten.length === 0) return { text, rewritten };
|
|
134
|
+
out.push(...lines.slice(cursor));
|
|
135
|
+
// One provenance section follows every other section, ending in one newline;
|
|
136
|
+
// it replaces every section an earlier application appended and lists, in
|
|
137
|
+
// item order, each item this or that application rewrote.
|
|
138
|
+
const listed = new Set(rewritten);
|
|
139
|
+
for (let index = out.length - 1; index >= 0; index--) {
|
|
140
|
+
if (!SECTION_HEADING.test(out[index])) continue;
|
|
141
|
+
let end = index + 1;
|
|
142
|
+
for (; end < out.length && !HEADING.test(out[end]); end++) {
|
|
143
|
+
const entry = LISTED.exec(out[end]);
|
|
144
|
+
if (entry !== null) listed.add(entry[1]);
|
|
145
|
+
}
|
|
146
|
+
out.splice(index, end - index);
|
|
147
|
+
}
|
|
148
|
+
while (out.length > 0 && out[out.length - 1] === '') out.pop();
|
|
149
|
+
const ids = starts.map((start) => start.id).filter((id) => listed.has(id));
|
|
150
|
+
out.push('', SECTION, '', ...ids.map(bullet), '');
|
|
151
|
+
return { text: out.join(newline), rewritten };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function main(argv) {
|
|
155
|
+
let source;
|
|
156
|
+
let target;
|
|
157
|
+
const keep = [];
|
|
158
|
+
for (let index = 0; index < argv.length; index++) {
|
|
159
|
+
const flag = argv[index];
|
|
160
|
+
const value = argv[index + 1];
|
|
161
|
+
if (value === undefined || value.startsWith('--')) {
|
|
162
|
+
throw new Error(`${flag} needs a value`);
|
|
163
|
+
}
|
|
164
|
+
if (flag === '--source') source = value;
|
|
165
|
+
else if (flag === '--target') target = value;
|
|
166
|
+
else if (flag === '--keep') keep.push(value);
|
|
167
|
+
else throw new Error(`unknown argument ${flag}`);
|
|
168
|
+
index++;
|
|
169
|
+
}
|
|
170
|
+
if (source === undefined || target === undefined) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
'usage: prefix-prompts.mjs --source <gears.md> --target <gears.md> [--keep <ITEM-ID>]...',
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
const { text, rewritten } = prefixPrompts(await readFile(resolve(source), 'utf8'), { keep });
|
|
176
|
+
await writeFile(resolve(target), text);
|
|
177
|
+
process.stdout.write(
|
|
178
|
+
rewritten.length === 0
|
|
179
|
+
? 'no eligible item; target equals source\n'
|
|
180
|
+
: `${rewritten.join('\n')}\n`,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const invokedPath = process.argv[1] === undefined ? undefined : resolve(process.argv[1]);
|
|
185
|
+
if (invokedPath === fileURLToPath(import.meta.url)) {
|
|
186
|
+
main(process.argv.slice(2)).catch((error) => {
|
|
187
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
188
|
+
process.exitCode = 1;
|
|
189
|
+
});
|
|
190
|
+
}
|
package/slc/prefix.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
<!-- SPDX-License-Identifier: Apache-2.0 -->
|
|
2
|
+
<!-- SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai> -->
|
|
3
|
+
|
|
4
|
+
# GEARS Prompt-Prefix Pass
|
|
5
|
+
|
|
6
|
+
Optional optimization pass of a playbook (a state-machine agent orchestrating
|
|
7
|
+
other agents).
|
|
8
|
+
Reorders each acting prompt so that its static instructions come first and the
|
|
9
|
+
runtime values relayed into it come last, giving every run of the same item an
|
|
10
|
+
identical prompt prefix that an LLM provider's prompt cache can serve.
|
|
11
|
+
|
|
12
|
+
- Source: a package of GEARS spec items produced by [text2gears](text2gears.md)
|
|
13
|
+
and, when scheduled, already rewritten by [optimize](optimize.md).
|
|
14
|
+
- Target: the same package with eligible prompts laid out prefix-first.
|
|
15
|
+
|
|
16
|
+
The pass is format-preserving: source and target are both GEARS packages, and
|
|
17
|
+
the pass sits between [text2gears](text2gears.md) and
|
|
18
|
+
[gears2fsm](gears2fsm.md), after [optimize](optimize.md) by pass-name order.
|
|
19
|
+
A playbook compiled without this pass has identical observable behavior; the
|
|
20
|
+
pass trades a compile-time rewrite for cheaper prompt processing at run time.
|
|
21
|
+
|
|
22
|
+
Prompt caches are prefix caches: a provider serves the longest prefix it has
|
|
23
|
+
already seen and processes everything after the first differing token afresh
|
|
24
|
+
[[1]], [[2]], [[3]].
|
|
25
|
+
A prompt that opens with this run's relayed request therefore shares nothing
|
|
26
|
+
with the previous run's prompt for the same item, while one that opens with
|
|
27
|
+
the item's instructions shares them all.
|
|
28
|
+
|
|
29
|
+
## Formats
|
|
30
|
+
|
|
31
|
+
| Role | Format | Extension |
|
|
32
|
+
| ------ | ------ | --------- |
|
|
33
|
+
| source | gears | .md |
|
|
34
|
+
| target | gears | .md |
|
|
35
|
+
|
|
36
|
+
## Prompt regions
|
|
37
|
+
|
|
38
|
+
Within an item's blockquote, each prompt line is one of:
|
|
39
|
+
|
|
40
|
+
- a **relay line** — prompt content that begins with the literal quote marker
|
|
41
|
+
`>` (written `> > …` in the GEARS file), which is how
|
|
42
|
+
[text2gears](text2gears.md#authored-prompt-fragments) relays a runtime value
|
|
43
|
+
such as `> Original request: <caller-input>`;
|
|
44
|
+
- a **blank line** — an empty prompt line, written `>`;
|
|
45
|
+
- an **instruction line** — any other prompt line.
|
|
46
|
+
|
|
47
|
+
A **relay block** is a maximal run of consecutive relay lines bounded by blank
|
|
48
|
+
lines or the blockquote's edges, whether Source authored it as a separate
|
|
49
|
+
quoted relay or set it apart with blank lines inside a fenced instruction.
|
|
50
|
+
A relay line adjacent to an instruction line is authored content of that
|
|
51
|
+
instruction — a quoted example inside a fenced instruction, for instance — and
|
|
52
|
+
belongs to the instruction, not to a relay block.
|
|
53
|
+
|
|
54
|
+
An instruction line may carry an inline `<placeholder>`, such as a configured
|
|
55
|
+
model name; the pass never splits or rewrites an instruction line to isolate
|
|
56
|
+
it.
|
|
57
|
+
|
|
58
|
+
## Eligibility
|
|
59
|
+
|
|
60
|
+
The pass shall rewrite an item only when **all** of the following hold:
|
|
61
|
+
|
|
62
|
+
- The item is an acting item with a prompt: a direct-Captain, delegated-role,
|
|
63
|
+
or nested-playbook-call blockquote.
|
|
64
|
+
A script item (`Captain shall run:`) has no prompt and is never rewritten.
|
|
65
|
+
- At least one relay block precedes an instruction line.
|
|
66
|
+
An item whose relay blocks already trail every instruction line is already
|
|
67
|
+
prefix-first and stays unchanged.
|
|
68
|
+
- No instruction line depends on a relayed value standing before it — one
|
|
69
|
+
that refers to the relayed text as above or preceding, or that continues a
|
|
70
|
+
sentence the relay began.
|
|
71
|
+
A reference to the relayed text as below or following stays true after the
|
|
72
|
+
move.
|
|
73
|
+
|
|
74
|
+
Judgment stays conservative: when eligibility is uncertain, the pass shall
|
|
75
|
+
leave the item unchanged rather than guess.
|
|
76
|
+
The pass shall not invent prompt lines, labels, or separators, and shall not
|
|
77
|
+
rewrite, merge, split, or drop any line.
|
|
78
|
+
|
|
79
|
+
Source prose that places a relay after an instruction, or an instruction at
|
|
80
|
+
the end of the prompt, tells [text2gears](text2gears.md) how to compose the
|
|
81
|
+
prompt faithfully; it does not make the item ineligible.
|
|
82
|
+
This pass overrides that composition order by design, records the override in
|
|
83
|
+
its provenance section, and `--no-optimize` restores it.
|
|
84
|
+
|
|
85
|
+
## Rewriting
|
|
86
|
+
|
|
87
|
+
For each eligible item, the pass shall rewrite only the blockquote:
|
|
88
|
+
|
|
89
|
+
- Emit every line that is not part of a relay block, in its original order,
|
|
90
|
+
keeping each line byte-for-byte, dropping leading and trailing blank lines,
|
|
91
|
+
and replacing each run of removed relay blocks together with the blank lines
|
|
92
|
+
bounding it by the first of those blank lines where it separated two
|
|
93
|
+
remaining lines; every other blank line, such as one inside a fenced
|
|
94
|
+
template, stays where it is.
|
|
95
|
+
- Emit one blank line.
|
|
96
|
+
- Emit the relay blocks in their original order, each byte-for-byte, with one
|
|
97
|
+
blank line between consecutive blocks.
|
|
98
|
+
- Keep the item's ID, heading, condition, acting clause, `Results:`, and any
|
|
99
|
+
other metadata unchanged.
|
|
100
|
+
- Preserve every other item and every non-item section byte-for-byte,
|
|
101
|
+
including an `## Optimizations` section the optimize pass appended.
|
|
102
|
+
|
|
103
|
+
Because no line changes, the target stays in the Source language and every
|
|
104
|
+
authored fragment remains intact; only the order of relay blocks relative to
|
|
105
|
+
instructions changes.
|
|
106
|
+
|
|
107
|
+
E.g., the source item
|
|
108
|
+
|
|
109
|
+
```markdown
|
|
110
|
+
### REVIEW-2
|
|
111
|
+
|
|
112
|
+
When Reviewer raises or keeps any finding, Captain shall relay the caller input and Reviewer's findings to Coder with the disposition prompt:
|
|
113
|
+
|
|
114
|
+
> > Original request: <caller-input>
|
|
115
|
+
> > Reviewer findings: <reviewer-output>
|
|
116
|
+
>
|
|
117
|
+
> For each review item, accept or reject it.
|
|
118
|
+
> Report every disposition, all relevant run results, and every rebuttal.
|
|
119
|
+
|
|
120
|
+
Results:
|
|
121
|
+
- `committed`: Coder accepted at least one item and added one new review-fix commit. Output shall include `coderOutput: <verbatim final text>` and `latestCommit: <commit identity>`.
|
|
122
|
+
- `rejectedAll`: Coder rejected every item and made no commit. Output shall include `coderOutput: <verbatim final text>`.
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
becomes
|
|
126
|
+
|
|
127
|
+
```markdown
|
|
128
|
+
### REVIEW-2
|
|
129
|
+
|
|
130
|
+
When Reviewer raises or keeps any finding, Captain shall relay the caller input and Reviewer's findings to Coder with the disposition prompt:
|
|
131
|
+
|
|
132
|
+
> For each review item, accept or reject it.
|
|
133
|
+
> Report every disposition, all relevant run results, and every rebuttal.
|
|
134
|
+
>
|
|
135
|
+
> > Original request: <caller-input>
|
|
136
|
+
> > Reviewer findings: <reviewer-output>
|
|
137
|
+
|
|
138
|
+
Results:
|
|
139
|
+
- `committed`: Coder accepted at least one item and added one new review-fix commit. Output shall include `coderOutput: <verbatim final text>` and `latestCommit: <commit identity>`.
|
|
140
|
+
- `rejectedAll`: Coder rejected every item and made no commit. Output shall include `coderOutput: <verbatim final text>`.
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Provenance
|
|
144
|
+
|
|
145
|
+
The target shall end with one `## Prefixed prompts` section — after an
|
|
146
|
+
`## Optimizations` section when one is present — listing every rewritten
|
|
147
|
+
item, one bullet per item in item order: `- <ITEM-ID>: relays → tail`.
|
|
148
|
+
A section an earlier application left in the source is replaced, and the
|
|
149
|
+
items it listed stay listed.
|
|
150
|
+
When no item is eligible, the target shall be the source content unchanged;
|
|
151
|
+
a package the pass never rewrote carries no `## Prefixed prompts` section.
|
|
152
|
+
|
|
153
|
+
The section is what lets a Source-to-GEARS fidelity checker accept the new
|
|
154
|
+
order: for a listed item it requires each instruction and relay block of the
|
|
155
|
+
fragments the item carries intact, blank lines inside a block included, and
|
|
156
|
+
used exactly once, the instruction blocks in Source order, the relay blocks in
|
|
157
|
+
Source order, and every relay after the last instruction, a relay Source joined
|
|
158
|
+
directly to an instruction staying beside it with its authored boundary, a bare
|
|
159
|
+
relay the prose authored free to trail, and one occurrence standing for one
|
|
160
|
+
authored fragment; for any other item it requires Source order throughout.
|
|
161
|
+
|
|
162
|
+
## Deterministic rewriting
|
|
163
|
+
|
|
164
|
+
The adjacent `prefix-prompts.mjs` tool performs this rewrite exactly:
|
|
165
|
+
|
|
166
|
+
```sh
|
|
167
|
+
node "<definition-directory>/prefix-prompts.mjs" --source "<source.gears.md>" --target "<target.gears.md>" [--keep <ITEM-ID>]...
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
It reads the source, rewrites every item eligible by the mechanical rules
|
|
171
|
+
above, writes the one provenance section and the target, and prints the
|
|
172
|
+
rewritten item IDs — or reports that no item was eligible and the target
|
|
173
|
+
equals the source.
|
|
174
|
+
`--keep` excludes an item the tool would otherwise rewrite.
|
|
175
|
+
|
|
176
|
+
The pass shall use the tool rather than rewrite by hand:
|
|
177
|
+
|
|
178
|
+
1. Read the source and decide, for each item with a relay block preceding an
|
|
179
|
+
instruction line, whether an instruction depends on a relay standing before
|
|
180
|
+
it; that judgment is the pass's only non-mechanical step.
|
|
181
|
+
2. Run the tool with `--keep` for each item so excluded.
|
|
182
|
+
3. Read the target and confirm that every listed item's instructions and
|
|
183
|
+
relays are intact and that nothing else changed.
|
|
184
|
+
|
|
185
|
+
The tool writes only the declared target and never modifies the source.
|
|
186
|
+
|
|
187
|
+
## Out of scope
|
|
188
|
+
|
|
189
|
+
- Any change to prompt wording, conditions, result contracts, or item order.
|
|
190
|
+
- Rewriting behaviors into one another; that is the [optimize](optimize.md)
|
|
191
|
+
pass.
|
|
192
|
+
- Where a transport sends a prompt as one content block, the provider's cache
|
|
193
|
+
boundary is the transport's concern; this pass only makes the shared prefix
|
|
194
|
+
exist.
|
|
195
|
+
|
|
196
|
+
## References
|
|
197
|
+
|
|
198
|
+
[1]: https://platform.claude.com/docs/en/docs/build-with-claude/prompt-caching "Anthropic — Prompt caching: place static content at the beginning of the prompt"
|
|
199
|
+
[2]: https://developers.openai.com/api/docs/guides/prompt-caching "OpenAI — Prompt caching: put stable instructions first, dynamic content at the end"
|
|
200
|
+
[3]: https://ai.google.dev/gemini-api/docs/caching "Google — Gemini context caching: put large and common contents at the beginning of the prompt"
|
package/slc/slc.pin-inputs.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AnyStateMachine, EventObject, PromiseActorLogic } from 'xstate';
|
|
2
|
-
import type { CaptainResult, JsonValue, PlaybookEffectBoundary, PlaybookEffectBoundaryStart, PlaybookEffectLedger, PlaybookEffectLedgerCapability, NormalizedError, PlaybookPendingBossQuestion, PlaybookPorts, PlaybookRepositoryReceipt, PlaybookRuntimeFactory, PlaybookSession, PlaybookState, PlayerResult } from './runtime.js';
|
|
2
|
+
import type { CaptainResult, JsonValue, PlaybookEffectBoundary, PlaybookEffectBoundaryStart, PlaybookEffectLedger, PlaybookEffectLedgerCapability, NormalizedError, PlaybookFailureCause, PlaybookPendingBossQuestion, PlaybookPorts, PlaybookRepositoryReceipt, PlaybookRuntimeFactory, PlaybookSession, PlaybookState, PlayerResult } from './runtime.js';
|
|
3
3
|
export interface PlaybookPendingBossQuestionContext {
|
|
4
4
|
questionId: string;
|
|
5
5
|
resumeStateId: string;
|
|
@@ -115,6 +115,33 @@ export declare const BOSS_REPLY_ERRORS: {
|
|
|
115
115
|
readonly missingQuestion: "needsBossReply outcome missing 'question' field";
|
|
116
116
|
readonly unregisteredState: (stateId: string) => string;
|
|
117
117
|
};
|
|
118
|
+
export declare const ABORTED_FAILURE_CAUSE: PlaybookFailureCause;
|
|
119
|
+
export declare function runtimeDefectCause(reason: string): PlaybookFailureCause;
|
|
120
|
+
export declare function playerFailureCause(input: {
|
|
121
|
+
readonly roleId: string;
|
|
122
|
+
readonly playerId?: string;
|
|
123
|
+
readonly error: unknown;
|
|
124
|
+
}): PlaybookFailureCause;
|
|
125
|
+
/**
|
|
126
|
+
* DR-063 §1: the cause of an unresolved governed settlement. A reconciled
|
|
127
|
+
* mismatch supplies its own receipt-read cause; the reasons a runtime owns map
|
|
128
|
+
* to adjudication, abort, and runtime-defect codes. Shared with DECIDE's
|
|
129
|
+
* bespoke runtime, which decides the same reasons.
|
|
130
|
+
*/
|
|
131
|
+
export declare function governedSettlementCause(reason: string, error: unknown, aborted: boolean, supplied: PlaybookFailureCause | undefined): PlaybookFailureCause;
|
|
132
|
+
/**
|
|
133
|
+
* DR-063 §2: the causes decided for thrown values that cannot carry the
|
|
134
|
+
* marker — a string, a frozen error — kept by identity for as long as the
|
|
135
|
+
* failure stands, so every surface that reads the FSM's own `lastError`
|
|
136
|
+
* publishes the cause decided for exactly that value, whatever turn reads it.
|
|
137
|
+
* A value that carries the marker answers from it; the map is bounded, since
|
|
138
|
+
* only values that refuse the marker need an entry.
|
|
139
|
+
*/
|
|
140
|
+
export interface PlaybookFailureCauseRetention {
|
|
141
|
+
retain(error: unknown, cause: PlaybookFailureCause): void;
|
|
142
|
+
causeOf(error: unknown): PlaybookFailureCause | undefined;
|
|
143
|
+
}
|
|
144
|
+
export declare function createFailureCauseRetention(): PlaybookFailureCauseRetention;
|
|
118
145
|
interface XStateRepositoryOperationSettlement<T> {
|
|
119
146
|
readonly status: 'fulfilled';
|
|
120
147
|
readonly value: T;
|
|
@@ -389,6 +416,13 @@ export declare function normalizeErrorFull(err: unknown): NormalizedError | unde
|
|
|
389
416
|
export declare function pendingBossQuestionFromContext(context: Record<string, unknown>): PlaybookPendingBossQuestionContext | undefined;
|
|
390
417
|
/** Add clarification context without repeating the question in a live conversation. */
|
|
391
418
|
export declare function composePlayerContinuation(input: Pick<PlaybookPlayerInput, 'pendingBossQuestion' | 'bossReply'>, body: string, resuming?: boolean): string;
|
|
419
|
+
/**
|
|
420
|
+
* The block for one governed call, or `undefined` where the call is not
|
|
421
|
+
* effect-authorized — a call whose declared outcomes carry no
|
|
422
|
+
* `one-descendant-commit` disposition may commit nothing, so it is told
|
|
423
|
+
* nothing.
|
|
424
|
+
*/
|
|
425
|
+
export declare function effectAuthorizedPreExistingBlock(effectBoundary: Pick<PlaybookEffectBoundaryStart, 'dispositions'>, baseline: PlaybookRepositoryReceipt['baseline'] | undefined): string | undefined;
|
|
392
426
|
/**
|
|
393
427
|
* Default player-prompt composer (slc/link.md §Player prompt composition).
|
|
394
428
|
* One callback-based pass substitutes each `<fieldName>` placeholder whose
|