@intentic/sandbox-contract 1.224.0 → 1.225.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.
Files changed (107) hide show
  1. package/README.md +14 -14
  2. package/dist/agent-catalog.js +3 -3
  3. package/dist/agent-catalog.js.map +1 -1
  4. package/dist/chores/chores.js +42 -42
  5. package/dist/chores/chores.js.map +1 -1
  6. package/dist/chores/extension-update.js +2 -2
  7. package/dist/chores/extension-update.js.map +1 -1
  8. package/dist/chores/fix-deps.js +1 -1
  9. package/dist/chores/fix-deps.js.map +1 -1
  10. package/dist/chores/probes.js +1 -1
  11. package/dist/chores/probes.js.map +1 -1
  12. package/dist/chores/verdict.js +2 -2
  13. package/dist/chores/verdict.js.map +1 -1
  14. package/dist/contracts/capabilities.contract.d.ts +41 -0
  15. package/dist/contracts/capabilities.contract.d.ts.map +1 -1
  16. package/dist/contracts/exit.contract.d.ts +80 -0
  17. package/dist/contracts/exit.contract.d.ts.map +1 -0
  18. package/dist/contracts/exit.contract.js +13 -0
  19. package/dist/contracts/exit.contract.js.map +1 -0
  20. package/dist/contracts/settings.contract.d.ts +12 -0
  21. package/dist/contracts/settings.contract.d.ts.map +1 -1
  22. package/dist/events.d.ts +4 -4
  23. package/dist/events.d.ts.map +1 -1
  24. package/dist/events.js +5 -5
  25. package/dist/events.js.map +1 -1
  26. package/dist/fast-tier.d.ts +9 -0
  27. package/dist/fast-tier.d.ts.map +1 -0
  28. package/dist/fast-tier.js +19 -0
  29. package/dist/fast-tier.js.map +1 -0
  30. package/dist/history-state.js +3 -3
  31. package/dist/history-state.js.map +1 -1
  32. package/dist/index.d.ts +203 -68
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +5 -0
  35. package/dist/index.js.map +1 -1
  36. package/dist/model-order.d.ts +1 -0
  37. package/dist/model-order.d.ts.map +1 -1
  38. package/dist/model-order.js +5 -0
  39. package/dist/model-order.js.map +1 -1
  40. package/dist/output-fields.d.ts.map +1 -1
  41. package/dist/output-fields.js +2 -2
  42. package/dist/output-fields.js.map +1 -1
  43. package/dist/prompt-complexity.d.ts +19 -0
  44. package/dist/prompt-complexity.d.ts.map +1 -0
  45. package/dist/prompt-complexity.js +83 -0
  46. package/dist/prompt-complexity.js.map +1 -0
  47. package/dist/publish-drafts.js +2 -2
  48. package/dist/publish-drafts.js.map +1 -1
  49. package/dist/schemas.d.ts +214 -0
  50. package/dist/schemas.d.ts.map +1 -1
  51. package/dist/schemas.js +113 -2
  52. package/dist/schemas.js.map +1 -1
  53. package/dist/workflow-faults.js +3 -3
  54. package/dist/workflow-faults.js.map +1 -1
  55. package/dist/workspace-state.d.ts +20 -20
  56. package/dist/workspace-state.d.ts.map +1 -1
  57. package/dist/workspace-state.js +20 -20
  58. package/dist/workspace-state.js.map +1 -1
  59. package/package.json +5 -5
  60. package/src/agent-catalog.test.ts +25 -25
  61. package/src/agent-catalog.ts +3 -3
  62. package/src/agent-run-model.test.ts +3 -3
  63. package/src/capability-ledger.test.ts +13 -13
  64. package/src/chores/chores.test.ts +1 -1
  65. package/src/chores/chores.ts +42 -42
  66. package/src/chores/digest.test.ts +1 -1
  67. package/src/chores/extension-update.ts +2 -2
  68. package/src/chores/fix-deps.ts +1 -1
  69. package/src/chores/probes.test.ts +6 -6
  70. package/src/chores/probes.ts +1 -1
  71. package/src/chores/stack.test.ts +3 -3
  72. package/src/chores/verdict.test.ts +20 -20
  73. package/src/chores/verdict.ts +2 -2
  74. package/src/contract-lock.test.ts +1 -1
  75. package/src/contracts/exit.contract.ts +42 -0
  76. package/src/contracts/providers.contract.ts +1 -1
  77. package/src/conversation-ids.ts +1 -1
  78. package/src/events.test.ts +3 -3
  79. package/src/events.ts +6 -6
  80. package/src/fast-tier.test.ts +88 -0
  81. package/src/fast-tier.ts +72 -0
  82. package/src/history-state.ts +3 -3
  83. package/src/hostnames.test.ts +1 -1
  84. package/src/index.ts +5 -0
  85. package/src/model-order.test.ts +11 -11
  86. package/src/model-order.ts +22 -0
  87. package/src/output-fields.ts +2 -2
  88. package/src/path-refs.test.ts +4 -4
  89. package/src/prompt-complexity.test.ts +160 -0
  90. package/src/prompt-complexity.ts +271 -0
  91. package/src/publish-drafts.ts +2 -2
  92. package/src/quick-model.test.ts +11 -11
  93. package/src/routes.test.ts +11 -5
  94. package/src/runtime-state.test.ts +1 -1
  95. package/src/schemas.test.ts +8 -8
  96. package/src/schemas.ts +311 -6
  97. package/src/search-globs.test.ts +2 -2
  98. package/src/share-paths.test.ts +1 -1
  99. package/src/title.test.ts +9 -9
  100. package/src/title.ts +1 -1
  101. package/src/tunnel-ids.test.ts +3 -3
  102. package/src/versions.test.ts +3 -3
  103. package/src/versions.ts +1 -1
  104. package/src/workflow-faults.test.ts +6 -6
  105. package/src/workflow-faults.ts +3 -3
  106. package/src/workspace-state.test.ts +32 -32
  107. package/src/workspace-state.ts +20 -20
