@sublang/playbook 10.0.0 → 11.0.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.
@@ -7,6 +7,9 @@ interface SessionAgent {
7
7
  readonly adapter: string;
8
8
  readonly model: TuningSelection;
9
9
  readonly effort: TuningSelection<Effort>;
10
+ /** Adapter-scoped fast mode. Absence is the provider default; `false` is a
11
+ * literal request, so this carries no provider-default sentinel. */
12
+ readonly fastMode?: boolean;
10
13
  readonly instruction?: string;
11
14
  readonly permissions?: PermissionPolicy;
12
15
  }
@@ -29,7 +32,7 @@ interface PlaybookCaptainUnresolvedEffectSettlementInput {
29
32
  readonly rootPlaybookId: string;
30
33
  readonly unresolvedEffects: readonly PlaybookCaptainUnresolvedEffect[];
31
34
  }
32
- type SnapshotAgentEnvelope = DeepReadonly<Omit<SessionAgent, 'model' | 'effort'>>;
35
+ type SnapshotAgentEnvelope = DeepReadonly<Omit<SessionAgent, 'model' | 'effort' | 'fastMode'>>;
33
36
  type PlayerLedgerSnapshotEntry = DeepReadonly<PlayerLedgerEntry>;
34
37
  export interface PlaybookCaptainDeps {
35
38
  loadModule?: (specifier: string) => Promise<unknown>;
@@ -1379,10 +1379,16 @@ function snapshotEffortSelection(value, path) {
1379
1379
  }
1380
1380
  return selection;
1381
1381
  }
