@intentic/sandbox-contract 1.223.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 (165) 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 +43 -43
  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/prompt.d.ts.map +1 -1
  13. package/dist/chores/prompt.js +3 -3
  14. package/dist/chores/prompt.js.map +1 -1
  15. package/dist/chores/verdict.js +2 -2
  16. package/dist/chores/verdict.js.map +1 -1
  17. package/dist/contracts/capabilities.contract.d.ts +41 -0
  18. package/dist/contracts/capabilities.contract.d.ts.map +1 -1
  19. package/dist/contracts/exit.contract.d.ts +80 -0
  20. package/dist/contracts/exit.contract.d.ts.map +1 -0
  21. package/dist/contracts/exit.contract.js +13 -0
  22. package/dist/contracts/exit.contract.js.map +1 -0
  23. package/dist/contracts/ports.contract.d.ts +11 -0
  24. package/dist/contracts/ports.contract.d.ts.map +1 -1
  25. package/dist/contracts/settings.contract.d.ts +12 -0
  26. package/dist/contracts/settings.contract.d.ts.map +1 -1
  27. package/dist/events.d.ts +4 -4
  28. package/dist/events.d.ts.map +1 -1
  29. package/dist/events.js +5 -5
  30. package/dist/events.js.map +1 -1
  31. package/dist/fast-tier.d.ts +9 -0
  32. package/dist/fast-tier.d.ts.map +1 -0
  33. package/dist/fast-tier.js +19 -0
  34. package/dist/fast-tier.js.map +1 -0
  35. package/dist/history-state.js +3 -3
  36. package/dist/history-state.js.map +1 -1
  37. package/dist/index.d.ts +214 -68
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +5 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/model-order.d.ts +1 -0
  42. package/dist/model-order.d.ts.map +1 -1
  43. package/dist/model-order.js +5 -0
  44. package/dist/model-order.js.map +1 -1
  45. package/dist/output-fields.d.ts.map +1 -1
  46. package/dist/output-fields.js +2 -2
  47. package/dist/output-fields.js.map +1 -1
  48. package/dist/prompt-complexity.d.ts +19 -0
  49. package/dist/prompt-complexity.d.ts.map +1 -0
  50. package/dist/prompt-complexity.js +83 -0
  51. package/dist/prompt-complexity.js.map +1 -0
  52. package/dist/publish-drafts.js +2 -2
  53. package/dist/publish-drafts.js.map +1 -1
  54. package/dist/schemas.d.ts +236 -0
  55. package/dist/schemas.d.ts.map +1 -1
  56. package/dist/schemas.js +116 -2
  57. package/dist/schemas.js.map +1 -1
  58. package/dist/workflow-faults.js +3 -3
  59. package/dist/workflow-faults.js.map +1 -1
  60. package/dist/workspace-state.d.ts +20 -20
  61. package/dist/workspace-state.d.ts.map +1 -1
  62. package/dist/workspace-state.js +20 -20
  63. package/dist/workspace-state.js.map +1 -1
  64. package/package.json +5 -5
  65. package/src/agent-catalog.test.ts +25 -25
  66. package/src/agent-catalog.ts +81 -81
  67. package/src/agent-run-model.test.ts +3 -3
  68. package/src/agent-run-model.ts +8 -8
  69. package/src/capability-env.ts +3 -3
  70. package/src/capability-ledger.test.ts +13 -13
  71. package/src/capability-secrets.ts +5 -5
  72. package/src/chores/chores.test.ts +1 -1
  73. package/src/chores/chores.ts +109 -109
  74. package/src/chores/digest.test.ts +1 -1
  75. package/src/chores/digest.ts +3 -3
  76. package/src/chores/extension-update.ts +5 -5
  77. package/src/chores/fix-deps.ts +4 -4
  78. package/src/chores/probes.test.ts +6 -6
  79. package/src/chores/probes.ts +17 -17
  80. package/src/chores/prompt.ts +9 -9
  81. package/src/chores/stack.test.ts +3 -3
  82. package/src/chores/stack.ts +17 -17
  83. package/src/chores/verdict.test.ts +20 -20
  84. package/src/chores/verdict.ts +25 -25
  85. package/src/contract-lock.test.ts +1 -1
  86. package/src/contract-lock.ts +7 -7
  87. package/src/contracts/activity.contract.ts +1 -1
  88. package/src/contracts/agent.contract.ts +2 -2
  89. package/src/contracts/agents.contract.ts +11 -11
  90. package/src/contracts/automations.contract.ts +5 -5
  91. package/src/contracts/capabilities.contract.ts +7 -7
  92. package/src/contracts/chores.contract.ts +3 -3
  93. package/src/contracts/ci.contract.ts +2 -2
  94. package/src/contracts/claude.contract.ts +3 -3
  95. package/src/contracts/drafts.contract.ts +1 -1
  96. package/src/contracts/endpoints.contract.ts +2 -2
  97. package/src/contracts/exit.contract.ts +42 -0
  98. package/src/contracts/extensions.contract.ts +8 -8
  99. package/src/contracts/git.contract.ts +6 -6
  100. package/src/contracts/grok.contract.ts +4 -4
  101. package/src/contracts/host.contract.ts +5 -5
  102. package/src/contracts/intentic.contract.ts +2 -2
  103. package/src/contracts/logs.contract.ts +1 -1
  104. package/src/contracts/loops.contract.ts +8 -8
  105. package/src/contracts/personas.contract.ts +8 -8
  106. package/src/contracts/ports.contract.ts +1 -1
  107. package/src/contracts/prepush.contract.ts +2 -2
  108. package/src/contracts/providers.contract.ts +4 -4
  109. package/src/contracts/public.contract.ts +1 -1
  110. package/src/contracts/push.contract.ts +2 -2
  111. package/src/contracts/secrets.contract.ts +2 -2
  112. package/src/contracts/sessions.contract.ts +2 -2
  113. package/src/contracts/settings.contract.ts +3 -3
  114. package/src/contracts/share.contract.ts +1 -1
  115. package/src/contracts/skills.contract.ts +2 -2
  116. package/src/contracts/system.contract.ts +9 -9
  117. package/src/contracts/usage.contract.ts +1 -1
  118. package/src/contracts/vpn.contract.ts +5 -5
  119. package/src/contracts/workflows.contract.ts +12 -12
  120. package/src/contracts/workspace.contract.ts +13 -13
  121. package/src/conversation-ids.ts +8 -8
  122. package/src/events.test.ts +3 -3
  123. package/src/events.ts +142 -142
  124. package/src/fast-tier.test.ts +88 -0
  125. package/src/fast-tier.ts +72 -0
  126. package/src/history-state.ts +15 -15
  127. package/src/host-protocol.ts +7 -7
  128. package/src/hostnames.test.ts +1 -1
  129. package/src/hostnames.ts +15 -15
  130. package/src/index.ts +13 -8
  131. package/src/listener-protocol.ts +12 -12
  132. package/src/model-order.test.ts +11 -11
  133. package/src/model-order.ts +52 -30
  134. package/src/output-fields.ts +11 -11
  135. package/src/path-refs.test.ts +4 -4
  136. package/src/path-refs.ts +6 -6
  137. package/src/prompt-complexity.test.ts +160 -0
  138. package/src/prompt-complexity.ts +271 -0
  139. package/src/publish-drafts.ts +8 -8
  140. package/src/quick-model.test.ts +11 -11
  141. package/src/quick-model.ts +15 -15
  142. package/src/routes.test.ts +11 -5
  143. package/src/routes.ts +19 -19
  144. package/src/runtime-state.test.ts +1 -1
  145. package/src/runtime-state.ts +22 -22
  146. package/src/schemas.test.ts +8 -8
  147. package/src/schemas.ts +1238 -921
  148. package/src/search-globs.test.ts +2 -2
  149. package/src/search-globs.ts +6 -6
  150. package/src/session-names.ts +5 -5
  151. package/src/share-paths.test.ts +1 -1
  152. package/src/share-paths.ts +7 -7
  153. package/src/sse.ts +1 -1
  154. package/src/state-portability.ts +8 -8
  155. package/src/terminal-protocol.ts +3 -3
  156. package/src/title.test.ts +9 -9
  157. package/src/title.ts +21 -21
  158. package/src/tunnel-ids.test.ts +3 -3
  159. package/src/tunnel-ids.ts +7 -7
  160. package/src/versions.test.ts +3 -3
  161. package/src/versions.ts +6 -6
  162. package/src/workflow-faults.test.ts +6 -6
  163. package/src/workflow-faults.ts +14 -14
  164. package/src/workspace-state.test.ts +32 -32
  165. package/src/workspace-state.ts +139 -139
@@ -2,31 +2,31 @@ import { STATE_DIR } from "@intentic/constants";
2
2
  import type { FileContribution } from "@intentic/extension-manifest";
3
3
  import type { StateFile } from "./state-portability.js";
4
4
 
5
- /* WHICH WORKSPACE FILE BACKS WHICH CORE VIEW one declaration, read by both sides of the wire.
5
+ /* WHICH WORKSPACE FILE BACKS WHICH CORE VIEW, one declaration, read by both sides of the wire.
6
6
  *
7
7
  * The daemon's own state lives under `<workspace>/.intentic/`, the agent edits it out-of-band with its file
8
8
  * tools, and the file watcher pushes every change as a `workspaceChanged` batch. Turning those paths back into
9
9
  * "and therefore this view is stale" used to be a hand-written table in the BROWSER (web's systemEventRouting),
10
- * maintained separately from the paths the daemon actually writes (composition.ts) two lists of the same
10
+ * maintained separately from the paths the daemon actually writes (composition.ts), two lists of the same
11
11
  * fact, in two packages, with nothing tying them together.
12
12
  *
13
13
  * They drifted, exactly as that shape always does. `.intentic/drafts/` is written by the AGENT (the drafts
14
- * skill puts a file there) and rendered by the Drafts view, but it was never added to the browser's table so
14
+ * skill puts a file there) and rendered by the Drafts view, but it was never added to the browser's table, so
15
15
  * a draft appearing on disk while the owner watched the page changed nothing until they refocused the tab.
16
16
  * Extension settings and the members list were missing for the same reason; writing them out is what showed
17
- * that neither is a drafts-shaped hole see their entries.
17
+ * that neither is a drafts-shaped hole, see their entries.
18
18
  *
19
19
  * So the binding is declared HERE, once, in the package both the daemon and the browser already import, and
20
20
  * each side derives what it needs: the daemon builds its store paths from `path`, the browser builds its
21
21
  * invalidation table from `invalidates`. Adding a manifest without saying what it makes stale is now a change
22
- * to one visible list rather than an omission in a file nobody edits and `workspace-state.test.ts` fails when
22
+ * to one visible list rather than an omission in a file nobody edits, and `workspace-state.test.ts` fails when
23
23
  * a daemon store names a `.intentic` path this list doesn't carry.
24
24
  *
25
25
  * This mirrors what routes.ts does for the route surface ("nothing is generated and nothing is hand-maintained")
26
26
  * one layer over: the same refusal to keep the same knowledge in two places.
27
27
  *
28
28
  * EXTENSIONS declare their own half in their manifest (`contributes.files`, @intentic/extension-api), in the same
29
- * two fields, and the browser unions the two lists see staleQueryKeys. That split is what this table is FOR:
29
+ * two fields, and the browser unions the two lists, see staleQueryKeys. That split is what this table is FOR:
30
30
  * before it existed the core enumeration had to carry `automations` and `automation-approvals`, query keys owned
31
31
  * by the automations extension, because the extension had no way to say so itself. A key belongs to whoever
32
32
  * queries it. */