@@ -26,7 +26,7 @@ import type { AgentHarness, AgentProvider, PermissionMode } from "./schemas.js";
26
26
  * drops the field. The record is what fixed that, and this is what keeps it honest.
27
27
  *
28
28
  * By SHAPE, not by a list: PROVIDERS × HARNESSES comes from the catalog itself, so a provider added tomorrow is
29
- * covered the day it is added. If a pair fails here, the answer is a row in capabilitiesOf never a special
29
+ * covered the day it is added. If a pair fails here, the answer is a row in capabilitiesOf: never a special
30
30
  * case in the surface that asked. */
31
31
 
32
32
  const pairs: { provider: AgentProvider; harness: AgentHarness }[] = PROVIDERS.flatMap((provider) =>
@@ -42,20 +42,20 @@ describe("every provider/harness pair declares what it can do", () => {
42
42
  expect(["claude-code", "codex", "opencode", "opencode-gemini", "acp", "pi"]).toContain(capabilities.runtime);
43
43
  expect(["modes", "plan"]).toContain(capabilities.permissions);
44
44
  expect(["full", "browser", "http", "none"]).toContain(capabilities.mcp);
45
- // Every runtime executes SOMETHING a record listing no backend would hide the shell every loop has.
45
+ // Every runtime executes SOMETHING: a record listing no backend would hide the shell every loop has.
46
46
  expect(capabilities.execution).toContain("shell");
47
47
  for (const backend of capabilities.execution) {
48
48
  expect(["shell", "js"]).toContain(backend);
49
49
  }
50
50
  expect(["namespace", "cwd"]).toContain(capabilities.isolation);
51
51
  expect(["replace", "append", "none"]).toContain(capabilities.instructions);
52
- // The permission modes offered must include the mode a clamp falls back to a floor that isn't in the
52
+ // The permission modes offered must include the mode a clamp falls back to: a floor that isn't in the
53
53
  // list would leave the composer showing a posture the runtime can't hold.
54
54
  expect(modesFor(capabilities)).toContain(clampMode("default", capabilities));
55
55
  });
56
56
  });