1382
+ function snapshotFastMode(value, path) {
1383
+ if (value !== undefined && typeof value !== 'boolean') {
1384
+ throw new TypeError(`${path} must be a boolean`);
1385
+ }
1386
+ return value;
1387
+ }
1382
1388
  function snapshotSessionAgent(value, path) {
1383
1389
  const agent = snapshotRecord(value, path);
1384
- rejectSnapshotKeys(agent, ['adapter', 'model', 'effort', 'instruction', 'permissions'], path);
1385
- const fixed = snapshotFixedAgent(Object.fromEntries(Object.entries(agent).filter(([key]) => key !== 'model' && key !== 'effort')), path);
1390
+ rejectSnapshotKeys(agent, ['adapter', 'model', 'effort', 'fastMode', 'instruction', 'permissions'], path);
1391
+ const fixed = snapshotFixedAgent(Object.fromEntries(Object.entries(agent).filter(([key]) => key !== 'model' && key !== 'effort' && key !== 'fastMode')), path);
1386
1392
  return {
1387
1393
  adapter: fixed.adapter,
1388
1394
  ...(fixed.instruction === undefined
@@ -1393,6 +1399,9 @@ function snapshotSessionAgent(value, path) {
1393
1399
  : { permissions: livePermissions(fixed.permissions) }),
1394
1400
  model: snapshotTuningSelection(agent.model, `${path}.model`),
1395
1401
  effort: snapshotEffortSelection(agent.effort, `${path}.effort`),
1402
+ ...(agent.fastMode === undefined
1403
+ ? {}
1404
+ : { fastMode: snapshotFastMode(agent.fastMode, `${path}.fastMode`) }),
1396
1405
  };
1397
1406
  }
1398
1407
  function fixedAgent(agent) {
@@ -1403,9 +1412,13 @@ function fixedAgent(agent) {
1403
1412
  };
1404
1413
  }
1405
1414
  function callSettings(agent, tuning = agent) {
1415
+ // cligent treats supplied call settings as a complete replacement, so an
1416
+ // omitted fastMode here is a request for the provider default, never an
1417
+ // inheritance of whatever the previous call left behind.
1406
1418
  return {
1407
1419
  model: tuning.model,
1408
1420
  effort: tuning.effort,
1421
+ ...(tuning.fastMode === undefined ? {} : { fastMode: tuning.fastMode }),
1409
1422
  ...(agent.instruction === undefined ? {} : { instruction: agent.instruction }),
1410
1423
  ...(agent.permissions === undefined ? {} : { permissions: agent.permissions }),
1411
1424
  };
@@ -1533,7 +1546,7 @@ async function buildEnablements(options, loadModule, hostCapabilities) {
1533
1546
  for (const role of entry.requiredRoleIds) {
1534
1547
  const path = `captain.options.playbooks.${id}.roles.${role}`;
1535
1548
  const rawBinding = snapshotRecord(roleRecord[role], path);
1536
- rejectSnapshotKeys(rawBinding, ['playerId', 'model', 'effort'], path);
1549
+ rejectSnapshotKeys(rawBinding, ['playerId', 'model', 'effort', 'fastMode'], path);
1537
1550
  const playerId = snapshotString(rawBinding.playerId, `${path}.playerId`);
1538
1551
  if (!PLAYER_ID_PATTERN.test(playerId) || playerId === INTERNAL_CAPTAIN_ID) {
1539
1552
  throw new Error(`${path}.playerId is not a canonical player id`);
@@ -1546,6 +1559,11 @@ async function buildEnablements(options, loadModule, hostCapabilities) {
1546
1559
  playerId,
1547
1560
  model: snapshotTuningSelection(rawBinding.model, `${path}.model`),
1548
1561
  effort: snapshotEffortSelection(rawBinding.effort, `${path}.effort`),
1562
+ ...(rawBinding.fastMode === undefined
1563
+ ? {}
1564
+ : {
1565
+ fastMode: snapshotFastMode(rawBinding.fastMode, `${path}.fastMode`),
1566
+ }),
1549
1567
  agent,
1550
1568
  });
1551
1569
  }
@@ -55,6 +55,9 @@ interface SessionAgent {
55
55
  readonly adapter: string;
56
56
  readonly model: TuningSelection;
57
57
  readonly effort: TuningSelection<Effort>;
58
+ /** Adapter-scoped fast mode. Absence is the provider default; `false` is a
59
+ * literal request, so this carries no provider-default sentinel. */
60
+ readonly fastMode?: boolean;
58
61
  readonly instruction?: string;
59
62
  readonly permissions?: PermissionPolicy;
60
63
  }
@@ -94,7 +97,7 @@ interface PlaybookCaptainUnresolvedEffectSettlementInput {
94
97
  }
95
98
 
96
99
  type SnapshotAgentEnvelope = DeepReadonly<
97
- Omit<SessionAgent, 'model' | 'effort'>
100
+ Omit<SessionAgent, 'model' | 'effort' | 'fastMode'>
98
101
  >;
99
102
 
100
103
  type PlayerLedgerSnapshotEntry = DeepReadonly<PlayerLedgerEntry>;
@@ -377,6 +380,7 @@ interface EffectivePlayerBinding {
377
380
  readonly playerId: string;
378
381
  readonly model: TuningSelection;
379
382
  readonly effort: TuningSelection<Effort>;
383
+ readonly fastMode?: boolean;
380
384
  readonly agent: SessionAgent;
381
385
  }
382
386
 
@@ -2562,6 +2566,16 @@ function snapshotEffortSelection(
2562
2566
  return selection as TuningSelection<Effort>;
2563
2567
  }
2564
2568
 
2569
+ function snapshotFastMode(
2570
+ value: JsonValue | undefined,
2571
+ path: string,
2572
+ ): boolean | undefined {
2573
+ if (value !== undefined && typeof value !== 'boolean') {
2574
+ throw new TypeError(`${path} must be a boolean`);
2575
+ }
2576
+ return value;
2577
+ }
2578
+
2565
2579
  function snapshotSessionAgent(
2566
2580
  value: JsonValue | undefined,
2567
2581
  path: string,
@@ -2569,13 +2583,14 @@ function snapshotSessionAgent(
2569
2583
  const agent = snapshotRecord(value, path);
2570
2584
  rejectSnapshotKeys(
2571
2585
  agent,
2572
- ['adapter', 'model', 'effort', 'instruction', 'permissions'],
2586
+ ['adapter', 'model', 'effort', 'fastMode', 'instruction', 'permissions'],
2573
2587
  path,
2574
2588
  );
2575
2589
  const fixed = snapshotFixedAgent(
2576
2590
  Object.fromEntries(
2577
2591
  Object.entries(agent).filter(
2578
- ([key]) => key !== 'model' && key !== 'effort',
2592
+ ([key]) =>
2593
+ key !== 'model' && key !== 'effort' && key !== 'fastMode',
2579
2594
  ),
2580
2595
  ) as JsonValue,
2581
2596
  path,
@@ -2590,10 +2605,15 @@ function snapshotSessionAgent(
2590
2605
  : { permissions: livePermissions(fixed.permissions) }),
2591
2606
  model: snapshotTuningSelection(agent.model, `${path}.model`),
2592
2607
  effort: snapshotEffortSelection(agent.effort, `${path}.effort`),
2608
+ ...(agent.fastMode === undefined
2609
+ ? {}
2610
+ : { fastMode: snapshotFastMode(agent.fastMode, `${path}.fastMode`) }),
2593
2611
  };
2594
2612
  }
2595
2613
 
2596
- function fixedAgent(agent: SessionAgent): Omit<SessionAgent, 'model' | 'effort'> {
2614
+ function fixedAgent(
2615
+ agent: SessionAgent,
2616
+ ): Omit<SessionAgent, 'model' | 'effort' | 'fastMode'> {
2597
2617
  return {
2598
2618
  adapter: agent.adapter,
2599
2619
  ...(agent.instruction === undefined ? {} : { instruction: agent.instruction }),
@@ -2603,11 +2623,15 @@ function fixedAgent(agent: SessionAgent): Omit<SessionAgent, 'model' | 'effort'>
2603
2623
 
2604
2624
  function callSettings(
2605
2625
  agent: SessionAgent,
2606
- tuning: Pick<SessionAgent, 'model' | 'effort'> = agent,
2626
+ tuning: Pick<SessionAgent, 'model' | 'effort' | 'fastMode'> = agent,
2607
2627
  ): AgentCallSettings {
2628
+ // cligent treats supplied call settings as a complete replacement, so an
2629
+ // omitted fastMode here is a request for the provider default, never an
2630
+ // inheritance of whatever the previous call left behind.
2608
2631
  return {
2609
2632
  model: tuning.model,
2610
2633
  effort: tuning.effort,
2634
+ ...(tuning.fastMode === undefined ? {} : { fastMode: tuning.fastMode }),
2611
2635
  ...(agent.instruction === undefined ? {} : { instruction: agent.instruction }),
2612
2636
  ...(agent.permissions === undefined ? {} : { permissions: agent.permissions }),
2613
2637
  };
@@ -2833,7 +2857,11 @@ async function buildEnablements(
2833
2857
  for (const role of entry.requiredRoleIds) {
2834
2858
  const path = `captain.options.playbooks.${id}.roles.${role}`;
2835
2859
  const rawBinding = snapshotRecord(roleRecord[role], path);
2836
- rejectSnapshotKeys(rawBinding, ['playerId', 'model', 'effort'], path);
2860
+ rejectSnapshotKeys(
2861
+ rawBinding,
2862
+ ['playerId', 'model', 'effort', 'fastMode'],
2863
+ path,
2864
+ );
2837
2865
  const playerId = snapshotString(rawBinding.playerId, `${path}.playerId`);
2838
2866
  if (!PLAYER_ID_PATTERN.test(playerId) || playerId === INTERNAL_CAPTAIN_ID) {
2839
2867
  throw new Error(`${path}.playerId is not a canonical player id`);
@@ -2848,6 +2876,14 @@ async function buildEnablements(
2848
2876
  playerId,
2849
2877
  model: snapshotTuningSelection(rawBinding.model, `${path}.model`),
2850
2878
  effort: snapshotEffortSelection(rawBinding.effort, `${path}.effort`),
2879
+ ...(rawBinding.fastMode === undefined
2880
+ ? {}
2881
+ : {
2882
+ fastMode: snapshotFastMode(
2883
+ rawBinding.fastMode,
2884
+ `${path}.fastMode`,
2885
+ ),
2886
+ }),
2851
2887
  agent,
2852
2888
  });
2853
2889
  }
@@ -8,9 +8,10 @@
8
8
 
9
9
  # The Captain and each stable session player carries its own defaults inline:
10
10
  # an adapter shorthand (claude, codex) or a block with
11
- # adapter/model/effort/permissions. A role may override only model or effort.
12
- # Omit an override to inherit the player default; set it to false to select
13
- # that provider's current default explicitly.
11
+ # adapter/model/effort/fastMode/permissions. A role may override only model,
12
+ # effort, or fastMode.
13
+ # Omit an override to inherit the player default. For model and effort, false
14
+ # selects the provider default; fastMode false is a literal disabled request.
14
15
 
15
16
  # Each adapter needs its vendor SDK installed as its own top-level
16
17
  # install root — they are optional peer dependencies, so you pay only
@@ -29,7 +30,7 @@
29
30
  # list, so those captains degrade to a prompt-level restriction (DR-013 A1).
30
31
  captain:
31
32
  adapter: claude
32
- model: claude-opus-4-8
33
+ model: claude-opus-5
33
34
  effort: high
34
35
  permissions:
35
36
  mode: auto
@@ -45,19 +46,22 @@ notifications:
45
46
  # shares one provider conversation across those playbooks.
46
47
  players:
47
48
  dev.coder:
48
- adapter: claude
49
- model: claude-opus-4-8[1m]
50
- effort: xhigh
49
+ adapter: codex
50
+ model: gpt-5.6-sol
51
+ effort: ultra
52
+ # Adapter-scoped fast mode is validated by the installed Cligent contract;
53
+ # omitting it takes the provider default.
54
+ fastMode: true
51
55
  permissions:
52
56
  mode: auto
57
+ writablePaths: ['.git']
53
58
 
54
59
  dev.reviewer:
55
- adapter: codex
56
- model: gpt-5.5
60
+ adapter: claude
61
+ model: claude-opus-5
57
62
  effort: xhigh
58
63
  permissions:
59
64
  mode: auto
60
- writablePaths: ['.git']
61
65
 
62
66
  # Enabled playbooks. Each is loaded from its explicit `from` module; the
63
67
  # `<id>` key must equal that module's manifest id. `from`, `command`, and
@@ -0,0 +1,82 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
+
4
+ export declare const RECORDS_STREAM_VERSION: 1;
5
+ export declare function defaultSessionsDir(): string;
6
+ export declare function openSessionStore(
7
+ sessionsDir: string,
8
+ ): PlaybookSessionStore;
9
+
10
+ export type ReplayJsonValue =
11
+ | null
12
+ | boolean
13
+ | number
14
+ | string
15
+ | readonly ReplayJsonValue[]
16
+ | ReplayRecord;
17
+ export type ReplayRecord = {
18
+ readonly [key: string]: ReplayJsonValue;
19
+ };
20
+ export interface ReplayStreamEntry {
21
+ readonly v: 1;
22
+ readonly seq: number;
23
+ readonly role?: string;
24
+ readonly record: ReplayRecord;
25
+ }
26
+ export interface ReplayStreamReadOptions {
27
+ readonly afterSeq?: number;
28
+ }
29
+ export interface ReplayStreamReadResult {
30
+ readonly entries: readonly ReplayStreamEntry[];
31
+ readonly lastReadableSeq: number;
32
+ }
33
+ export interface LeaseReplayStreamReadResult extends ReplayStreamReadResult {
34
+ readonly lastDurableSeq: number;
35
+ readonly incomplete: boolean;
36
+ }
37
+ export type ReplayStreamStatus =
38
+ | {
39
+ readonly lastReadableSeq: number;
40
+ readonly lastDurableSeq: number;
41
+ readonly incomplete: boolean;
42
+ }
43
+ | {
44
+ readonly lastReadableSeq: null;
45
+ readonly lastDurableSeq: null;
46
+ readonly incomplete: true;
47
+ };
48
+ export interface PlaybookSessionSummary {
49
+ readonly schemaVersion: number;
50
+ readonly sessionId: string;
51
+ readonly state: 'settled' | 'uncertain';
52
+ readonly cwd: string;
53
+ readonly updatedAt: string;
54
+ }
55
+ export interface SkippedPlaybookSession {
56
+ readonly sessionId: string;
57
+ readonly reason: string;
58
+ }
59
+ export interface PlaybookSessionListResult {
60
+ readonly sessions: readonly PlaybookSessionSummary[];
61
+ readonly skipped: readonly SkippedPlaybookSession[];
62
+ }
63
+ export interface PlaybookSessionStore {
64
+ readonly sessionsDir: string;
65
+ list(): Promise<PlaybookSessionListResult>;
66
+ read(sessionId: string): Promise<PlaybookSessionSummary>;
67
+ readStream(
68
+ sessionId: string,
69
+ options?: ReplayStreamReadOptions,
70
+ ): Promise<ReplayStreamReadResult>;
71
+ acquire(sessionId: string): Promise<PlaybookSessionLease>;
72
+ }
73
+ export interface PlaybookSessionLease {
74
+ readonly sessionId: string;
75
+ readonly ownerToken: string;
76
+ append(record: object, role?: string): Promise<void>;
77
+ readStream(
78
+ options?: ReplayStreamReadOptions,
79
+ ): Promise<LeaseReplayStreamReadResult>;
80
+ streamStatus(): ReplayStreamStatus;
81
+ release(): Promise<ReplayStreamStatus>;
82
+ }
@@ -0,0 +1,113 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
+
4
+ import {
5
+ RECORDS_STREAM_VERSION,
6
+ createCaptainSessionStore,
7
+ defaultCaptainSessionsDir,
8
+ } from './bin/session-store.js';
9
+
10
+ export { RECORDS_STREAM_VERSION };
11
+
12
+ export function defaultSessionsDir() {
13
+ return defaultCaptainSessionsDir();
14
+ }
15
+
16
+ export function openSessionStore(sessionsDir) {
17
+ if (typeof sessionsDir !== 'string') {
18
+ throw new TypeError('session store path must be a string');
19
+ }
20
+ return wrapStore(createCaptainSessionStore({ sessionsDir }));
21
+ }
22
+
23
+ function wrapStore(store) {
24
+ return Object.freeze({
25
+ sessionsDir: store.sessionsDir,
26
+ list: async () => projectListResult(await store.listSummaries()),
27
+ read: async (sessionId) =>
28
+ projectSummary(await store.readSummary(sessionId)),
29
+ readStream: async (sessionId, options) =>
30
+ projectReadResult(await store.readStream(sessionId, options)),
31
+ acquire: async (sessionId) => wrapLease(await store.acquire(sessionId)),
32
+ });
33
+ }
34
+
35
+ function wrapLease(lease) {
36
+ let finalStatus;
37
+ let releaseInFlight;
38
+
39
+ const release = () => {
40
+ if (finalStatus !== undefined) return Promise.resolve(finalStatus);
41
+ if (releaseInFlight !== undefined) return releaseInFlight;
42
+ const operation = lease.release();
43
+ releaseInFlight = operation.then(
44
+ (status) => {
45
+ finalStatus = projectStatus(status);
46
+ return finalStatus;
47
+ },
48
+ (cause) => {
49
+ releaseInFlight = undefined;
50
+ throw cause;
51
+ },
52
+ );
53
+ return releaseInFlight;
54
+ };
55
+
56
+ return Object.freeze({
57
+ sessionId: lease.sessionId,
58
+ ownerToken: lease.ownerToken,
59
+ append: (record, role) => lease.append(record, role),
60
+ readStream: async (options) => {
61
+ if (finalStatus !== undefined) {
62
+ throw new Error('released session lease has no live replay reader');
63
+ }
64
+ return projectLeaseReadResult(await lease.readStream(options));
65
+ },
66
+ streamStatus: () => finalStatus ?? projectStatus(lease.streamStatus()),
67
+ release,
68
+ });
69
+ }
70
+
71
+ function projectListResult(result) {
72
+ return Object.freeze({
73
+ sessions: Object.freeze(result.sessions.map(projectSummary)),
74
+ skipped: Object.freeze(
75
+ result.skipped.map(({ sessionId, reason }) =>
76
+ Object.freeze({ sessionId, reason }),
77
+ ),
78
+ ),
79
+ });
80
+ }
81
+
82
+ function projectSummary(summary) {
83
+ return Object.freeze({
84
+ schemaVersion: summary.schemaVersion,
85
+ sessionId: summary.sessionId,
86
+ state: summary.state,
87
+ cwd: summary.cwd,
88
+ updatedAt: summary.updatedAt,
89
+ });
90
+ }
91
+
92
+ function projectReadResult(result) {
93
+ return Object.freeze({
94
+ entries: Object.freeze([...result.entries]),
95
+ lastReadableSeq: result.lastReadableSeq,
96
+ });
97
+ }
98
+
99
+ function projectLeaseReadResult(result) {
100
+ return Object.freeze({
101
+ ...projectReadResult(result),
102
+ lastDurableSeq: result.lastDurableSeq,
103
+ incomplete: result.incomplete,
104
+ });
105
+ }
106
+
107
+ function projectStatus(status) {
108
+ return Object.freeze({
109
+ lastReadableSeq: status.lastReadableSeq,
110
+ lastDurableSeq: status.lastDurableSeq,
111
+ incomplete: status.incomplete,
112
+ });
113
+ }
@@ -23,31 +23,40 @@ Consult @specs/map.md for relevant context and @specs/meta.md for spec requireme
23
23
  Do not change any files.
24
24
  ```
25
25
 
26
- Neither role's player shall receive the other role's proposal until both proposals are complete and Coder has committed Coder's own proposal.
26
+ Neither role's player shall receive the other role's proposal until both proposals are complete.
27
+ Each proposal is complete only when its player affirmatively provides a complete design proposal.
28
+ A progress report, status update, or promise of a later proposal supports no proposal outcome.
27
29
  A Boss interrupt during the parallel proposal pair shall restart the whole pair so both players receive the same new topic and remain independent.
28
30
 
29
- When both proposals are complete, Captain shall then give Coder the following instruction:
31
+ When both proposals are complete, Captain shall give Coder the following instruction, with the complete topic and Reviewer's complete proposal inserted under their own labels in quotes (`>`):
30
32
 
31
33
  ```markdown
32
- Turn your proposal into the necessary spec items or DRs.
34
+ Synthesize your independent proposal with Reviewer's proposal below.
35
+ Keep to the original topic below and follow what it asks.
36
+ Keep the best, essential parts of either proposal and reject any point that is unsound, unnecessary, or outside the topic.
37
+ Turn the resulting design into the necessary DRs and/or spec items.
33
38
  Follow @specs/meta.md and update @specs/map.md when needed.
34
- Do not inspect or incorporate Reviewer's proposal before this commit.
35
- Do not change code or implement the proposal.
39
+ Do not change code or implement the design.
36
40
 
37
41
  Commit the result as one new commit, following @specs/packages/git.md.
38
42
  Make the commit message explain concisely what changed and why.
39
- Coder is <coder-llm>; format the model token in conventional human form.
43
+ Identify every new commit you make.
44
+ Coder is <coder-llm> and Reviewer is <reviewer-llm>.
45
+
46
+ > Original topic: \<caller-topic\>
47
+ > Reviewer's independent proposal: \<reviewer-proposal\>
40
48
  ```
41
49
 
42
- After Coder commits, Captain shall call playbook `review`:
50
+ No transition shall depend on a fixed presentation format of either player's reply.
51
+ Captain shall use the repository-effect receipt as the authoritative identity of Coder's new `decide`-owned commit.
52
+
53
+ After Coder commits, Captain shall call playbook `review` with the following input in quotes (`>`):
43
54
 
44
- > Review the latest commit as a spec-design change against the initial intent.
45
- > Compare it with your independent proposal and take the best of both.
46
- > Make your suggestions.
47
- >
48
- > Initial intent: \<caller-topic\>.
49
- > Coder's independent proposal: \<coder-proposal\>.
55
+ > Original intent: \<caller-topic\>
56
+ > Review scope: the `decide`-owned commit \<decide-commit\> and its resulting repository state.
57
+ > Coder output: \<coder-output\>
50
58
 
51
- `decide` is complete when `review` returns the approved latest commit and the fact that no unsettled findings remain.
52
- When `review` returns an authored abort or failure, or a terminal result that does not prove exact approval, `decide` shall report the failure and the last `decide`-owned commit to its caller.
59
+ `decide` is complete only when `review` returns a result that applies to the supplied review scope, gives the exact evaluated repository revision, and affirmatively establishes that no unsettled findings remain.
60
+ It then returns the `decide`-owned commit and that evaluated revision to its caller.
61
+ When `review` returns an authored abort or failure, or a terminal result that does not establish those facts, `decide` shall report the failure and the last `decide`-owned commit to its caller.
53
62
  When the nested `review` call fails outside that authored result contract, `decide` shall park as failed and retain the control-plane error instead of reporting an authored review outcome.
@@ -9,45 +9,55 @@ Roles:
9
9
  - Coder
10
10
  - Reviewer
11
11
 
12
- The caller supplies the initial intent or any specific context and relevant run results.
12
+ The caller supplies:
13
13
 
14
- `review` examines committed work only.
15
- Every review round begins with a new or existing commit.
14
+ - the original intent;
15
+ - the review scope in the caller's own words;
16
+ - optional relevant context and run results.
16
17
 
17
- At the first review round, Captain shall relay to Reviewer the complete initial review request in quotes (`>`).
18
+ The caller's review scope is the baseline for every round, and each review-fix commit joins that scope as it lands, so every later round reviews the cumulative committed state.
19
+ `review` examines committed work only.
20
+ Captain shall take the evaluated repository revision from repository authority, not from either player's prose.
18
21
 
19
22
  At the first review round, Captain shall give Reviewer the following instruction:
20
23
 
21
24
  ```markdown
22
- A new review begins on the latest commit.
23
- Review the latest commit and resulting repository state; read the commit message first for its intent, scope, and rationale.
25
+ A new review begins for the review scope.
26
+ Keep to the original intent and follow what it asks.
27
+ When the scope names commits, read each commit message for its context and rationale; otherwise use repository history and commit messages wherever they help establish that context.
24
28
  ```
25
29
 
26
30
  After every review-fix commit, Captain shall give Reviewer the following instruction:
27
31
 
28
32
  ```markdown
29
- Review the latest commit and resulting repository state; read the commit message first for its intent, scope, and rationale.
33
+ A new review round begins for the review scope in the cumulative committed state, with particular attention to the latest review-fix commit.
34
+ Keep to the original intent and follow what it asks.
35
+ Read the latest review-fix commit's message and see Coder's feedback below.
30
36
  ```
31
37
 
32
38
  When Coder rejects every finding and makes no commit, Captain shall give Reviewer the following instruction:
33
39
 
34
40
  ```markdown
35
41
  No new commit was made because Coder rejected every finding.
42
+ See Coder's feedback below.
36
43
  ```
37
44
 
38
- At the start of *every* review round, Captain shall relay to Reviewer any Coder feedback from the preceding round and any relevant run results, in quotes (`>`) after the instruction.
45
+ At the start of *every* review round, Captain shall relay to Reviewer the original intent, the review scope and context, the exact repository revision being evaluated, any Coder feedback from the preceding round, and any relevant run results, in quotes (`>`) after the instruction.
39
46
 
40
47
  At the start of *every* review round, Captain shall append the following instruction to the end of the prompt:
41
48
 
42
49
  ```markdown
43
50
  Understand the full picture and think systematically about the underlying design.
44
- Continue to identify issues or improvements, if any (numbered; no duplication).
45
- For any rebuttal, accept or challenge it.
46
- Treat as settled, and do not raise again, any finding in this review rejected twice with reasoning.
51
+ Continue to identify issues or improvements, if any, without duplication.
52
+ Number the findings consistently across rounds.
47
53
  Flag only what materially affects correctness, behavior, or spec quality — not style, equally valid alternatives, or theoretical threats.
48
- For specs, flag stale, missing, over-specified, or under-specified ones.
54
+ For specs, flag stale, missing, over-specified, or under-specified ones, if any.
49
55
  Avoid unnecessary complexity in code or tests, but flag any fundamental design flaw when leaving it would cost more in later patches than fixing it now.
50
56
 
57
+ If an issue represents a class of defect, find every instance within the review scope worth fixing rather than surfacing one or two per round, which drags out the review.
58
+ For any rebuttal, accept or challenge it.
59
+ Treat as settled, and do not raise again, any finding in this review rejected twice with reasoning.
60
+
51
61
  Do not re-run tests or builds whose inputs have not changed since any previous reported run.
52
62
  Do not edit files or commit; report findings only.
53
63
 
@@ -55,26 +65,34 @@ Consult @specs/map.md for context if needed; verify it remains accurate.
55
65
  Consult @specs/meta.md for spec requirements if needed; verify affected specs follow it.
56
66
  ```
57
67
 
58
- When Reviewer raises or keeps any finding, Captain shall relay the Reviewer's findings and any relevant run results to Coder, in quotes (`>`), along with the following prompt:
68
+ Finding numbers are references within this review only.
69
+ No review transition shall depend on numbering or any fixed presentation format of either player's reply.
70
+
71
+ When Reviewer raises or keeps any finding, Captain shall relay the original intent, the review scope and context, the exact repository revision being evaluated, the Reviewer's findings, and any relevant run results to Coder, in quotes (`>`), along with the following prompt:
59
72
 
60
73
  ```markdown
61
74
  For each review item, accept or reject it.
62
75
  Before deciding, understand the full picture and think systematically about the underlying design.
76
+ Keep to the original intent and follow what it asks.
63
77
  Reject anything that is not essential or is not worth fixing now.
64
- If you accept an item, fix its root cause, including any fundamental design flaw - do not patch around it.
78
+ If you accept an item, fix its root cause, including any fundamental design flaw do not patch around it; if it represents a class of defect, find every instance within the review scope worth fixing rather than addressing one or two per round, which drags out the review.
65
79
  If you reject an item, give the reasoning and cite code or test output that supports it.
66
80
  Do not re-run tests or builds whose inputs have not changed since any previous reported run.
67
81
 
68
- If you accept any item, make minimal changes and commit them as one new commit; never amend the reviewed commit.
82
+ If you accept any item, make minimal changes and add one new review-fix commit; never rewrite any existing commit.
69
83
  Follow @specs/packages/git.md.
70
84
  Make the commit message explain concisely what changed and why, including relevant verification.
71
- Coder is <coder-llm>; Reviewer is <reviewer-llm>; format model tokens in conventional human form.
85
+ Identify every new commit you make.
86
+ Coder is <coder-llm>; Reviewer is <reviewer-llm>.
72
87
 
73
88
  If you reject every item, change nothing and make no commit.
74
89
  Report every disposition, all relevant run results, and every rebuttal.
75
90
  ```
76
91
 
92
+ Captain shall use the repository-effect receipt as the authoritative identity of any review-fix commit.
93
+
77
94
  When Coder makes a new commit or decides to reject all findings, Captain shall relay the above required context to Reviewer and begin the next review round.
78
95
  Reviewer shall read the context information and follow the corresponding instructions.
79
- Rounds continue until Reviewer raises no unsettled findings.
80
- `review` then returns the approved latest commit and the fact that no findings remain to its caller.
96
+ Rounds continue until Reviewer affirmatively reports that the requested review is complete and no unsettled findings remain.
97
+ A progress report, status update, or promise of a later result supports no review outcome.
98
+ `review` then returns the exact repository revision at which the review scope was evaluated and the fact that no unsettled findings remain within that scope.