@@ -35,56 +35,56 @@ import type { StateFile } from "./state-portability.js";
35
35
  * right to declare NO invalidations (for a daemon-owned file, the answer more often than not), and a
36
36
  * portability class, because the daemon's own state is what an environment export has to reason about.
37
37
  *
38
- * `path` is workspace-root-relative, forward-slash the space `workspaceChanged` paths arrive in. Matching is
38
+ * `path` is workspace-root-relative, forward-slash, the space `workspaceChanged` paths arrive in. Matching is
39
39
  * by PREFIX, which lets one entry cover three shapes without a second matching rule:
40
40
  * - an exact file `.intentic/config/settings.json`
41
41
  * - a directory `.intentic/config/drafts/` (one file per draft)
42
42
  * - a name family `.intentic/config/environment.custom.` (…Dockerfile and anything later named beside it)
43
- * A directory entry keeps its trailing slash so it can never prefix-match a sibling file. Entries may NEST
43
+ * A directory entry keeps its trailing slash so it can never prefix-match a sibling file. Entries may NEST,
44
44
  * see stateFileFor, which resolves the longest match rather than the first. */
45
45
  export interface WorkspaceStateFile extends StateFile {
46
- /* The browser query keys this file's contents feed. EMPTY is a real answer, not a gap a file the browser
47
- * renders nothing from, or one deliberately kept off the push path and `why` says which. Never a prefix
46
+ /* The browser query keys this file's contents feed. EMPTY is a real answer, not a gap, a file the browser
47
+ * renders nothing from, or one deliberately kept off the push path, and `why` says which. Never a prefix
48
48
  * test over `.intentic/` as a whole: one stray write must not cost every view a refetch, which is the
49
49
  * amplification that once turned an iq index rebuild into an endless request storm. */
50
50
  readonly invalidates: readonly string[];
51
51
  // Why this file has no invalidations, for the entries that declare none. Absent when it has some.
52
52
  readonly why?: string;
53
- /* Whether this entry is TRACKED by the root repo the third thing an entry declares, and the one an owner
53
+ /* Whether this entry is TRACKED by the root repo, the third thing an entry declares, and the one an owner
54
54
  * sees most directly: a tracked entry gets a diff in the Changes review and a line in `git log`, so a change
55
55
  * to how this sandbox behaves can be read, reverted, and attributed.
56
56
  *
57
57
  * ABSENT IS THE ANSWER FOR ALMOST EVERYTHING, and deliberately so. The root repo excludes `.intentic`
58
58
  * wholesale and this flag is the only thing that carves an entry back out, so a store added later is
59
- * untracked until someone says otherwise the same default-deny the `portability` classes are built on, for
59
+ * untracked until someone says otherwise, the same default-deny the `portability` classes are built on, for
60
60
  * the same reason. An ignore-pattern list would invert it: a credential store added next month would be
61
61
  * committed on its first write, and nothing would have had to change for that to happen.
62
62
  *
63
63
  * WHAT EARNS IT is one question, asked of the entry rather than of its shape: does a change here change what
64
64
  * this sandbox DOES? Two families answer yes.
65
- * - CONFIGURATION the small, slow-moving files that decide how the sandbox behaves: settings, personas,
65
+ * - CONFIGURATION, the small, slow-moving files that decide how the sandbox behaves: settings, personas,
66
66
  * skills, automations, workflow designs, the environment overlay, which extensions are on.
67
- * - AUTHORED CONTENT WHOSE CONSEQUENCES LEAVE THE SANDBOX a workspace extension is code that runs in the
67
+ * - AUTHORED CONTENT WHOSE CONSEQUENCES LEAVE THE SANDBOX, a workspace extension is code that runs in the
68
68
  * app with a declared permission surface and a backend of its own; a post draft is words that go out
69
69
  * under the owner's name. Neither is configuration, and reading `versioned` as config-only is what kept
70
70
  * both of them out: an agent could write, and then run, a whole extension with a diff nowhere, and
71
71
  * propose a public post that left no trace once declined. The rule was never "is this a setting", it was
72
- * "can this be read, reverted and attributed" and for the two things the AGENT authors on its own,
72
+ * "can this be read, reverted and attributed", and for the two things the AGENT authors on its own,
73
73
  * that matters more than it does for a file a person edited on purpose.
74
74
  *
75
75
  * Two kinds of entry stay out on purpose even though they are `carry` and hold no secret:
76
76
  * - LEDGERS (workflow runs, loop iterations, thread bookkeeping, permission-usage batches, held-wake
77
77
  * queues), which are rewritten on a timer or several times per step. Tracking them buries the owner's
78
- * code review under machine noise one of them is written every few seconds while a browser has the app
78
+ * code review under machine noise, one of them is written every few seconds while a browser has the app
79
79
  * open. A queue is a ledger too: it records that something was ASKED, and is emptied when it is answered.
80
80
  * - BULK (session transcripts, artifacts), which are hundreds of megabytes of constantly-rewritten
81
81
  * content. They travel in a bundle; they do not belong in a diff.
82
82
  * `versioned` is therefore NARROWER than `carry`, and the two answer different questions: carry is "does it
83
83
  * move to a new sandbox", this is "should a human review it changing". */
84
84
  readonly versioned?: true;
85
- /* AUTHORED but not configuration the fourth question an entry can answer, and the narrowest: is this
85
+ /* AUTHORED but not configuration, the fourth question an entry can answer, and the narrowest: is this
86
86
  * human- or agent-written TEXT that a workspace search should surface? Every `versioned` entry already is
87
- * (a setting, a persona, a skill things the agent is asked to find and edit), so this flag exists only
87
+ * (a setting, a persona, a skill, things the agent is asked to find and edit), so this flag exists only
88
88
  * for the entries that are authored content without being config: a draft awaiting approval, a staged
89
89
  * README, an extension the agent wrote in place. Two of those three are now `versioned` as well, which makes
90
90
  * the flag redundant to `SEARCHABLE_STATE_PATHS` on them and is why it stays anyway: searchability is a
@@ -94,27 +94,27 @@ export interface WorkspaceStateFile extends StateFile {
94
94
  * hand-keeping a deny list that goes stale the day a store is added (which is how a 98 kB loop ledger and
95
95
  * whole third-party extension checkouts ended up ranking in code search). */
96
96
  readonly authored?: true;
97
- /* WHETHER DESKTOP-SYNC COPIES THIS DOWN the fifth question, and the one `portability` cannot answer even
97
+ /* WHETHER DESKTOP-SYNC COPIES THIS DOWN, the fifth question, and the one `portability` cannot answer even
98
98
  * though it looks like it should.
99
99
  *
100
100
  * Portability asks whether a piece of state may RESTORE into a different sandbox. Backup asks whether it may
101
101
  * be COPIED to the owner's own machine so the loss of this sandbox is not the loss of the work. Those read as
102
102
  * the same question and are not: `members.json` is the clearest case, and its own entry argues the half that
103
- * was already written down an access list that TRAVELLED would let a source sandbox hand itself the
103
+ * was already written down, an access list that TRAVELLED would let a source sandbox hand itself the
104
104
  * target's ownership, which is why it may never be `carry`. None of that reasoning says the owner may not
105
105
  * hold a copy of who could drive their own sandbox. Conflating the two is what made the sync ignore the state
106
106
  * dir wholesale, so a sandbox that went away took every persona, skill, draft and transcript with it.
107
107
  *
108
108
  * SO IT DERIVES, and this flag exists only to carve out the entry where the derivation is wrong. The default
109
109
  * (backedUp below) is `carry` plus `identity`: ordinary state and the small records that bind this sandbox to
110
- * its owner. `derived` is excluded for size it is the caches, the checkouts and the browser profiles, all
111
- * rebuildable and all bulk and `secret` is excluded because a credential's blast radius is the number of
110
+ * its owner. `derived` is excluded for size, it is the caches, the checkouts and the browser profiles, all
111
+ * rebuildable and all bulk, and `secret` is excluded because a credential's exposure is the number of
112
112
  * places it exists, and a laptop is one more place.
113
113
  *
114
114
  * `false` is the only value: an entry either accepts the derived answer or opts out of the copy, and there is
115
115
  * no entry that needs opting IN against its class. Anything opting out says why on the entry. */
116
116
  readonly backup?: false;
117
- /* WHO BUILDS THIS TREE, when it is not the daemon an extension, pnpm, another process entirely. Declared
117
+ /* WHO BUILDS THIS TREE, when it is not the daemon, an extension, pnpm, another process entirely. Declared
118
118
  * on the entry because the coverage guard's second direction ("every declared entry is built somewhere in
119
119
  * the daemon") is only meaningful for entries the daemon owns: one it can never build must say who does, or
120
120
  * the guard would read the entry as dead. Absent for everything the daemon writes itself. */
@@ -123,16 +123,16 @@ export interface WorkspaceStateFile extends StateFile {
123
123
 
124
124
  /* Declared `as const` so the paths survive as literal types (see WorkspaceStatePath below), then published under
125
125
  * the interface. Both bindings are needed and neither is redundant: the const is the only thing that can produce
126
- * the path union, and every consumer reads entries as `WorkspaceStateFile` an exact-literal tuple loses the
126
+ * the path union, and every consumer reads entries as `WorkspaceStateFile`, an exact-literal tuple loses the
127
127
  * optional members (`note`, `why`) on the entries that omit them, which is a worse type for reading than the
128
128
  * interface it satisfies. One list, two views of it. */
129
129
  const STATE_FILES = [
130
130
  /* A capability add/remove recomposes the environment overlay and can add or drop a repo's panel.
131
131
  *
132
- * SPLIT ALREADY and this entry's classification had not caught up, which is the whole of what changed here.
132
+ * SPLIT ALREADY, and this entry's classification had not caught up, which is the whole of what changed here.
133
133
  * It read `secret` on a claim that had stopped being true: that each entry's `config` carries that
134
134
  * capability's credential, so the manifest is a secret in full. It does not. capabilities-store.ts's
135
- * withSecretVault keeps credential VALUES off /work entirely the manifest holds `__intentic_vaulted__`
135
+ * withSecretVault keeps credential VALUES off /work entirely, the manifest holds `__intentic_vaulted__`
136
136
  * where one used to be, reads rehydrate so no caller noticed, and main.ts sweeps a hand-written value out at
137
137
  * boot. What is left is the SHAPE of a connection: a kind, a URL, a username, a purpose, which permissions a
138
138
  * connected computer was granted.
@@ -140,19 +140,19 @@ const STATE_FILES = [
140
140
  * WHICH KEYS THOSE ARE IS DERIVED, not listed a second time: `echo` already answers "what of this config may
141
141
  * a browser see", and the credential keys are exactly its complement (capabilities/secret-fields.ts). A kind
142
142
  * that starts withholding a new field starts vaulting it on the same commit. That is what makes this
143
- * classification a property of the code rather than a promise to re-audit it the reason the entry can be
143
+ * classification a property of the code rather than a promise to re-audit it, the reason the entry can be
144
144
  * reclassified at all, and the reason a hand-kept "these fields are safe" list could not have earned it.
145
145
  *
146
146
  * `carry`, and this is the entry where that earns the most. composeEnvironment reads its Dockerfile fragments
147
147
  * from here, so a bundle that dropped it arrived on a stock overlay with an import report listing every
148
148
  * connection to re-add by hand. It now arrives listing them itself, each visibly unconnected and waiting for
149
- * one credential apiece the shape personas.json has had all along, for the same reason.
149
+ * one credential apiece, the shape personas.json has had all along, for the same reason.
150
150
  *
151
151
  * `versioned`, which is the point. Connecting this sandbox to a deployment orchestrator, or granting a
152
152
  * connected computer shell and screen control, is the largest change anyone makes to what it can DO, and it
153
153
  * left a diff nowhere. One consequence worth stating rather than discovering: an identifier that pairs with a
154
- * credential Komodo's api key beside its api secret, which its own connector card calls "like a database
155
- * user" is echoed, and therefore lands in the diff exactly as a database username would. */
154
+ * credential. Komodo's api key beside its api secret, which its own connector card calls "like a database
155
+ * user", is echoed, and therefore lands in the diff exactly as a database username would. */
156
156
  {
157
157
  path: ".intentic/config/capabilities.json",
158
158
  invalidates: ["capabilities", "environment", "panels", "manifests"],
@@ -163,21 +163,21 @@ const STATE_FILES = [
163
163
  /* Which workspace-derived recommendations the owner has said "not needed" to, and the evidence each was
164
164
  * declined against. It rides the `capabilities` key because the catalog is what changes when one lands, and
165
165
  * it travels because a decision about what this workspace does NOT need is as much the owner's as the
166
- * connections themselves an export that dropped it would greet them on the target with the same
166
+ * connections themselves, an export that dropped it would greet them on the target with the same
167
167
  * suggestions they had already dismissed. Holds no credential: it is a card name and a file path. */
168
168
  { path: ".intentic/config/capability-dismissals.json", invalidates: ["capabilities"], portability: "carry", versioned: true },
169
169
 
170
- /* The secret use ledger one row per moment the agent's exits spent a stored secret (a `{{secret:name}}`
170
+ /* The secret use ledger, one row per moment the agent's exits spent a stored secret (a `{{secret:name}}`
171
171
  * reference resolved into a shell command, a value typed into a browser field), joined onto the secrets
172
172
  * inventory as each entry's "last used" (sandbox's secrets/secret-uses.ts). Holds names and destinations,
173
- * never values which is why it may `carry`: like the automations' run ledger, a use history is about the
173
+ * never values, which is why it may `carry`: like the automations' run ledger, a use history is about the
174
174
  * secrets, and an export that dropped it would arrive claiming none had ever been touched. */
175
175
  { path: ".intentic/records/secret-uses.json", invalidates: ["secrets"], portability: "carry" },
176
176
 
177
- /* The wallet's payment ledger one row per payment attempt that reached policy (sandbox's
177
+ /* The wallet's payment ledger, one row per payment attempt that reached policy (sandbox's
178
178
  * wallet/wallet-ledger.ts): what was paid, to whom, how it settled, with the onchain transaction hash
179
- * when the endpoint stated one. Holds amounts, hosts and addresses, never a credential the signing key
180
- * never enters the container at all so it may `carry`: like the secret-use ledger, a spend history is
179
+ * when the endpoint stated one. Holds amounts, hosts and addresses, never a credential, the signing key
180
+ * never enters the container at all, so it may `carry`: like the secret-use ledger, a spend history is
181
181
  * about the owner's money, and an export that dropped it would arrive claiming none was ever spent. */
182
182
  {
183
183
  path: ".intentic/records/wallet-ledger.json",
@@ -186,16 +186,16 @@ const STATE_FILES = [
186
186
  portability: "carry",
187
187
  },
188
188
 
189
- /* The named personas this sandbox shows the outside world which connected accounts each one speaks for,
189
+ /* The named personas this sandbox shows the outside world, which connected accounts each one speaks for,
190
190
  * what a session wearing it may do, where it works (schemas.ts PersonaSchema). It invalidates `capabilities` as well as
191
191
  * its own key because a card and the accounts it names are read together everywhere they are shown: connect
192
192
  * a second Reddit and the persona list has a new candidate; remove one and a card points at nothing.
193
193
  *
194
- * It is `carry`, and that is the whole design rather than an oversight a card is a NAME and a list of ids,
194
+ * It is `carry`, and that is the whole design rather than an oversight, a card is a NAME and a list of ids,
195
195
  * never a credential, so it travels to a new sandbox in full while the logins it refers to stay behind. What
196
196
  * arrives is a workspace that already knows it has a work-reddit and a studio-x, both visibly unconnected,
197
197
  * each waiting for one sign-in. It was also the FIRST file under .intentic the root repo tracked, and the
198
- * argument it was carved out on a card is configuration, holds no secret, and belongs in review is the
198
+ * argument it was carved out on, a card is configuration, holds no secret, and belongs in review, is the
199
199
  * one `versioned` now generalises to the rest of the config slice (personas/personas-store.ts argues it at
200
200
  * length, and its reasoning is why the flag exists rather than a second hand-kept list). */
201
201
  { path: ".intentic/config/personas.json", invalidates: ["personas", "capabilities", "manifests"], portability: "carry", versioned: true },
@@ -206,7 +206,7 @@ const STATE_FILES = [
206
206
  * stale copy of it:
207
207
  * - custom is the owner-approved SOURCE OF TRUTH and the only one that must travel;
208
208
  * - approved is COMPOSED from custom + the capability fragments + this container's base image, and is
209
- * rewritten on the target's first boot carrying it would ship a FROM naming an image the target may
209
+ * rewritten on the target's first boot, carrying it would ship a FROM naming an image the target may
210
210
  * not be on (see composeEnvironment's baseImageOf);
211
211
  * - the proposal and the per-tool drafts under environment.d/ are the agent's pending requests, which the
212
212
  * owner has not answered yet; they travel so the question survives the move. */
@@ -230,19 +230,19 @@ const STATE_FILES = [
230
230
  portability: "derived",
231
231
  note: "Stamps of when each rule last did something; the new sandbox starts its own record.",
232
232
  },
233
- /* Written by the AGENT's file tools (the drafts skill), read by the owner's approval inbox the one entry
233
+ /* Written by the AGENT's file tools (the drafts skill), read by the owner's approval inbox, the one entry
234
234
  * here whose whole point is that a change arrives from outside the browser that renders it. `authored`:
235
235
  * a draft is text somebody wrote, and "find the reddit draft about X" is an ordinary search.
236
236
  *
237
237
  * `versioned` because a draft is the furthest-reaching thing the agent writes: these words go out under the
238
- * owner's name, to an audience, and cannot be recalled. The approval inbox already gates that but a gate is
238
+ * owner's name, to an audience, and cannot be recalled. The approval inbox already gates that, but a gate is
239
239
  * not a record. Declining one used to erase it, so the question "what has this agent tried to post" had no
240
240
  * answer at all, and an approved post's own history (what was proposed, what the owner changed, when it
241
241
  * actually went) lived only in a file nobody could diff.
242
242
  *
243
243
  * It costs almost nothing to track, which is why the ledger objection does not reach it: a draft is one small
244
244
  * file per post, it is written a handful of times across its whole life (proposed → approved → posted, with
245
- * `postedAt`/`postedUrl` stamped at the end), and it is KEPT afterwards rather than consumed so tracking
245
+ * `postedAt`/`postedUrl` stamped at the end), and it is KEPT afterwards rather than consumed, so tracking
246
246
  * yields a durable record instead of the add/delete churn a queue would produce. Nothing in a draft is a
247
247
  * credential: it is a platform, a target URL and the body itself, all of it bound for publication anyway. */
248
248
  { path: ".intentic/config/drafts/", invalidates: ["drafts"], portability: "carry", versioned: true, authored: true },
@@ -253,69 +253,69 @@ const STATE_FILES = [
253
253
  {
254
254
  path: ".intentic/config/automations.json",
255
255
  invalidates: [],
256
- why: "Declared by the intentic.automations extension's contributes.files `automations` is its query key, not core's.",
256
+ why: "Declared by the intentic.automations extension's contributes.files, `automations` is its query key, not core's.",
257
257
  portability: "carry",
258
258
  versioned: true,
259
259
  },
260
- /* The run history, keyed by automation id the LEDGER half of what automations.json used to be, and split
260
+ /* The run history, keyed by automation id, the LEDGER half of what automations.json used to be, and split
261
261
  * out of it for the one reason this table's `versioned` note already gives: a tracked file must be worth
262
262
  * reviewing. A scheduled automation records a run every time it fires, so every fire dirtied the manifest
263
- * the owner reviews, and the run records went into `git log` with it timestamps and conversation ids
263
+ * the owner reviews, and the run records went into `git log` with it, timestamps and conversation ids
264
264
  * committed beside the prompt they belong to, burying an actual edit to the automation's config under
265
265
  * machine noise. Config is now the only thing in the tracked file, and a fire touches nothing tracked.
266
266
  *
267
267
  * It is `carry` for the same reason the workflow ledger is: a run history is about the automation, not about
268
268
  * the machine, and an export that dropped it would arrive claiming every automation had never run.
269
269
  *
270
- * Its invalidation is the extension's, exactly like the manifest above and it has to be DECLARED there
270
+ * Its invalidation is the extension's, exactly like the manifest above, and it has to be DECLARED there
271
271
  * rather than inherited, because the row renders its run history from this file now: without its own entry
272
272
  * a completed run would stop refreshing the view the moment it stopped living in automations.json. */
273
273
  {
274
274
  path: ".intentic/records/automation-runs.json",
275
275
  invalidates: [],
276
- why: "Declared by the intentic.automations extension's contributes.files `automations` is its query key, not core's.",
276
+ why: "Declared by the intentic.automations extension's contributes.files, `automations` is its query key, not core's.",
277
277
  portability: "carry",
278
278
  },
279
279
  {
280
280
  path: ".intentic/records/approvals/",
281
281
  invalidates: [],
282
- why: "Declared by the intentic.automations extension's contributes.files `automation-approvals` is its query key, not core's.",
282
+ why: "Declared by the intentic.automations extension's contributes.files, `automation-approvals` is its query key, not core's.",
283
283
  portability: "carry",
284
284
  },
285
285
  /* The maintenance ledger and probe evidence, written by the daemon's chores-store and rendered by the
286
- * intentic.maintenance extension the automations shape exactly: the path is the daemon's, the query keys
286
+ * intentic.maintenance extension, the automations shape exactly: the path is the daemon's, the query keys
287
287
  * (`maintenance-report`, `maintenance-runs`) are the extension's own contributes.files. Point-in-time
288
288
  * evidence about this workspace, so `carry` like the run ledgers: an export that dropped it would arrive
289
289
  * claiming no chore had ever been checked. */
290
290
  {
291
291
  path: ".intentic/records/chores/",
292
292
  invalidates: [],
293
- why: "Declared by the intentic.maintenance extension's contributes.files `maintenance-report`/`maintenance-runs` are its query keys, not core's.",
293
+ why: "Declared by the intentic.maintenance extension's contributes.files, `maintenance-report`/`maintenance-runs` are its query keys, not core's.",
294
294
  portability: "carry",
295
295
  },
296
296
  /* The documentation STAGING tree (documentation extension's paths.ts): generation writes here, the owner
297
- * reads and approves here, publishing copies into the repo. `authored` is the whole nature of the entry
297
+ * reads and approves here, publishing copies into the repo. `authored` is the whole nature of the entry,
298
298
  * these are draft READMEs, drafts-shaped in every way that matters, and "find the staged page about X" is
299
299
  * as ordinary a search as finding a post draft. */
300
300
  {
301
301
  path: ".intentic/config/docs/",
302
302
  invalidates: [],
303
- why: "Declared by the intentic.documentation extension's contributes.files `documentation`/`documentation-runs` are its query keys, not core's.",
303
+ why: "Declared by the intentic.documentation extension's contributes.files, `documentation`/`documentation-runs` are its query keys, not core's.",
304
304
  portability: "carry",
305
305
  authored: true,
306
306
  outsideWriter: "the intentic.documentation extension's staging writes (its paths.ts)",
307
307
  },
308
308
  /* The workflow designs and their run ledger became CORE keys the day runs got cards on the fleet board and
309
309
  * a mode of the chat panel (web's useWorkflowRuns): those surfaces exist whether or not the workflows
310
- * extension is enabled, so their freshness cannot ride an extension's contributes.files an owner turning
310
+ * extension is enabled, so their freshness cannot ride an extension's contributes.files, an owner turning
311
311
  * the extension off would have frozen the board's run cards mid-run. This push is the ONLY live feed the
312
312
  * run surfaces have: the scheduler writes the ledger several times per step and nothing polls for it.
313
313
  * The runs file invalidates `workflows` too, because GET /workflows embeds each design's runs
314
- * (WorkflowSummary) a settled step changes that answer as surely as an edited design does. */
314
+ * (WorkflowSummary), a settled step changes that answer as surely as an edited design does. */
315
315
  { path: ".intentic/config/workflows.json", invalidates: ["workflows"], portability: "carry", versioned: true },
316
316
  { path: ".intentic/records/workflow-runs.json", invalidates: ["workflows", "workflow-runs"], portability: "carry" },
317
317
  /* The SAVED loops, which are a manifest and so the opposite of the ledger below them: a handful of entries
318
- * a person authors, read by two surfaces at once the workflows page that owns them, and every chat
318
+ * a person authors, read by two surfaces at once, the workflows page that owns them, and every chat
319
319
  * composer's loop picker. Those two are in different windows as often as not (a popped-out chat is its own
320
320
  * window), so an edit made on the page has to reach a picker nobody is going to think to reopen. A CORE key
321
321
  * rather than the workflows extension's, for the reason the workflow designs beside it are: the composer
@@ -324,14 +324,14 @@ const STATE_FILES = [
324
324
  {
325
325
  path: ".intentic/records/loops.json",
326
326
  invalidates: [],
327
- why: "Ralph loops and their iteration history. Nothing observes it: where a RUNNING loop stands rides on the fleet roster (AgentSummary.loop), which the /events stream already pushes about once a second, and a second source invalidating on this file could only ever disagree with the card beside it. The iteration list of an ENDED loop is an on-demand read nothing renders it until someone opens it (web's useLoops, which holds no query for exactly this reason).",
327
+ why: "Ralph loops and their iteration history. Nothing observes it: where a RUNNING loop stands rides on the fleet roster (AgentSummary.loop), which the /events stream already pushes about once a second, and a second source invalidating on this file could only ever disagree with the card beside it. The iteration list of an ENDED loop is an on-demand read, nothing renders it until someone opens it (web's useLoops, which holds no query for exactly this reason).",
328
328
  portability: "carry",
329
329
  },
330
330
 
331
331
  /* ---- reached by no query, for reasons that are not oversights ----
332
332
  *
333
333
  * This channel's currency is a QUERY KEY, and invalidation only reaches a query something is observing.
334
- * Both entries below are outside that by design, so an empty set is the honest record naming a key no
334
+ * Both entries below are outside that by design, so an empty set is the honest record, naming a key no
335
335
  * query uses would put the drift this table exists to remove straight back into it. Each says which
336
336
  * constraint would have to move first, so the next reader doesn't re-derive it. */
337
337
  {
@@ -343,39 +343,39 @@ const STATE_FILES = [
343
343
  {
344
344
  path: ".intentic/records/thread-sessions.json",
345
345
  invalidates: [],
346
- why: "Thread bookkeeping (an inbound thread a Front Desk visitor, a Discord or Slack channel → sandbox conversation + provider session), written on EVERY inbound message. Nothing in the browser reads it: what a thread produces is a conversation, and the fleet board already learns about that from the agent registry's own push. Naming a key here would bill every connected browser a refetch per inbound message the request storm this table's own note warns about to refresh nothing it can see.",
346
+ why: "Thread bookkeeping (an inbound thread, a Front Desk visitor, a Discord or Slack channel, → sandbox conversation + provider session), written on EVERY inbound message. Nothing in the browser reads it: what a thread produces is a conversation, and the fleet board already learns about that from the agent registry's own push. Naming a key here would bill every connected browser a refetch per inbound message, the request storm this table's own note warns about, to refresh nothing it can see.",
347
347
  portability: "carry",
348
348
  },
349
349
  /* SPLIT, so that "what an extension is configured to do" and "the token it does it with" stop being one file.
350
350
  *
351
351
  * This entry used to be `secret` and untracked, classed by what a value COULD hold: values are a primitive
352
352
  * union an extension chooses the meaning of, and "an API key for the service I talk to" is squarely within
353
- * it. That classification was honest about the risk and wrong about the file it meant an extension's whole
353
+ * it. That classification was honest about the risk and wrong about the file, it meant an extension's whole
354
354
  * configuration was unreviewable because one of its keys might be a credential, AND the credential was in
355
355
  * there anyway, in a file the workspace API does not lock. A turn could simply read it.
356
356
  *
357
357
  * A descriptor already says which keys those are (`contributes.settings[].secret`), so the values it names
358
- * now live in the vault off /work and this file keeps the rest the capability manifest's split, applied to
358
+ * now live in the vault off /work and this file keeps the rest, the capability manifest's split, applied to
359
359
  * the same problem one table over (extensions/extension-settings.ts holds it, and the reasoning). Reads
360
360
  * rehydrate, so no caller changed.
361
361
  *
362
362
  * What the split earns: `carry`, because what is left is an extension's configuration and a bundle should
363
363
  * arrive with it; and `versioned`, because turning an extension's behaviour on is a decision, and the file
364
364
  * that records it can now be read without reading anybody's token. The boot sweep is what keeps that true of
365
- * a file the agent can also edit see vaultExtensionSettingSecrets.
365
+ * a file the agent can also edit, see vaultExtensionSettingSecrets.
366
366
  *
367
367
  * NO `note`, and the split is why: a note is printed by the import report beside a SKIPPED entry, so an entry
368
368
  * that carries can never show one. "Re-enter the credentials" is now the vault's instruction to give, and the
369
- * vault is under `.intentic/auth/` which is skipped, and says so there. */
369
+ * vault is under `.intentic/auth/`, which is skipped, and says so there. */
370
370
  {
371
371
  path: ".intentic/config/extension-settings.json",
372
372
  invalidates: [],
373
- why: "Held in a module-level shallowRef store per extension (web's extensionSettingsStore) with no query observer, and deliberately so: api.settings.get must answer SYNCHRONOUSLY from an extension's first activate() line, and the store outlives every component scope. A module-level QueryObserver is the one shape that would make invalidation refetch, and this app already ruled it out it detaches on the queryClient.clear() at logout (see useSandbox's sandbox-list mirror). So a remote member's setting edit reaches this browser on its next load, not live.",
373
+ why: "Held in a module-level shallowRef store per extension (web's extensionSettingsStore) with no query observer, and deliberately so: api.settings.get must answer SYNCHRONOUSLY from an extension's first activate() line, and the store outlives every component scope. A module-level QueryObserver is the one shape that would make invalidation refetch, and this app already ruled it out, it detaches on the queryClient.clear() at logout (see useSandbox's sandbox-list mirror). So a remote member's setting edit reaches this browser on its next load, not live.",
374
374
  portability: "carry",
375
375
  versioned: true,
376
376
  },
377
- /* Unlike the settings file above it, the on/off switch IS observed by a query the Extensions tab's list,
378
- * which carries each row's switch position so a flip made elsewhere (another member, the agent writing the
377
+ /* Unlike the settings file above it, the on/off switch IS observed by a query, the Extensions tab's list,
378
+ * which carries each row's switch position, so a flip made elsewhere (another member, the agent writing the
379
379
  * file) shows up here live. It does not re-run the host: activating or retiring an extension is the loader's
380
380
  * reconcile, which the tab's own toggle triggers, so a remote flip takes effect on this browser's next load. */
381
381
  {
@@ -384,16 +384,16 @@ const STATE_FILES = [
384
384
  portability: "carry",
385
385
  versioned: true,
386
386
  },
387
- /* Workspace extensions: one directory per extension, consumed straight from the workspace no clone, no
387
+ /* Workspace extensions: one directory per extension, consumed straight from the workspace, no clone, no
388
388
  * install moment. Written like drafts, by the agent's own file tools (which is the point: an agent authors
389
389
  * an extension and it is live for the daemon and every session at once, since .intentic is shared), so this
390
390
  * push is what makes one appearing or changing show up on the Extensions tab while the owner watches.
391
- * `authored` for the same reason as drafts: this is source the agent wrote and will be asked to find
391
+ * `authored` for the same reason as drafts: this is source the agent wrote and will be asked to find,
392
392
  * unlike `.intentic/extensions/` below, which is CLONES of source that lives elsewhere.
393
393
  *
394
394
  * `versioned` FOR THAT SAME REASON, which is the whole argument. Every other load path an extension can take
395
395
  * is already reviewable by construction: a git-installed one is a sha in `capabilities.json` that an owner
396
- * approved, a baked one shipped in the image. This one is neither it is code that appears because an agent
396
+ * approved, a baked one shipped in the image. This one is neither, it is code that appears because an agent
397
397
  * wrote a file, runs in the app on the owner's session, may register a rail tile, and may serve HTTP from a
398
398
  * node process with the workspace under `node:fs` and whatever `permissions.daemon` names. Untracked, the
399
399
  * switch that turns it on was in `git log` (extension-enablement.json, below) while the thing being switched
@@ -401,11 +401,11 @@ const STATE_FILES = [
401
401
  * kind with no install moment to review at, so the diff is the only review there is.
402
402
  *
403
403
  * Not a ledger and not bulk: a handful of small authored files per extension, written when someone edits them.
404
- * The daemon restarts the backend host on a change here, so an edit is already a consequential event this
404
+ * The daemon restarts the backend host on a change here, so an edit is already a consequential event, this
405
405
  * makes it a legible one. */
406
406
  { path: ".intentic/config/workspace-extensions/", invalidates: ["extensions"], portability: "carry", versioned: true, authored: true },
407
407
  /* What the registry comparison found per installed extension (update available / advisory / post-update
408
- * health), written by the periodic check and by the update/revert transactions pushed to the tab because
408
+ * health), written by the periodic check and by the update/revert transactions, pushed to the tab because
409
409
  * an advisory that auto-disabled something must not wait for a reload to be seen. */
410
410
  { path: ".intentic/records/extension-updates.json", invalidates: ["extensions"], portability: "carry" },
411
411
  /* The owner's per-extension update posture (notify / agent / auto, and the advisory opt-out). Carried:
@@ -416,26 +416,26 @@ const STATE_FILES = [
416
416
  {
417
417
  path: ".intentic/records/extension-usage.json",
418
418
  invalidates: [],
419
- why: "Which of the routes each extension DECLARED it has actually called the evidence behind the permissions list on its row. The one entry here whose empty set is a RATE decision rather than an architectural one: every browser with the app open reports its batch on a timer, so wiring this to the `extensions` query would refetch the whole list every few seconds for a figure nobody is watching change. The tab reads it when it loads, which is when anyone is reading it.",
419
+ why: "Which of the routes each extension DECLARED it has actually called, the evidence behind the permissions list on its row. The one entry here whose empty set is a RATE decision rather than an architectural one: every browser with the app open reports its batch on a timer, so wiring this to the `extensions` query would refetch the whole list every few seconds for a figure nobody is watching change. The tab reads it when it loads, which is when anyone is reading it.",
420
420
  portability: "carry",
421
421
  },
422
422
  /* THE ONE ENTRY WHERE "HOLDS NO CREDENTIAL" IS TRUE AND `versioned` IS STILL WRONG, which is worth stating
423
423
  * because it looks like the two above it: an email and a role per row, nothing to vault, and "who may drive
424
424
  * this sandbox" is as consequential a fact as any this table tracks.
425
425
  *
426
- * It stays out for two reasons that are not about secrecy. It is a MIRROR the platform's invite records are
426
+ * It stays out for two reasons that are not about secrecy. It is a MIRROR, the platform's invite records are
427
427
  * the grant, this is the copy the enforcer keeps so a grant it never received is never honoured, and a change
428
428
  * here is the two disagreeing rather than anyone deciding something. Review of the decision already exists,
429
429
  * on the Access tab, against the record that is authoritative. And tracking it would mean reclassifying it
430
430
  * `carry` to satisfy the guard, which is the one thing it must never be: an access list that travelled would
431
431
  * let a source sandbox hand itself the target's ownership. Widening the guard for this single entry is the
432
- * worse trade it protects every `identity` entry, and most of those ARE credentials. */
432
+ * worse trade, it protects every `identity` entry, and most of those ARE credentials. */
433
433
  {
434
434
  path: ".intentic/identity/members.json",
435
435
  invalidates: [],
436
- why: "Not this view's source at all: SandboxAccess renders the PLATFORM's invite records (apiClient.invite.list), and this file is the daemon's ENFORCED copy written first so a grant the enforcer never got is never recorded, then never read back. A change here means the two disagreed, which the write order makes fail-closed rather than stale.",
436
+ why: "Not this view's source at all: SandboxAccess renders the PLATFORM's invite records (apiClient.invite.list), and this file is the daemon's ENFORCED copy, written first so a grant the enforcer never got is never recorded, then never read back. A change here means the two disagreed, which the write order makes fail-closed rather than stale.",
437
437
  portability: "identity",
438
- note: "Re-invite collaborators from the Access tab a grant is the platform's record, and the target enforces its own copy.",
438
+ note: "Re-invite collaborators from the Access tab, a grant is the platform's record, and the target enforces its own copy.",
439
439
  },
440
440
 
441
441
  // ---- daemon-owned, nothing derives from watching them ----
@@ -444,7 +444,7 @@ const STATE_FILES = [
444
444
  * and no generic export can safely distinguish those files. The broad root also makes a newly-added provider
445
445
  * secret by construction instead of relying on another hand-maintained provider-name list.
446
446
  *
447
- * IT IS NO LONGER ONLY THE AI LOGINS. Both credential splits put their vault here `capability-secrets.json`
447
+ * IT IS NO LONGER ONLY THE AI LOGINS. Both credential splits put their vault here, `capability-secrets.json`
448
448
  * and `extension-secrets.json`, sited beside the provider homes precisely because this tree is already
449
449
  * outside the file routes, the workspace walk and the search index (composition.ts sites them, and the two
450
450
  * stores argue why). So this is now the ONE entry a secret-less bundle leaves behind, and its note is
@@ -455,7 +455,7 @@ const STATE_FILES = [
455
455
  invalidates: [],
456
456
  why: "AI-provider credentials and runtime homes, plus the capability and extension-settings secret vaults; each account is rendered through owner-gated provider routes.",
457
457
  portability: "secret",
458
- note: "Sign the agent's AI accounts in again on the Agent tab, then re-enter each connection's credential on Capabilities and each extension's secret settings on Extensions both arrived listed but unauthenticated.",
458
+ note: "Sign the agent's AI accounts in again on the Agent tab, then re-enter each connection's credential on Capabilities and each extension's secret settings on Extensions, both arrived listed but unauthenticated.",
459
459
  },
460
460
  /* Agent session transcripts, rewritten on every streamed token.
461
461
  *
@@ -470,7 +470,7 @@ const STATE_FILES = [
470
470
  {
471
471
  path: ".intentic/records/sessions/claude/",
472
472
  invalidates: [],
473
- why: "Agent session transcripts see the note above on why the memory notes under it stay polled.",
473
+ why: "Agent session transcripts, see the note above on why the memory notes under it stay polled.",
474
474
  portability: "carry",
475
475
  },
476
476
  {
@@ -482,11 +482,11 @@ const STATE_FILES = [
482
482
  {
483
483
  path: ".intentic/local/cache/",
484
484
  invalidates: [],
485
- why: "Rebuildable indexes and caches the iq index and its vector sidecar, the whisper model; ignored by the watcher and recreated from carried workspace content.",
485
+ why: "Rebuildable indexes and caches, the iq index and its vector sidecar, the whisper model; ignored by the watcher and recreated from carried workspace content.",
486
486
  portability: "derived",
487
487
  },
488
488
  /* Connector and extension scratch, one directory per extension under runtime/extensions/<id>
489
- * (extensionRuntimeDir below the ONLY way an extension names a home here, so a new one lands under its
489
+ * (extensionRuntimeDir below, the ONLY way an extension names a home here, so a new one lands under its
490
490
  * own id by construction instead of minting a file at the .intentic root). Resume watermarks, cached
491
491
  * hour-tokens, gateway discovery state: all of it either expires or re-establishes itself, and classifying
492
492
  * the root once is what keeps a token an extension caches tomorrow out of bundles without a second edit. */
@@ -504,7 +504,7 @@ const STATE_FILES = [
504
504
  portability: "derived",
505
505
  },
506
506
  /* Not written by the daemon at all: pnpm auto-creates its content-addressable store at the project's
507
- * mountpoint, and `.intentic` is its own mount in an isolated turn so an install run from under it mints
507
+ * mountpoint, and `.intentic` is its own mount in an isolated turn, so an install run from under it mints
508
508
  * this. Declared anyway, because the table's job is to say what everything under `.intentic` IS: hardlink
509
509
  * sources a fresh install rebuilds, which an export must not ship (it reached 1.3 GB on the workspace this
510
510
  * entry was written against). */
@@ -518,14 +518,14 @@ const STATE_FILES = [
518
518
  {
519
519
  path: ".intentic/local/newest-run.json",
520
520
  invalidates: [],
521
- why: "The newest daemon version that ever ran this workspace (store/newest-run.ts) a downgrade tripwire, about THIS sandbox the way rule-firings is.",
521
+ why: "The newest daemon version that ever ran this workspace (store/newest-run.ts), a downgrade tripwire, about THIS sandbox the way rule-firings is.",
522
522
  portability: "derived",
523
523
  note: "The target stamps its own daemon version on first boot.",
524
524
  },
525
525
  {
526
526
  path: ".intentic/records/verify.json",
527
527
  invalidates: [],
528
- why: "The dependency verifier's verdict memory; nothing renders it directly outcomes reach the owner as activity entries and workspace events.",
528
+ why: "The dependency verifier's verdict memory; nothing renders it directly, outcomes reach the owner as activity entries and workspace events.",
529
529
  portability: "carry",
530
530
  },
531
531
  {
@@ -539,14 +539,14 @@ const STATE_FILES = [
539
539
  invalidates: [],
540
540
  why: "Webhook secret + conclusion memory; the Pipelines view reads it through /ci/runs, not off disk.",
541
541
  portability: "secret",
542
- note: "Re-add the CI webhook on the Pipelines view its secret is per-sandbox.",
542
+ note: "Re-add the CI webhook on the Pipelines view, its secret is per-sandbox.",
543
543
  },
544
544
  /* THE ONE ENTRY THAT OPTS OUT OF THE BACKUP, and the reason the flag exists rather than the rule simply
545
545
  * reading `portability !== "derived"`. It is `identity` like the three below it, so the derived answer would
546
- * copy it down with them but where those are a name, a workspace id and a role per row, these are tokens
546
+ * copy it down with them, but where those are a name, a workspace id and a role per row, these are tokens
547
547
  * that AUTHENTICATE against this sandbox from outside it. Hashed, which lowers the stakes and does not
548
548
  * settle them: the point of a backup is to be readable after the thing it backs up is gone, and a file whose
549
- * only purpose is to admit callers has no business sitting in one. Nothing is lost by leaving it out the
549
+ * only purpose is to admit callers has no business sitting in one. Nothing is lost by leaving it out, the
550
550
  * entry's own note already says the tokens must be re-minted on any new sandbox, so a copy could never have
551
551
  * been restored anyway. */
552
552
  {
@@ -555,7 +555,7 @@ const STATE_FILES = [
555
555
  why: "Hashed control tokens (the ACP editor bridge, and anything else driving this sandbox from outside), listed on demand by the owner.",
556
556
  portability: "identity",
557
557
  backup: false,
558
- note: "Mint fresh control tokens the old ones authenticate against the source sandbox.",
558
+ note: "Mint fresh control tokens, the old ones authenticate against the source sandbox.",
559
559
  },
560
560
  {
561
561
  path: ".intentic/identity/owner.json",
@@ -585,71 +585,71 @@ const STATE_FILES = [
585
585
  invalidates: [],
586
586
  why: "Browser-login profiles: Chromium rewrites these constantly. Descent-ignored by the watcher outright.",
587
587
  portability: "derived",
588
- note: "Log the agent's browser back into any site it needs profiles do not travel.",
588
+ note: "Log the agent's browser back into any site it needs, profiles do not travel.",
589
589
  },
590
590
  {
591
591
  path: ".intentic/local/extensions/",
592
592
  invalidates: [],
593
- why: "Extension checkouts whole git clones. The `extensions` query is driven by the capability manifest above, not by their contents.",
593
+ why: "Extension checkouts, whole git clones. The `extensions` query is driven by the capability manifest above, not by their contents.",
594
594
  portability: "derived",
595
595
  note: "Extensions re-clone from the capability manifest on the target's next reconcile.",
596
596
  },
597
597
  { path: ".intentic/records/plugins/", invalidates: [], why: "Agent plugin dirs, read by the SDK's loader each turn.", portability: "carry" },
598
- /* THE SKILLS THE OWNER WROTE THEMSELVES, one directory per skill the source of truth the reconciler copies
598
+ /* THE SKILLS THE OWNER WROTE THEMSELVES, one directory per skill, the source of truth the reconciler copies
599
599
  * into `.agents/skills` for the ones currently switched on (settings.json's `skills` list). It is here rather
600
600
  * than in the loaded folder for the reason the plugin dirs are: that tree holds only what is currently ON,
601
601
  * and a skill switched off has to keep its text somewhere the loaders will not read it from.
602
602
  *
603
603
  * `versioned`, like the rest of the config slice: a skill changes how the agent behaves, so it earns a diff
604
604
  * in the Changes review and a line in `git log` the same way a rule or a persona does. `carry` for the same
605
- * reason it is text the owner wrote, with no credential in it and nothing about this machine. */
605
+ * reason, it is text the owner wrote, with no credential in it and nothing about this machine. */
606
606
  { path: ".intentic/config/skills/", invalidates: ["skills"], portability: "carry", versioned: true },
607
- /* ONE FOLDER PER PERSONA what a session wearing that card is told, and the skills and tools only it gets.
607
+ /* ONE FOLDER PER PERSONA, what a session wearing that card is told, and the skills and tools only it gets.
608
608
  * Laid out as a Claude Code plugin (`.claude-plugin/plugin.json`, `skills/`, `agents/`, `commands/`,
609
609
  * `hooks/`, `.mcp.json`) so the runtime's own loader reads it and this daemon parses none of it, exactly as
610
610
  * the plugin checkouts above are read (personas/persona-kit.ts).
611
611
  *
612
612
  * A SECOND ENTRY BESIDE `personas.json` RATHER THAN A FIELD INSIDE IT, because the two are different kinds
613
- * of thing to review. The card is a name, some ids and some switches a few lines that diff cleanly. This
613
+ * of thing to review. The card is a name, some ids and some switches, a few lines that diff cleanly. This
614
614
  * is prose and files: a system prompt, a skill, a subagent. Folding a 20k prompt into the JSON would make
615
615
  * every persona edit an unreadable diff and put text somebody wrote inside a record nobody writes by hand.
616
616
  *
617
617
  * `versioned` and `carry` for the same reasons the card and the skills above are: it changes how the agent
618
- * behaves, it holds no credential, and it belongs in a pull request which is also what makes it
618
+ * behaves, it holds no credential, and it belongs in a pull request, which is also what makes it
619
619
  * searchable, since every versioned entry already is. */
620
620
  { path: ".intentic/config/personas/", invalidates: ["personas"], portability: "carry", versioned: true },
621
621
  ] as const satisfies readonly WorkspaceStateFile[];
622
622
 
623
623
  export const WORKSPACE_STATE_FILES: readonly WorkspaceStateFile[] = STATE_FILES;
624
624
 
625
- /* The entries the root repo tracks, workspace-root-relative and in declaration order what history.ts turns
625
+ /* The entries the root repo tracks, workspace-root-relative and in declaration order, what history.ts turns
626
626
  * into the negations that carve them back out of the wholesale `.intentic` exclusion.
627
627
  *
628
628
  * Derived rather than written down beside the exclude rule, for the reason this whole file exists: the git rule
629
629
  * lives in the daemon and the classification lives here, and a second hand-kept copy of "which config is
630
630
  * reviewable" is a copy that goes stale the first time someone adds a store. Marking an entry `versioned` is now
631
- * the entire change the exclude list follows on the next boot, in both places it is written. */
631
+ * the entire change, the exclude list follows on the next boot, in both places it is written. */
632
632
  export const VERSIONED_STATE_PATHS: readonly string[] = WORKSPACE_STATE_FILES.filter((file) => file.versioned).map((file) => file.path);
633
633
 
634
634
  /* The `.intentic` slice a workspace SEARCH may surface: configuration a person reviews (`versioned`) plus the
635
- * authored-content dirs (`authored`) drafts, staged docs, workspace extensions. Everything else under
635
+ * authored-content dirs (`authored`), drafts, staged docs, workspace extensions. Everything else under
636
636
  * `.intentic` is machine state, and the search engine (iq's floor) denies it BY DEFAULT off this list, the
637
637
  * same default-deny the portability classes are built on and for the same reason: a deny list is a list a new
638
- * ledger is forgotten from, and the forgetting is silent it ranked loop iteration history and cloned
638
+ * ledger is forgotten from, and the forgetting is silent, it ranked loop iteration history and cloned
639
639
  * third-party extension source against the user's own code for months before this derivation existed.
640
640
  *
641
641
  * WHAT THE CREDENTIAL SPLITS MOVED ACROSS THIS LINE, since the note that used to sit here said the opposite and
642
642
  * was worth replacing rather than deleting. `capabilities.json` was `secret` and unversioned, and the sentence
643
643
  * celebrated that the index therefore stopped copying capability tokens into search text. It is `versioned` now
644
- * and searchable and the guarantee is unchanged, because the tokens are not in the file any more. The floor
644
+ * and searchable, and the guarantee is unchanged, because the tokens are not in the file any more. The floor
645
645
  * moved from "keep the index away from the file that holds credentials" to "the file holds none", which is the
646
- * stronger of the two: it also holds for the shell, which never consulted this list at all. `auth/` where those
647
- * values went, both vaults included is still denied, and is the entry that was doing the real work all along. */
646
+ * stronger of the two: it also holds for the shell, which never consulted this list at all. `auth/`, where those
647
+ * values went, both vaults included, is still denied, and is the entry that was doing the real work all along. */
648
648
  export const SEARCHABLE_STATE_PATHS: readonly string[] = WORKSPACE_STATE_FILES.filter((file) => file.versioned || file.authored).map(
649
649
  (file) => file.path,
650
650
  );
651
651
 
652
- /* WHAT KIND OF THING THIS IS, in the one word a person browsing the state dir would use and the axis the
652
+ /* WHAT KIND OF THING THIS IS, in the one word a person browsing the state dir would use, and the axis the
653
653
  * DIRECTORY LAYOUT is built on, so the folder you open explains itself before you read a table about it.
654
654
  *
655
655
  * It is DERIVED, and that is the whole reason it can be trusted. Forty-nine entries already answer three
@@ -657,7 +657,7 @@ export const SEARCHABLE_STATE_PATHS: readonly string[] = WORKSPACE_STATE_FILES.f
657
657
  * perfectly rather than cut across each other: every `versioned` entry is `carry`, every `authored` entry is
658
658
  * `carry`, and nothing is both a credential and a thing a person edits. A nested set of answers is exactly what
659
659
  * a directory tree can express, which is why five folders can carry rules that used to take five hand-kept path
660
- * lists the git exclude, the search allow-list, the sync ignore, the watcher skip, the export bundle.
660
+ * lists, the git exclude, the search allow-list, the sync ignore, the watcher skip, the export bundle.
661
661
  *
662
662
  * Declaring the group on each entry instead would have made it a fourth independent fact to keep in step with
663
663
  * the other three, which is the failure this file exists to argue against. Adding a store still means answering
@@ -666,25 +666,25 @@ export const SEARCHABLE_STATE_PATHS: readonly string[] = WORKSPACE_STATE_FILES.f
666
666
  export type StateGroup =
667
667
  /* Reviewed and reviewable: settings, personas, skills, drafts, staged docs, the environment overlay. Tracked
668
668
  * by the root repo, searchable, backed up, and carried into a new sandbox. Two of its members (drafts, staged
669
- * docs) are authored content rather than configuration, and the folder is still called `config` the word
669
+ * docs) are authored content rather than configuration, and the folder is still called `config`, the word
670
670
  * that makes seventeen of the nineteen instantly clear beats one that makes all nineteen vague. */
671
671
  | "config"
672
- /* What HAPPENED here run ledgers, approvals, chores, transcripts, artifacts. Machine-written, so untracked
672
+ /* What HAPPENED here, run ledgers, approvals, chores, transcripts, artifacts. Machine-written, so untracked
673
673
  * and unsearchable, but the owner's history all the same: backed up and carried. */
674
674
  | "records"
675
675
  /* Rebuildable from something that does travel: caches, indexes, extension checkouts, scratch, the composed
676
676
  * overlay, browser profiles. Neither backed up nor carried, and the janitor may delete it. `local` in the
677
- * sense every other tool uses it belongs to this machine, is not shared, and losing it costs nothing. */
677
+ * sense every other tool uses it, belongs to this machine, is not shared, and losing it costs nothing. */
678
678
  | "local"
679
679
  /* Who owns this sandbox and who may drive it. Backed up so the owner keeps a copy of their own access, never
680
- * carried a list that travelled would let a source sandbox claim the target. */
680
+ * carried, a list that travelled would let a source sandbox claim the target. */
681
681
  | "identity"
682
682
  /* Credentials. Never backed up; carried only when the owner opts in at export and the bundle records it. */
683
683
  | "secrets";
684
684
 
685
685
  /* THE FOLDER EACH GROUP LIVES IN.
686
686
  *
687
- * The group name IS the directory name one vocabulary, not a name and a translation of it. That is what lets
687
+ * The group name IS the directory name, one vocabulary, not a name and a translation of it. That is what lets
688
688
  * the guard in workspace-state.test.ts check the whole layout with one rule ("every entry sits under its own
689
689
  * group's folder") rather than trusting forty-nine literals to have been typed correctly, and it is why renaming
690
690
  * a folder is an edit here plus the literals the compiler then points at, with nothing able to half-move.
@@ -729,12 +729,12 @@ export const stateGroupOf = (file: WorkspaceStateFile): StateGroup => {
729
729
  }
730
730
  };
731
731
 
732
- // The entries of one group, workspace-root-relative and in declaration order what each rule that used to keep
732
+ // The entries of one group, workspace-root-relative and in declaration order, what each rule that used to keep
733
733
  // its own path list now asks for instead.
734
734
  export const stateGroupPaths = (group: StateGroup): readonly string[] =>
735
735
  WORKSPACE_STATE_FILES.filter((file) => stateGroupOf(file) === group).map((file) => file.path);
736
736
 
737
- /* THE SLICE DESKTOP-SYNC COPIES DOWN ordinary state and the records that bind this sandbox to its owner,
737
+ /* THE SLICE DESKTOP-SYNC COPIES DOWN, ordinary state and the records that bind this sandbox to its owner,
738
738
  * minus anything that opted out (see `backup` on the interface).
739
739
  *
740
740
  * The sync used to ignore `.intentic` WHOLE, which is the same conflation the `backup` flag exists to undo: the
@@ -750,12 +750,12 @@ export const BACKED_UP_STATE_PATHS: readonly string[] = WORKSPACE_STATE_FILES.fi
750
750
 
751
751
  /* Its complement, which is what a sync ignore list actually needs: everything under the state dir that must NOT
752
752
  * come down. Derived from the same predicate rather than listed, so a store added tomorrow is excluded until its
753
- * class says otherwise the same default-deny the search floor and the portability classes are built on. */
753
+ * class says otherwise, the same default-deny the search floor and the portability classes are built on. */
754
754
  export const UNBACKED_STATE_PATHS: readonly string[] = WORKSPACE_STATE_FILES.filter((file) => !BACKED_UP_STATE_PATHS.includes(file.path)).map(
755
755
  (file) => file.path,
756
756
  );
757
757
 
758
- /* THE ONE WAY AN EXTENSION NAMES ITS SCRATCH HOME `.intentic/runtime/extensions/<id>`, workspace-relative
758
+ /* THE ONE WAY AN EXTENSION NAMES ITS SCRATCH HOME, `.intentic/runtime/extensions/<id>`, workspace-relative
759
759
  * and forward-slash so the browser bundle can hold it too; callers join it onto whatever root is in force.
760
760
  *
761
761
  * It exists for the reason statePath does one table over: before it, every gateway spelled the layout itself
@@ -766,23 +766,23 @@ export const UNBACKED_STATE_PATHS: readonly string[] = WORKSPACE_STATE_FILES.fil
766
766
  export const extensionRuntimeDir = (extension: string): string =>
767
767
  `${STATE_GROUP_DIR.local}/runtime/extensions/${extension.replaceAll(/[^a-zA-Z0-9._-]/g, "_")}`;
768
768
 
769
- /* The manifests whose problems the unreadable-manifest notice SHOWS the handful a person hand-edits and the
769
+ /* The manifests whose problems the unreadable-manifest notice SHOWS, the handful a person hand-edits, and the
770
770
  * one fact that decides it is already in the table above.
771
771
  *
772
772
  * Every store reads through the same `jsonFile`, so every store reports what it could not make sense of, and for
773
- * a long time the notice showed all of them. That is wrong twice over. Its advice fix the file and this clears
774
- * on its own is addressed to somebody holding an editor, which is true of `settings.json` and false of a
773
+ * a long time the notice showed all of them. That is wrong twice over. Its advice, fix the file and this clears
774
+ * on its own, is addressed to somebody holding an editor, which is true of `settings.json` and false of a
775
775
  * daemon-written LEDGER nobody opens: a run history that stopped matching a schema the build tightened is not a
776
776
  * mistake the owner made, and the card asked them to repair sixty kilobytes of machine JSON by hand. Worse, a
777
777
  * file that reports into the notice without feeding the notice's QUERY leaves a complaint no write can refresh,
778
- * so it sits on screen until the daemon restarts which is exactly how the workflow ledger's entry became
778
+ * so it sits on screen until the daemon restarts, which is exactly how the workflow ledger's entry became
779
779
  * permanent furniture.
780
780
  *
781
781
  * Both follow from one rule, which is why this derives rather than lists: a file's problems are shown IFF a write
782
782
  * to that file refreshes the notice. Declaring `manifests` in `invalidates` is the entire opt-in, so the edit
783
783
  * that puts a file on the card is the same edit that keeps it current, and neither can be done without the
784
784
  * other. A ledger that breaks still falls back and still sets its unreadable bytes aside on the next write
785
- * (store/json-file.ts) it just stops asking the owner to fix it. */
785
+ * (store/json-file.ts), it just stops asking the owner to fix it. */
786
786
  export const REPORTED_MANIFEST_PATHS: readonly string[] = WORKSPACE_STATE_FILES.filter((file) => file.invalidates.includes("manifests")).map(
787
787
  (file) => file.path,
788
788
  );
@@ -794,22 +794,22 @@ export const isReportedManifest = (relPath: string): boolean => REPORTED_MANIFES
794
794
  /* Old directory names are never read or migrated. Keep that finite set in one quarantine record so access,
795
795
  * export, and search cannot reinterpret abandoned machine state as ordinary workspace content after a rename.
796
796
  * `artifacts` still carry; the distinction here tells portability only which retired roots are secrets or
797
- * derived and tells the state janitor which it may DELETE: a retired `derived` root is a rebuildable cache
797
+ * derived, and tells the state janitor which it may DELETE: a retired `derived` root is a rebuildable cache
798
798
  * by its own classification, so leaving 466 MB of abandoned model where only a manual `rm` reaches it was
799
799
  * quarantine doing half its job. Secret and artifact roots stay until an owner removes them by hand: deleting
800
800
  * content is not the janitor's call, only deleting what the class already says is disposable. */
801
801
  export const RETIRED_WORKSPACE_STATE_DIRS = {
802
802
  /* The flat spellings, from before the state dir was grouped into its five folders. Every entry in the table
803
803
  * used to sit directly under `.intentic/`, and a sandbox that predates the move still has those directories
804
- * on disk with real contents in them. They are quarantined rather than migrated the workspace rule is fresh
805
- * state and no compatibility layers but quarantine is exactly what they need, because the alternative is
804
+ * on disk with real contents in them. They are quarantined rather than migrated, the workspace rule is fresh
805
+ * state and no compatibility layers, but quarantine is exactly what they need, because the alternative is
806
806
  * worse than leaving them: `auth` at the old spelling is a real credential store that the classifier no
807
807
  * longer recognises, so without these names it would read as ordinary workspace content and be indexed by
808
808
  * search, carried by an export and copied down by the backup. Being listed here keeps the old copy as
809
809
  * untouchable as the new one, and lets the janitor delete the rebuildable half. */
810
810
  secret: ["claude", "codex", "kimi", "opencode", "cliproxy", "auth", "ci.json"],
811
811
  /* The ownership records at their OLD flat spelling, and a bucket of their own rather than a few more names
812
- * in `secret` because the two classes differ on exactly the thing that matters for a leftover. A `secret`
812
+ * in `secret`, because the two classes differ on exactly the thing that matters for a leftover. A `secret`
813
813
  * travels when the owner opts in at export; an `identity` never travels at all, since a list of who may
814
814
  * drive this sandbox arriving in another one is how a source hands itself the target's ownership. Filed under
815
815
  * `secret` these would have become carryable by ticking a box, which is the one outcome their live entries
@@ -819,23 +819,23 @@ export const RETIRED_WORKSPACE_STATE_DIRS = {
819
819
  artifacts: ["attachments", "acceptance", "loops", "workflow-runs", "transcripts", "artifacts", "sessions"],
820
820
  } as const;
821
821
 
822
- /* THE DAEMON'S OWN CONTROL PLANE the entries directly under the workspace root's `.intentic/` that the file
822
+ /* THE DAEMON'S OWN CONTROL PLANE, the entries directly under the workspace root's `.intentic/` that the file
823
823
  * API refuses to read, write, move or delete for anyone, the owner included (workspace/workspace-files.ts holds
824
824
  * the enforcement and the full reasoning for each name).
825
825
  *
826
826
  * The list lives HERE, in the package both sides import, because the browser has to draw the same rule the
827
827
  * daemon enforces. It didn't, and the gap was a small piece of theatre: the explorer listed `capabilities.json`
828
828
  * like any other file, opening it flashed a tab, the read came back with nothing there, and the tab closed
829
- * itself a refusal acted out as a glitch. A file the app will not open should say so before it is clicked,
829
+ * itself, a refusal acted out as a glitch. A file the app will not open should say so before it is clicked,
830
830
  * which takes a rule the explorer can consult, not a status code it can only react to.
831
831
  *
832
- * Naming these to the browser gives nothing away that the tree did not already publish it listed them, sizes
832
+ * Naming these to the browser gives nothing away that the tree did not already publish, it listed them, sizes
833
833
  * and all. What stays behind the guard is the only thing that ever mattered: the bytes. */
834
- /* GROUP-RELATIVE NOW, and the one rule the regrouping did NOT simplify worth saying because every other rule
834
+ /* GROUP-RELATIVE NOW, and the one rule the regrouping did NOT simplify, worth saying because every other rule
835
835
  * over this tree collapsed to a prefix and this one could not. What the file API refuses to open cuts ACROSS the
836
836
  * groups: the capability manifest is `config`, the transcripts are `records`, the browser profiles are `local`,
837
837
  * and all of `identity` and `secrets` is in. That is not an accident of the grouping, it is a different question
838
- * "would showing the bytes hand someone something" rather than "what kind of thing is this" so it keeps an
838
+ *, "would showing the bytes hand someone something" rather than "what kind of thing is this", so it keeps an
839
839
  * explicit list, just one that now names the folder each entry lives in. */
840
840
  const LOCKED_STATE_ENTRIES: ReadonlySet<string> = new Set([
841
841
  "identity/owner.json",
@@ -846,14 +846,14 @@ const LOCKED_STATE_ENTRIES: ReadonlySet<string> = new Set([
846
846
  "secrets/auth",
847
847
  "records/sessions",
848
848
  "local/browser",
849
- /* The provider CLI's own home, which this table does not declare and so has no group to move into it is
849
+ /* The provider CLI's own home, which this table does not declare and so has no group to move into, it is
850
850
  * written by the agent's runtime rather than by any daemon store. It stays at the state dir's root, and the
851
851
  * two-segment match below still reaches it because a bare name joins to itself. Locked for the reason the
852
852
  * credential entries are: it holds a live session for whatever the agent is signed into. */
853
853
  "claude.json",
854
854
  ]);
855
855
 
856
- /* Whether a workspace-root-relative path lands in that control plane and so is shown locked rather than
856
+ /* Whether a workspace-root-relative path lands in that control plane, and so is shown locked rather than
857
857
  * opened. Scoped deliberately tight, matching the guard: only the ROOT `.intentic` counts (a repo's own nested
858
858
  * one is ordinary content) and only these entries within it, subtrees included, so a new provider dropped under
859
859
  * `auth/` is covered without a second edit.
@@ -872,7 +872,7 @@ export const isLockedWorkspacePath = (relPath: string): boolean => {
872
872
  }
873
873
  /* Two shapes, because two layouts exist on disk. A current entry is `<group>/<name>`; a leftover from before
874
874
  * the regrouping is a bare name directly under the state dir, and the quarantine record is what still knows
875
- * those are credentials. Both are locked a stale `auth/` full of real tokens is no safer to open than the
875
+ * those are credentials. Both are locked, a stale `auth/` full of real tokens is no safer to open than the
876
876
  * live one, and the whole point of quarantining the old spellings was that nothing downstream reclassifies
877
877
  * them as ordinary content. */
878
878
  return (
@@ -881,22 +881,22 @@ export const isLockedWorkspacePath = (relPath: string): boolean => {
881
881
  );
882
882
  };
883
883
 
884
- /* THE LOCKED ENTRIES THE ROOT REPO TRACKS refused by the file API, and diffable anyway.
884
+ /* THE LOCKED ENTRIES THE ROOT REPO TRACKS, refused by the file API, and diffable anyway.
885
885
  *
886
886
  * `capabilities.json` is the only one today and the whole reason this exists. Both of its rules are right on
887
887
  * their own: it is `versioned`, because connecting this sandbox to a deployment orchestrator is the largest
888
888
  * change anyone makes to what it can DO and that belongs in review; and it is locked, because a member who
889
889
  * could PUT one through the generic file API would be granting themselves a capability the owner never
890
- * approved. The lock was always about that WRITE its credentials live in the vault, not in the file.
890
+ * approved. The lock was always about that WRITE, its credentials live in the vault, not in the file.
891
891
  *
892
892
  * Together, though, the second silently cancelled the first. The Changes panel listed the file (git tracks it,
893
- * so `git status` reports it), and clicking the row asked a diff route that refuses every control-plane path
893
+ * so `git status` reports it), and clicking the row asked a diff route that refuses every control-plane path,
894
894
  * a 404 on the one surface `versioned` exists to produce. The bytes were already in `git log`, in every clone
895
895
  * of the root repo and in the workspace search; only the review was missing.
896
896
  *
897
897
  * So the review surfaces ask THIS instead of the flat lock, and it derives from the same flag rather than
898
898
  * naming the file, so marking another locked entry `versioned` cannot reproduce the contradiction. Every other
899
- * surface read, write, move, delete, publish still asks `isLockedWorkspacePath` and still refuses.
899
+ * surface, read, write, move, delete, publish, still asks `isLockedWorkspacePath` and still refuses.
900
900
  *
901
901
  * Accepts either slash, like the rule above it. */
902
902
  export const isReviewableLockedPath = (relPath: string): boolean => {
@@ -908,7 +908,7 @@ export const isReviewableLockedPath = (relPath: string): boolean => {
908
908
  * the first sentence of this file's header TRUE rather than aspirational.
909
909
  *
910
910
  * "The daemon builds its store paths from `path`" was the design; the code did not. `composition.ts` and twenty
911
- * files beside it spelled the same layout a SECOND way `join(root, ".intentic", "settings.json")` with
911
+ * files beside it spelled the same layout a SECOND way, `join(root, ".intentic", "settings.json")`, with
912
912
  * nothing tying the two spellings together. Rename a store's file and this table keeps declaring the old name:
913
913
  * no error, no failing test, just a view that quietly stops refreshing, which is the exact failure the table was
914
914
  * written to end and the exact way drafts went missing.
@@ -921,12 +921,12 @@ export type WorkspaceStatePath = (typeof STATE_FILES)[number]["path"];
921
921
  * this; keeping it here rather than in the web means the rule is unit-testable without a query client, and the
922
922
  * daemon can assert against the same table.
923
923
  *
924
- * `contributed` is what the ACTIVATED extensions declared in `contributes.files` passed in rather than
924
+ * `contributed` is what the ACTIVATED extensions declared in `contributes.files`, passed in rather than
925
925
  * imported, because which extensions are live is a browser fact this package has no way to know. It is a
926
926
  * required argument for the same reason: an added second source that callers may forget is a source that
927
927
  * silently does nothing, which is the failure this whole file exists to remove. Extension entries are unioned
928
928
  * flat with the core ones, not layered over them: both lists describe the same fact about the same file, and a
929
- * path can legitimately match one entry in each a core prefix that invalidates nothing must not veto a
929
+ * path can legitimately match one entry in each, a core prefix that invalidates nothing must not veto a
930
930
  * narrower extension entry beneath it, or everything under one of the daemon's machine-state prefixes would be
931
931
  * unreachable to extensions by construction. */
932
932
  export const staleQueryKeys = (paths: readonly string[], contributed: readonly FileContribution[]): readonly string[] => [
@@ -937,9 +937,9 @@ export const staleQueryKeys = (paths: readonly string[], contributed: readonly F
937
937
  ),
938
938
  ];
939
939
 
940
- /* Every query key any watched file feeds what a NEW /events connection invalidates wholesale (core's table
940
+ /* Every query key any watched file feeds, what a NEW /events connection invalidates wholesale (core's table
941
941
  * plus the running extensions'). The file push is these keys' ONLY live feed, and a `workspaceChanged` frame
942
- * produced while the stream was down is a frame nobody will ever resend so each key's view would sit stale
942
+ * produced while the stream was down is a frame nobody will ever resend, so each key's view would sit stale
943
943
  * until the file's NEXT write, indefinitely for anything that settled while the browser was away. Re-asking on
944
944
  * connect bounds the damage at one cheap read per key, which is what lets those views go entirely unpolled. */
945
945
  export const fileBoundQueryKeys = (contributed: readonly FileContribution[]): readonly string[] => [