57
57
 
58
- // An ACP provider is an installed capability's id any string that isn't a native provider. It gets the ACP
58
+ // An ACP provider is an installed capability's id: any string that isn't a native provider. It gets the ACP
59
59
  // floor whatever harness the client happened to send, because the agent IS its own loop.
60
60
  test("an unknown provider id is an ACP agent, on either harness", () => {
61
61
  for (const harness of HARNESSES) {
@@ -63,12 +63,12 @@ test("an unknown provider id is an ACP agent, on either harness", () => {
63
63
  }
64
64
  });
65
65
 
66
- /* THE `pi` ID IS RESERVED for the Pi coding agent's own RPC runtime an `agent`-kind capability like any ACP
66
+ /* THE `pi` ID IS RESERVED for the Pi coding agent's own RPC runtime: an `agent`-kind capability like any ACP
67
67
  * agent, but served over Pi's JSONL protocol, which carries abilities the ACP floor cannot: real mid-turn
68
68
  * steering, the thinking-level scale, a published command list. Falling to the ACP record instead would strip
69
69
  * exactly the abilities that justify a fifth runtime. */
70
70
  describe("the pi provider", () => {
71
- it("runs the pi runtime on either harness pi is its own loop", () => {
71
+ it("runs the pi runtime on either harness: pi is its own loop", () => {
72
72
  for (const harness of HARNESSES) {
73
73
  expect(capabilitiesOf("pi", harness.value).runtime).toBe("pi");
74
74
  }
@@ -96,7 +96,7 @@ describe("the pi provider", () => {
96
96
  * "there is nothing to choose". Claude is always its own Claude Code loop. Kimi has no native runtime at all
97
97
  * (Moonshot speaks the Anthropic protocol directly), so it runs Claude Code whatever the client sent. Gemini is
98
98
  * Kimi's mirror: it has no CLAUDE CODE road, because that loop announces itself in every request and Google's
99
- * Antigravity channel refuses on the announcement every account, every time, reported as a spent quota it
99
+ * Antigravity channel refuses on the announcement: every account, every time, reported as a spent quota it
100
100
  * never was. */
101
101
  test("only codex and grok change runtime with the harness", () => {
102
102
  const switched = PROVIDERS.filter((provider) => capabilitiesOf(provider.value, "native") !== capabilitiesOf(provider.value, "claude-code"));
@@ -105,26 +105,26 @@ test("only codex and grok change runtime with the harness", () => {
105
105
  });
106
106
 
107
107
  /* THE RULE THAT KEEPS CLAUDE CODE TRAFFIC AWAY FROM GOOGLE, asserted where it is decided rather than at each of
108
- * the surfaces that obey it. Everything downstream the adapter that serves a turn, the transcript store, the
109
- * quick helper's choice of loop reads the runtime off this record, so a Gemini turn asking for Claude Code and
108
+ * the surfaces that obey it. Everything downstream: the adapter that serves a turn, the transcript store, the
109
+ * quick helper's choice of loop: reads the runtime off this record, so a Gemini turn asking for Claude Code and
110
110
  * getting it back would put the refused loop on the road again everywhere at once. */
111
111
  test("gemini answers with its own runtime whatever harness is asked for", () => {
112
112
  const native = capabilitiesOf("gemini", "native");
113
113
 
114
114
  expect(native.runtime).toBe("opencode-gemini");
115
115
  expect(capabilitiesOf("gemini", "claude-code")).toEqual(native);
116
- // Same abilities as the Grok loop it shares only the runtime id, which keys adapter health, differs.
116
+ // Same abilities as the Grok loop it shares: only the runtime id, which keys adapter health, differs.
117
117
  expect({ ...native, runtime: "opencode" }).toEqual(capabilitiesOf("grok", "native"));
118
118
  });
119
119
 
120
- test("codex and grok under the Claude Code harness get the full ceiling it is the same loop", () => {
120
+ test("codex and grok under the Claude Code harness get the full ceiling, it is the same loop", () => {
121
121
  for (const provider of ["codex", "grok"] as const) {
122
122
  expect(capabilitiesOf(provider, "claude-code")).toEqual(capabilitiesOf("claude", "native"));
123
123
  }
124
124
  });
125
125
 
126
126
  // The composer's four modes describe behaviours the Claude Code loop actually has. A runtime whose every tool
127
- // call is pre-approved has two postures, so it is offered two names not four names for two behaviours.
127
+ // call is pre-approved has two postures, so it is offered two names, not four names for two behaviours.
128
128
  test("a plan-only runtime offers the two postures it has", () => {
129
129
  expect(modesFor(capabilitiesOf("codex", "native"))).toEqual(["plan", "bypassPermissions"]);
130
130
  expect(modesFor(capabilitiesOf("claude", "native"))).toEqual(["default", "acceptEdits", "plan", "bypassPermissions"]);
@@ -138,7 +138,7 @@ test("a mode the runtime can't hold falls back to the one it runs; one it can ho
138
138
  expect(clampMode("acceptEdits", capabilitiesOf("claude", "native"))).toBe("acceptEdits");
139
139
  });
140
140
 
141
- /* The user-facing half. The full ceiling says nothing an empty list is what hides the picker's block and
141
+ /* The user-facing half. The full ceiling says nothing: an empty list is what hides the picker's block, and
142
142
  * every axis the record carries has a sentence here, because a capability nobody can read is how this started. */
143
143
  test("the ceiling has nothing to disclose; a floor names what it lacks", () => {
144
144
  expect(limitationsOf(capabilitiesOf("claude", "native"))).toEqual([]);
@@ -149,15 +149,15 @@ test("the ceiling has nothing to disclose; a floor names what it lacks", () => {
149
149
  expect(grok).toContain("no effort control");
150
150
  expect(grok).toContain("worktree by working directory only");
151
151
 
152
- // ACP takes our http MCP tools when it advertises them, so its line is a narrowing rather than an absence
152
+ // ACP takes our http MCP tools when it advertises them, so its line is a narrowing rather than an absence:
153
153
  // and it publishes commands and terminals, which must NOT be listed as missing.
154
154
  const acp = limitationsOf(capabilitiesOf("some-installed-agent", "native"));
155
- expect(acp).toContain("MCP tools only no plugins or browser");
155
+ expect(acp).toContain("MCP tools only: no plugins or browser");
156
156
  expect(acp).not.toContain("no slash commands");
157
157
  expect(acp).not.toContain("no terminal panel");
158
158
 
159
159
  const codex = limitationsOf(capabilitiesOf("codex", "native"));
160
- expect(codex).toContain("browser tools only no plugins or other MCP tools");
160
+ expect(codex).toContain("browser tools only: no plugins or other MCP tools");
161
161
  expect(codex).not.toContain("no MCP tools or plugins");
162
162
  });
163
163
 
@@ -179,7 +179,7 @@ test("every axis a record can lack has words for it", () => {
179
179
  };
180
180
 
181
181
  // Eleven DISCLOSABLE axes, eleven sentences: an axis added to the interface without one would silently
182
- // never be disclosed. fastMode is the deliberate twelfth a record alone can't tell the truth about it (a
182
+ // never be disclosed. fastMode is the deliberate twelfth: a record alone can't tell the truth about it (a
183
183
  // translator-routed turn reads true here and still can't go fast), so it is answered by fastAllowed
184
184
  // instead. Anything else added to the interface has to move this number.
185
185
  expect(limitationsOf(nothing)).toHaveLength(11);
@@ -196,12 +196,12 @@ test("the instruction axis discloses its two weaker answers, differently", () =>
196
196
  expect(grok).toContain("added to theirs");
197
197
  expect(grok).not.toContain("isn't applied");
198
198
  expect(acp).toContain("isn't applied");
199
- // Codex on its own runtime replaces, like the Claude Code loop so it has nothing to disclose here.
199
+ // Codex on its own runtime replaces, like the Claude Code loop, so it has nothing to disclose here.
200
200
  expect(limitationsOf(capabilitiesOf("codex", "native")).join(" ")).not.toContain("system prompt");
201
201
  });
202
202
 
203
203
  /* The JS backend is hosted by the one loop the daemon can put its own execution seam through. Pinned as a test
204
- * rather than left to the records because turn planning gates the backend on this axis a runtime gaining it
204
+ * rather than left to the records because turn planning gates the backend on this axis: a runtime gaining it
205
205
  * here without a seam behind it would advertise code runs that can never execute. */
206
206
  test("only the Claude Code loop hosts the js execution backend", () => {
207
207
  for (const { provider, harness } of pairs) {
@@ -230,7 +230,7 @@ describe("the max-effort rule", () => {
230
230
  expect(effortAllowed("high", "kimi", false)).toBe(true);
231
231
  });
232
232
 
233
- it("is repaired, not refused, on the way to the API the tier drops, the user's thinking choice does not", () => {
233
+ it("is repaired, not refused, on the way to the API: the tier drops, the user's thinking choice does not", () => {
234
234
  expect(sendableEffort("max", false)).toBe("high");
235
235
  expect(sendableEffort("max", undefined)).toBe("high");
236
236
  expect(sendableEffort("max", true)).toBe("max");
@@ -241,7 +241,7 @@ describe("the max-effort rule", () => {
241
241
 
242
242
  /* AN `endpoint/<id>` PROVIDER is a model API the user configured, and the one thing this record has to get right
243
243
  * about it is that it is NOT an ACP agent. Both are minted by installing a capability and both are unknown to
244
- * NATIVE_PROVIDERS, so the id is the only thing that tells them apart and they want opposite records: an ACP
244
+ * NATIVE_PROVIDERS, so the id is the only thing that tells them apart, and they want opposite records: an ACP
245
245
  * agent brings its own loop and gets the documented floor, while an endpoint is driven BY the Claude Code loop
246
246
  * and gets its full ceiling. Getting this backwards would strip steering, per-tool approvals, MCP and the mount
247
247
  * namespace from every turn on a user's own model. */
@@ -262,7 +262,7 @@ describe("a configured model endpoint", () => {
262
262
  expect(endpointProvider("gpu-box")).toBe("endpoint/gpu-box");
263
263
  expect(endpointIdOf(endpointProvider("gpu-box"))).toBe("gpu-box");
264
264
  expect(isEndpointProvider("endpoint/gpu-box")).toBe(true);
265
- // A bare id that merely starts with the word is not one the separator is what makes the namespace.
265
+ // A bare id that merely starts with the word is not one: the separator is what makes the namespace.
266
266
  expect(isEndpointProvider("endpoints-r-us")).toBe(false);
267
267
  expect(endpointIdOf("claude")).toBeUndefined();
268
268
  // Its credential was configured with the endpoint, so there is nothing left for a connect gate to offer.
@@ -271,7 +271,7 @@ describe("a configured model endpoint", () => {
271
271
  });
272
272
 
273
273
  /* FAST SPEED IS OFFERED ON THREE CONDITIONS AT ONCE, and the interesting cases are the ones where two of them
274
- * hold. A translator-routed provider runs the Claude Code loop same record, same ceiling and still cannot go
274
+ * hold. A translator-routed provider runs the Claude Code loop: same record, same ceiling, and still cannot go
275
275
  * fast, because the harness refuses a non-Anthropic endpoint; a Claude model that publishes no `fast` badge
276
276
  * cannot either. Both would be silent failures if the composer offered the control anyway: the turn runs, the
277
277
  * answer arrives, and only the bill says it was standard speed. */
@@ -281,7 +281,7 @@ describe("offering fast speed", () => {
281
281
  });
282
282
 
283
283
  it("is refused for a routed provider on the Claude Code loop, whose endpoint is not first-party", () => {
284
- // Grok under the claude-code harness reads the FULL Claude Code record the capability alone would say
284
+ // Grok under the claude-code harness reads the FULL Claude Code record: the capability alone would say
285
285
  // yes. It is served through the sandbox's translator, so the harness would report `not_first_party`.
286
286
  expect(capabilitiesOf("grok", "claude-code").fastMode).toBe(true);
287
287
  expect(fastAllowed(capabilitiesOf("grok", "claude-code"), "grok", ["fast"])).toBe(false);
@@ -291,7 +291,7 @@ describe("offering fast speed", () => {
291
291
 
292
292
  it("is refused for a Claude model whose catalog row doesn't publish it", () => {
293
293
  expect(fastAllowed(capabilitiesOf("claude", "native"), "claude", ["reasoning"])).toBe(false);
294
- // The seed floor and any provider that reports ids only no capabilities published, nothing claimed.
294
+ // The seed floor and any provider that reports ids only: no capabilities published, nothing claimed.
295
295
  expect(fastAllowed(capabilitiesOf("claude", "native"), "claude", undefined)).toBe(false);
296
296
  });
297
297
 
@@ -486,11 +486,11 @@ export const limitationsOf = (capabilities: AgentCapabilities): string[] => [
486
486
  ...(capabilities.mcp === "none"
487
487
  ? ["no MCP tools or plugins"]
488
488
  : capabilities.mcp === "http"
489
- ? ["MCP tools only no plugins or browser"]
489
+ ? ["MCP tools only: no plugins or browser"]
490
490
  : capabilities.mcp === "browser"
491
- ? ["browser tools only no plugins or other MCP tools"]
491
+ ? ["browser tools only: no plugins or other MCP tools"]
492
492
  : []),
493
- ...(capabilities.execution.includes("js") ? [] : ["no code runs its shell is the one way to execute"]),
493
+ ...(capabilities.execution.includes("js") ? [] : ["no code runs, its shell is the one way to execute"]),
494
494
  ...(capabilities.effort ? [] : ["no effort control"]),
495
495
  ...(capabilities.commands ? [] : ["no slash commands"]),
496
496
  ...(capabilities.terminals ? [] : ["no terminal panel"]),
@@ -3,7 +3,7 @@ import { resolveAgentRunModels } from "./agent-run-model.js";
3
3
  import type { QuickModelSource } from "./quick-model.js";
4
4
 
5
5
  /* Which model a run somebody's BUTTON started opens on. The rule answers the same two surfaces its quick-model
6
- * sibling does the daemon walks it, the settings row names it so what these pin is the pair of properties
6
+ * sibling does: the daemon walks it, the settings row names it, so what these pin is the pair of properties
7
7
  * that separate the two: an account this sandbox cannot reach never sits at the head of the chain, and an empty
8
8
  * answer stays empty rather than being filled in with a tier nobody chose. */
9
9
 
@@ -13,7 +13,7 @@ const GOOGLE: QuickModelSource = { provider: `gemini`, ready: true, models: [`ge
13
13
 
14
14
  const offline = (source: QuickModelSource): QuickModelSource => ({ ...source, ready: false });
15
15
 
16
- test("keeps the user's own order this list is read, never ranked", () => {
16
+ test("keeps the user's own order, this list is read, never ranked", () => {
17
17
  // The opposite of the quick chain, which sorts by tier and cost. Here the order IS the setting: someone who
18
18
  // put Opus above GPT wants Opus first, and a resolver that knew better would spend the wrong account.
19
19
  expect(resolveAgentRunModels([CLAUDE, CODEX], [`codex:gpt-5.6`, `claude:claude-opus-5`])).toEqual([
@@ -30,7 +30,7 @@ test("steps over a provider this sandbox has no credential for", () => {
30
30
  ]);
31
31
  });
32
32
 
33
- test("resolves to nothing when no pin is reachable it does NOT fall back to whatever is connected", () => {
33
+ test("resolves to nothing when no pin is reachable: it does NOT fall back to whatever is connected", () => {
34
34
  // The deliberate difference from resolveQuickModels, which lands on its Auto ladder here. An agent run is
35
35
  // billed in whole sessions, so an unreachable list hands the choice back to the caller's floor (the user's
36
36
  // own composer pick) rather than spending an account they never pointed at.
@@ -1,16 +1,16 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import { type AgentCapabilities, capabilitiesOf, limitationsOf } from "./agent-catalog.js";
3
3
 
4
- /* THE CAPABILITY LEDGER which claims in the record are backed by machinery, and which by prose.
4
+ /* THE CAPABILITY LEDGER, which claims in the record are backed by machinery, and which by prose.
5
5
  *
6
6
  * agent-catalog.test.ts asks two other questions: does every provider/harness pair HAVE a row, and does every
7
7
  * axis a record can lack have words for it. A row can pass both and still be a lie. The ability is declared,
8
- * the sentence renders in the picker's footer, and nothing anywhere behaves differently so the day the
8
+ * the sentence renders in the picker's footer, and nothing anywhere behaves differently, so the day the
9
9
  * runtime gains the ability (or loses it), the declaration keeps its old value and the footer keeps telling the
10
10
  * user something that stopped being true. Over-promising is the direction that hurts: told a runtime can be
11
11
  * steered, someone types a mid-turn correction into a queue that does not exist.
12
12
  *
13
- * The record's header already states the rule "a capability is listed here only if something READS it". This
13
+ * The record's header already states the rule: "a capability is listed here only if something READS it". This
14
14
  * is the rule with teeth. Every field is ENFORCED (a seam opens or closes on it, and the entry cites where) or
15
15
  * DESCRIPTIVE (it gates nothing; its whole job is that one sentence). `Record<keyof AgentCapabilities, …>`
16
16
  * makes the split exhaustive at COMPILE time: a field added to the interface without an entry here does not
@@ -19,7 +19,7 @@ import { type AgentCapabilities, capabilitiesOf, limitationsOf } from "./agent-c
19
19
  *
20
20
  * WHAT THIS DOES NOT DO, deliberately: prove an ENFORCED gate exists. Those gates live in the daemon and the
21
21
  * web, which sit ABOVE this package and must not be imported back into it. So an enforced entry's citation is a
22
- * comment a reader checks, and the honest reading is "someone verified this once" not "CI verifies it". The
22
+ * comment a reader checks, and the honest reading is "someone verified this once", not "CI verifies it". The
23
23
  * descriptive half IS machine-checked below, because it can be: those claims reach the user through
24
24
  * `limitationsOf`, which is right here.
25
25
  *
@@ -31,7 +31,7 @@ const LEDGER: Record<keyof AgentCapabilities, Backing> = {
31
31
  // adapter-registry.ts maps runtime → the adapter that serves the turn; getting it wrong runs the turn on
32
32
  // the wrong loop entirely. Also keys the daemon's per-runtime health probes (useSandboxVersion.ts).
33
33
  runtime: "enforced",
34
- // conversation.ts's `steerable` the composer offers mid-turn injection only where there is a queue.
34
+ // conversation.ts's `steerable`: the composer offers mid-turn injection only where there is a queue.
35
35
  steering: "enforced",
36
36
  // turn-plan.ts forwards `permissionMode` only under "modes" (bar plan); modesFor/clampMode build the
37
37
  // composer's mode list from it, so a plan-only runtime is offered two postures rather than four names.
@@ -47,23 +47,23 @@ const LEDGER: Record<keyof AgentCapabilities, Backing> = {
47
47
  // turn-plan.ts tells a "cwd" turn where its worktree is, because an absolute /work path still reaches the
48
48
  // shared checkout there. Under "namespace" the mount does it and the note would be noise.
49
49
  isolation: "enforced",
50
- /* system-prompt.ts composes a turn's standing instructions AGAINST this value a replacement where one may
50
+ /* system-prompt.ts composes a turn's standing instructions AGAINST this value: a replacement where one may
51
51
  * be sent, an addition where only that is possible, and the user-message door for the persona note where
52
- * there is no system seam at all and each adapter reads the field the composition set (codex-agent.ts's
52
+ * there is no system seam at all, and each adapter reads the field the composition set (codex-agent.ts's
53
53
  * two config keys, grok-agent.ts's per-message `system`). It is also the one axis whose absence was the bug
54
54
  * that produced it: before the field existed, every runtime was composed for as though it were the Claude
55
55
  * Code loop, and five of the six silently dropped the owner's prompt. */
56
56
  instructions: "enforced",
57
57
  // turn-plan.ts (honoured) plans the JS backend only where "js" is declared, so a runtime without it is
58
- // handed no `jsExecution` and mounts no Code tool the same drop-what-you-can't-honour rule as `effort`.
58
+ // handed no `jsExecution` and mounts no Code tool: the same drop-what-you-can't-honour rule as `effort`.
59
59
  execution: "enforced",
60
60
 
61
- /* DESCRIPTIVE true of the runtime, and nothing consults them. Each describes behaviour that is emergent
61
+ /* DESCRIPTIVE: true of the runtime, and nothing consults them. Each describes behaviour that is emergent
62
62
  * rather than gated: an agent that never emits `question` frames simply never asks, one that publishes no
63
63
  * commands leaves the `/` popover empty. Nothing has to switch off, which is why nothing does. That also
64
64
  * means the declaration and the behaviour are two independent facts, and only the reader below keeps them
65
65
  * from disagreeing in the one place a user can see. Wiring one up (hiding the terminal tab, skipping
66
- * auto-resume) moves it to ENFORCED in the change that wires it. */
66
+ * auto-resume) moves it to ENFORCED: in the change that wires it. */
67
67
  questions: "descriptive",
68
68
  commands: "descriptive",
69
69
  terminals: "descriptive",
@@ -86,7 +86,7 @@ it("classifies exactly the fields a live record carries", () => {
86
86
  /* A DESCRIPTIVE field earns its place by reaching the user, since by definition it does nothing else. Checked
87
87
  * per field rather than by counting sentences: an aggregate is satisfied by one axis contributing two lines
88
88
  * while another contributes none, which is exactly the case worth catching. Anything that survives here without
89
- * a sentence is decoration, and the fix is to delete the field or wire it up not to add an exemption. */
89
+ * a sentence is decoration, and the fix is to delete the field or wire it up, not to add an exemption. */
90
90
  describe("a descriptive claim reaches the user", () => {
91
91
  it("has something to disclose against: the ceiling discloses nothing", () => {
92
92
  expect(limitationsOf(CEILING)).toEqual([]);
@@ -102,9 +102,9 @@ describe("a descriptive claim reaches the user", () => {
102
102
  });
103
103
 
104
104
  /* The ledger is a split, not a label: a set that swallowed everything would typecheck and prove nothing. Both
105
- * sides being occupied is what makes reading an entry informative and if the descriptive side ever empties
105
+ * sides being occupied is what makes reading an entry informative, and if the descriptive side ever empties
106
106
  * because every axis got wired up, that is a real event, and deleting this file is the right response to it. */
107
- it("is a real split neither side is empty", () => {
107
+ it("is a real split: neither side is empty", () => {
108
108
  expect(fieldsWhere("enforced").length).toBeGreaterThan(0);
109
109
  expect(fieldsWhere("descriptive").length).toBeGreaterThan(0);
110
110
  });
@@ -16,7 +16,7 @@ describe(`the chore book's order`, () => {
16
16
  });
17
17
 
18
18
  it(`keeps every chore in the book`, () => {
19
- // The sort cannot drop an entry but a future refactor to a filter-into-groups could, so the count is
19
+ // The sort cannot drop an entry, but a future refactor to a filter-into-groups could, so the count is
20
20
  // pinned to the ids rather than to a number, which says which one went missing.
21
21
  expect(new Set(CHORES.map((chore) => chore.id)).size).toBe(CHORES.length);
22
22
  expect(CHORES.length).toBe(17);