@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
@@ -1,4 +1,4 @@
1
- /* WHY A WORKFLOW GRAPH IS NOT RUNNABLE the rules that `WorkflowSchema` cannot state, because each of them is
1
+ /* WHY A WORKFLOW GRAPH IS NOT RUNNABLE, the rules that `WorkflowSchema` cannot state, because each of them is
2
2
  * about the graph rather than about one field.
3
3
  *
4
4
  * A rule per function, so the reason a graph is refused reads as one sentence in one place. They are
@@ -38,7 +38,7 @@ const stepFaults = (step: WorkflowStep, ids: ReadonlySet<string>): string[] => {
38
38
  if (step.handoff === "continue" && !continuesOneSession(step)) {
39
39
  faults.push(
40
40
  step.needs.length === 0
41
- ? `"${step.title}" continues a session but starts the run there is nothing to continue.`
41
+ ? `"${step.title}" continues a session but starts the run: there is nothing to continue.`
42
42
  : `"${step.title}" continues a session but waits for ${step.needs.length} steps; it can only continue one.`,
43
43
  );
44
44
  }
@@ -49,24 +49,24 @@ const stepFaults = (step: WorkflowStep, ids: ReadonlySet<string>): string[] => {
49
49
  ),
50
50
  );
51
51
  }
52
- /* A STEP THAT DECLARES NOTHING IS NOT A FAULT it is the ordinary step, and this used to refuse it.
52
+ /* A STEP THAT DECLARES NOTHING IS NOT A FAULT, it is the ordinary step, and this used to refuse it.
53
53
  *
54
54
  * The rule was borrowed from loops (`loopCanConverge`), where it is right: a LOOP is started to repeat
55
55
  * until something is true, so one with nothing to produce and nothing to check has no reason to run twice
56
56
  * and the dialog is correct to grey out its button. A STEP is not started to repeat. It is one agent
57
- * session with a job, and the job being done is the turn ending which is exactly what the loop machinery
57
+ * session with a job, and the job being done is the turn ending, which is exactly what the loop machinery
58
58
  * already does with it (loop-stop's `readDocument` answers `done` for a `none` output, so iteration 1 is
59
59
  * the only iteration).
60
60
  *
61
61
  * Keeping the rule here forced every step to declare an output or a check before the graph would save, and
62
- * the cheapest way to satisfy it was a `claim` which buys a verdict file nobody reads, a page of contract
62
+ * the cheapest way to satisfy it was a `claim`, which buys a verdict file nobody reads, a page of contract
63
63
  * in the prompt, and a way for a step that did the work to fail for not having described it.
64
64
  */
65
65
  return faults;
66
66
  };
67
67
 
68
68
  /* Two steps continuing the SAME session is the one graph that is legal on paper and broken in practice: both
69
- * would run on one conversation, in parallel, against one worktree and one turn mutex so they would serialize
69
+ * would run on one conversation, in parallel, against one worktree and one turn mutex, so they would serialize
70
70
  * on a lock neither knows about and the second would inherit a session the first had moved on. A predecessor
71
71
  * can be continued once; anything else that needs its result takes it as a handover. */
72
72
  const sharedContinuationFaults = (steps: readonly WorkflowStep[]): string[] => {
@@ -110,8 +110,8 @@ const cycleFaults = (steps: readonly WorkflowStep[]): string[] => {
110
110
  return faults;
111
111
  };
112
112
 
113
- /* A GATE THAT CANNOT BE ANSWERED. Every rule here is about the gate against the GRAPH which is why none of
114
- * them can live in the schema and all of them fail the same expensive way if unchecked: the run spends its
113
+ /* A GATE THAT CANNOT BE ANSWERED. Every rule here is about the gate against the GRAPH, which is why none of
114
+ * them can live in the schema, and all of them fail the same expensive way if unchecked: the run spends its
115
115
  * whole fan-out of sessions and then answers `blocked`, on every commit, for a reason nobody sees until they
116
116
  * go reading the daemon's log.
117
117
  *
@@ -136,16 +136,16 @@ const gateFaults = (workflow: Pick<Workflow, "steps" | "gate">): string[] => {
136
136
  return [`The gate reads "${gate.field}", which "${step.title}" does not declare.`];
137
137
  }
138
138
  if (field.type === "string[]") {
139
- return [`The gate reads "${gate.field}", which is a list a release decision has to be one value.`];
139
+ return [`The gate reads "${gate.field}", which is a list: a release decision has to be one value.`];
140
140
  }
141
- // The schema refuses this on save (pass is min(1)), but the designer edits drafts the schema never sees
141
+ // The schema refuses this on save (pass is min(1)), but the designer edits drafts the schema never sees,
142
142
  // and an empty allowlist is a gate no run could ever answer "pass", which deserves a sentence, not a save error.
143
143
  if (gate.pass.length === 0) {
144
144
  return [`The gate names no passing values, so no run could ever ship.`];
145
145
  }
146
146
  // A field the step may legally omit is a gate that answers `blocked` whenever it does, which is a release
147
147
  // stuck on a technicality rather than on the product.
148
- return field.required ? [] : [`The gate reads "${gate.field}", which "${step.title}" declares optional it has to be required.`];
148
+ return field.required ? [] : [`The gate reads "${gate.field}", which "${step.title}" declares optional, it has to be required.`];
149
149
  };
150
150
 
151
151
  /* Why the graph is not runnable, as a list of sentences. Empty ⇒ it is. Shared by the save route (which
@@ -163,13 +163,13 @@ export const workflowFaults = (workflow: Pick<Workflow, "steps" | "gate">): stri
163
163
  ];
164
164
  };
165
165
 
166
- /* WHAT ONLY A RUN CAN BE WRONG ABOUT kept apart from the rules above because it is not about the graph, and
166
+ /* WHAT ONLY A RUN CAN BE WRONG ABOUT, kept apart from the rules above because it is not about the graph, and
167
167
  * the graph is what gets SAVED. A design whose steps take their goal and instruction from the request is a
168
168
  * perfectly good design; it is only unrunnable on the particular run that forgot to bring one.
169
169
  *
170
170
  * Which is why this cannot be a save-time rule and must not become one: refusing to save such a workflow would
171
- * outlaw the entire point of a workflow being a SHAPE. The check belongs at the two doors that start runs the
172
- * run route and the gate's webhook and it has to be there rather than left to fail later, because "later"
171
+ * outlaw the entire point of a workflow being a SHAPE. The check belongs at the two doors that start runs, the
172
+ * run route and the gate's webhook, and it has to be there rather than left to fail later, because "later"
173
173
  * means every session in the fan-out has already been paid for before anyone finds out the model was handed an
174
174
  * empty instruction.
175
175
  */
@@ -21,7 +21,7 @@ import {
21
21
  // What the automations extension declares in its manifest, and what the memory extension WOULD declare if the
22
22
  // watcher reported its files. Literals rather than the real manifests: an extension package importing this one is
23
23
  // the dependency direction, so reaching back for them here would invert it. The real manifests are checked
24
- // against this rule where they are loaded web's fileBindings.test.ts and the daemon's file-bindings.test.ts.
24
+ // against this rule where they are loaded: web's fileBindings.test.ts and the daemon's file-bindings.test.ts.
25
25
  const AUTOMATIONS: readonly FileContribution[] = [
26
26
  { path: `${STATE_DIR}/config/automations.json`, invalidates: [`automations`] },
27
27
  { path: `${STATE_DIR}/records/approvals/`, invalidates: [`automation-approvals`] },
@@ -45,14 +45,14 @@ describe(`staleQueryKeys`, () => {
45
45
  });
46
46
 
47
47
  it(`matches a name family and a one-file-per-entry directory through one prefix each`, () => {
48
- // environment.Dockerfile, environment.custom.Dockerfile, environment.approved.Dockerfile one entry.
48
+ // environment.Dockerfile, environment.custom.Dockerfile, environment.approved.Dockerfile: one entry.
49
49
  expect(staleQueryKeys([`.intentic/config/environment.custom.Dockerfile`], [])).toEqual([`environment`]);
50
50
  expect(staleQueryKeys([`.intentic/config/drafts/post-1.json`], [])).toEqual([`drafts`]);
51
51
  });
52
52
 
53
53
  it(`refreshes the Drafts view when the AGENT writes a draft`, () => {
54
54
  // The regression this table was reorganized around: the drafts skill writes these files directly, so
55
- // there is no browser mutation to hang an invalidate on the watcher push is the only signal, and it
55
+ // there is no browser mutation to hang an invalidate on: the watcher push is the only signal, and it
56
56
  // used to be dropped on the floor.
57
57
  expect(staleQueryKeys([`.intentic/config/drafts/post-1.json`], [])).toEqual([`drafts`]);
58
58
  });
@@ -75,7 +75,7 @@ describe(`staleQueryKeys`, () => {
75
75
  });
76
76
 
77
77
  it(`dedupes keys across a batch that touches several manifests`, () => {
78
- // A capability add recomposes the overlay, so both entries claim `environment` one refetch, not two.
78
+ // A capability add recomposes the overlay, so both entries claim `environment`: one refetch, not two.
79
79
  expect(staleQueryKeys([`.intentic/config/capabilities.json`, `.intentic/config/environment.Dockerfile`], [])).toEqual([
80
80
  `capabilities`,
81
81
  `environment`,
@@ -129,7 +129,7 @@ describe(`isReportedManifest`, () => {
129
129
 
130
130
  it(`stays quiet about daemon-written state the owner cannot repair`, () => {
131
131
  // The regression this exists for: the workflow ledger's records predated a schema that gained a required
132
- // field, so every read reported the whole file ignored advice ("fix the file") addressed to nobody,
132
+ // field, so every read reported the whole file ignored: advice ("fix the file") addressed to nobody,
133
133
  // about sixty kilobytes of machine JSON, and refreshed by no write because the ledger feeds no query, so
134
134
  // it sat on the card until the daemon restarted. A ledger recovers on its own next write instead.
135
135
  expect(isReportedManifest(`.intentic/records/workflow-runs.json`)).toBe(false);
@@ -142,7 +142,7 @@ describe(`isReportedManifest`, () => {
142
142
  });
143
143
 
144
144
  it(`does not report a file outside the workspace`, () => {
145
- // What `relative` hands the daemon for the manifests it keeps under /history never nameable on screen.
145
+ // What `relative` hands the daemon for the manifests it keeps under /history: never nameable on screen.
146
146
  expect(isReportedManifest(`../../history/settings.json`)).toBe(false);
147
147
  });
148
148
  });
@@ -158,7 +158,7 @@ describe(`WORKSPACE_STATE_FILES`, () => {
158
158
  it(`states a reason for every entry that invalidates nothing`, () => {
159
159
  // An empty `invalidates` is a real answer (daemon machine state, a deliberately-polled surface, a path
160
160
  // whose query keys belong to an extension), but a SILENT one is indistinguishable from the omission this
161
- // table exists to prevent which is exactly how drafts went missing. Requiring the reason is what makes
161
+ // table exists to prevent, which is exactly how drafts went missing. Requiring the reason is what makes
162
162
  // the difference visible at review time.
163
163
  for (const file of WORKSPACE_STATE_FILES) {
164
164
  if (file.invalidates.length === 0) {
@@ -179,7 +179,7 @@ describe(`WORKSPACE_STATE_FILES`, () => {
179
179
  });
180
180
 
181
181
  it(`only nests under an entry that invalidates nothing, so one write can't be billed twice`, () => {
182
- // Entries may nest when a subtree needs a different portability class stateFileFor's longest match
182
+ // Entries may nest when a subtree needs a different portability class: stateFileFor's longest match
183
183
  // keeps that unambiguous. Invalidation has no longest-match rule: staleQueryKeys unions every
184
184
  // matching entry, so a nest under an entry that DOES invalidate would bill the outer view's queries for
185
185
  // a write that belongs to the inner one. Nesting is therefore only legal beneath an empty `invalidates`.
@@ -247,7 +247,7 @@ describe(`isReviewableLockedPath`, () => {
247
247
  it(`admits the locked entry the root repo tracks, and nothing else locked`, () => {
248
248
  expect(isReviewableLockedPath(`.intentic/config/capabilities.json`)).toBe(true);
249
249
  // Every other locked entry is a credential, an identity binding or private runtime state. None is
250
- // versioned, so none is reachable through a diff the carve-out cannot widen without the flag.
250
+ // versioned, so none is reachable through a diff: the carve-out cannot widen without the flag.
251
251
  expect(isReviewableLockedPath(`.intentic/identity/owner.json`)).toBe(false);
252
252
  expect(isReviewableLockedPath(`.intentic/identity/members.json`)).toBe(false);
253
253
  expect(isReviewableLockedPath(`.intentic/secrets/ci.json`)).toBe(false);
@@ -257,7 +257,7 @@ describe(`isReviewableLockedPath`, () => {
257
257
  expect(isReviewableLockedPath(`.git/config`)).toBe(false);
258
258
  });
259
259
 
260
- it(`answers only for the locked set an ordinary path was never refused to begin with`, () => {
260
+ it(`answers only for the locked set: an ordinary path was never refused to begin with`, () => {
261
261
  // Tracked, but not locked: the guards never ask this of them, and a `true` here would read as "this
262
262
  // path needed a carve-out", which is a different and wrong statement.
263
263
  expect(isReviewableLockedPath(`.intentic/config/settings.json`)).toBe(false);
@@ -285,22 +285,22 @@ describe(`VERSIONED_STATE_PATHS`, () => {
285
285
  *
286
286
  * `versioned` carves an entry out of the root repo's wholesale `.intentic` exclusion, so marking one is the
287
287
  * difference between a file the owner reviews and a file the next baseline commit publishes into `git log`
288
- * forever. A credential marked by a hurried hand is not recoverable by unmarking it later the commit is
289
- * already written which is why the refusal is mechanical here rather than a rule in a comment. */
288
+ * forever. A credential marked by a hurried hand is not recoverable by unmarking it later: the commit is
289
+ * already written, which is why the refusal is mechanical here rather than a rule in a comment. */
290
290
  it(`never tracks a credential or an identity binding`, () => {
291
291
  const leaked = WORKSPACE_STATE_FILES.filter((file) => file.versioned && (file.portability === `secret` || file.portability === `identity`));
292
292
  expect(leaked.map((file) => file.path)).toEqual([]);
293
293
  });
294
294
 
295
- /* Narrower than `carry` ON PURPOSE, and this is where that stays true. The two answer different questions
296
- * carry is "does it move to a new sandbox", versioned is "should a human review it changing" so the
295
+ /* Narrower than `carry` ON PURPOSE, and this is where that stays true. The two answer different questions:
296
+ * carry is "does it move to a new sandbox", versioned is "should a human review it changing", so the
297
297
  * ledgers and the bulk are `carry` and deliberately absent: the run ledger is rewritten several times per
298
298
  * workflow step, the usage batch every few seconds a browser is open, and the transcripts run to hundreds of
299
299
  * megabytes. Tracking any of them buries the owner's code review under machine noise. */
300
300
  it(`leaves the ledgers and the bulk out even though they travel`, () => {
301
301
  for (const path of [
302
302
  `.intentic/records/workflow-runs.json`,
303
- // Split out of the tracked automations manifest precisely so a fire stops dirtying it the one
303
+ // Split out of the tracked automations manifest precisely so a fire stops dirtying it: the one
304
304
  // entry here that would be a REGRESSION rather than an oversight if it ever went tracked.
305
305
  `.intentic/records/automation-runs.json`,
306
306
  `.intentic/records/loops.json`,
@@ -315,11 +315,11 @@ describe(`VERSIONED_STATE_PATHS`, () => {
315
315
  });
316
316
 
317
317
  /* Spelled out rather than derived, so ADDING a tracked entry is a visible edit to this list and not a silent
318
- * consequence of editing the table above the review the flag itself exists to force. */
318
+ * consequence of editing the table above: the review the flag itself exists to force. */
319
319
  it(`tracks exactly the configuration slice plus the agent's own authored output`, () => {
320
320
  expect(VERSIONED_STATE_PATHS.toSorted()).toEqual([
321
321
  `.intentic/config/automations.json`,
322
- /* The connections themselves, and the entry that took the longest to earn its place it was classed
322
+ /* The connections themselves, and the entry that took the longest to earn its place: it was classed
323
323
  * `secret` on the strength of holding each capability's credential, which stopped being true when the
324
324
  * vault took the values out and left the shape behind. Connecting a deployment orchestrator, or
325
325
  * granting a connected computer shell and screen control, is the largest change made to what this
@@ -327,9 +327,9 @@ describe(`VERSIONED_STATE_PATHS`, () => {
327
327
  `.intentic/config/capabilities.json`,
328
328
  `.intentic/config/capability-dismissals.json`,
329
329
  /* The two entries the AGENT authors on its own initiative, and the reason `versioned` is not read as
330
- * config-only. Both are the sandbox acting outward a draft publishes words under the owner's name,
330
+ * config-only. Both are the sandbox acting outward: a draft publishes words under the owner's name,
331
331
  * a workspace extension is code that runs in the app and can serve HTTP with the workspace under
332
- * node:fs and both used to reach that far with no diff anywhere. Kept rather than consumed, one
332
+ * node:fs, and both used to reach that far with no diff anywhere. Kept rather than consumed, one
333
333
  * small file at a time, so tracking them yields a record instead of churn. */
334
334
  `.intentic/config/drafts/`,
335
335
  `.intentic/config/environment.Dockerfile`,
@@ -340,12 +340,12 @@ describe(`VERSIONED_STATE_PATHS`, () => {
340
340
  * behind it was not, so a commit could record turning an extension on and say nothing about what it
341
341
  * was told to do. Tracked once its declared-secret values moved to the vault. */
342
342
  `.intentic/config/extension-settings.json`,
343
- // The owner's per-extension update posture (notify / agent / auto) a standing decision about
343
+ // The owner's per-extension update posture (notify / agent / auto): a standing decision about
344
344
  // what may run unattended, which is exactly the kind of edit worth a line in `git log`.
345
345
  `.intentic/config/extension-update-policy.json`,
346
346
  `.intentic/config/loop-designs.json`,
347
347
  `.intentic/config/personas.json`,
348
- // A persona's own kit the prompt it runs on and the skills only its turns reach. Tracked for the
348
+ // A persona's own kit: the prompt it runs on and the skills only its turns reach. Tracked for the
349
349
  // reason its card is, one step further: this is the text that decides how that persona behaves.
350
350
  `.intentic/config/personas/`,
351
351
  `.intentic/config/settings.json`,
@@ -360,7 +360,7 @@ describe(`VERSIONED_STATE_PATHS`, () => {
360
360
 
361
361
  /* THE ASYMMETRY THIS CLOSED, kept as its own assertion because it is the failure rather than a detail of it:
362
362
  * the switch was tracked and the thing it switched was not, so a commit could record turning on an extension
363
- * whose code nobody else could read and a workspace extension has no install moment to review at instead. */
363
+ * whose code nobody else could read, and a workspace extension has no install moment to review at instead. */
364
364
  it(`tracks a workspace extension's code, not just the switch that enables it`, () => {
365
365
  expect(VERSIONED_STATE_PATHS).toContain(`.intentic/config/extension-enablement.json`);
366
366
  expect(VERSIONED_STATE_PATHS).toContain(`.intentic/config/workspace-extensions/`);
@@ -375,14 +375,14 @@ describe(`VERSIONED_STATE_PATHS`, () => {
375
375
  for (const path of [`.intentic/records/approvals/`, `.intentic/config/docs/`]) {
376
376
  expect([path, VERSIONED_STATE_PATHS.includes(path)]).toEqual([path, false]);
377
377
  }
378
- // Both still reach workspace search searchability is a property of the content, not of tracking.
378
+ // Both still reach workspace search: searchability is a property of the content, not of tracking.
379
379
  for (const path of [`.intentic/records/approvals/`, `.intentic/config/docs/`]) {
380
380
  const entry = WORKSPACE_STATE_FILES.find((file) => file.path === path);
381
381
  expect([path, entry?.versioned === true || entry?.authored === true]).toEqual([path, path === `.intentic/config/docs/`]);
382
382
  }
383
383
  });
384
384
 
385
- /* The composed overlay is `derived` recomposed on every boot from the custom file that IS tracked, against
385
+ /* The composed overlay is `derived`: recomposed on every boot from the custom file that IS tracked, against
386
386
  * whatever base image this container happens to be on. Tracking it would put a rewritten-at-startup file in
387
387
  * front of the owner as a change they made. */
388
388
  it(`tracks the environment overlay's source but not its composed output`, () => {
@@ -395,7 +395,7 @@ describe(`VERSIONED_STATE_PATHS`, () => {
395
395
  *
396
396
  * `stateGroupOf` reads three existing answers instead of adding a fourth, which is only sound while those answers
397
397
  * nest: reviewed and authored entries must all be `carry`, so "did a person write this" never has to be asked of
398
- * a credential. Nothing in the type system says so `versioned: true` on a `secret` entry compiles and the
398
+ * a credential. Nothing in the type system says so, `versioned: true` on a `secret` entry compiles, and the
399
399
  * failure would be quiet in the worst way: the entry would group as `secrets` (correctly kept out of the backup)
400
400
  * while the git exclude and the search floor, which read `versioned` directly, went on tracking and indexing it.
401
401
  * A credential in the owner's diff and in the search index, from one plausible-looking flag. */
@@ -439,8 +439,8 @@ describe(`state groups`, () => {
439
439
 
440
440
  /* THE LAYOUT GUARD, and the reason the folders can carry the rules at all.
441
441
  *
442
- * Five prefixes replaced five hand-kept path lists the git exclude, the search allow-list, the sync backup,
443
- * the watcher skip, the export bundle and every one of them is now only as true as the claim that an entry
442
+ * Five prefixes replaced five hand-kept path lists: the git exclude, the search allow-list, the sync backup,
443
+ * the watcher skip, the export bundle, and every one of them is now only as true as the claim that an entry
444
444
  * physically SITS in the folder its class puts it in. Nothing else checks that: the paths are literals, and a
445
445
  * credential typed into `config/` by mistake would compile, track in git, index in search and copy to the
446
446
  * owner's laptop, with each of those rules behaving exactly as designed. This is the one assertion standing
@@ -453,7 +453,7 @@ describe(`state groups`, () => {
453
453
  });
454
454
 
455
455
  /* The browser profiles' path is copied into @intentic/workspace-ignore (isBrowserProfilePath), which cannot
456
- * import this package it is the browser-safe half, deliberately free of zod and the contract surface, so
456
+ * import this package: it is the browser-safe half, deliberately free of zod and the contract surface, so
457
457
  * the platform's web bundle can take it. This is the guard that keeps the copy honest. If it fails, the
458
458
  * profiles moved and `BROWSER_PROFILE_GROUP` in _sandbox/workspace-ignore/src/constants.ts must move too, or
459
459
  * the tree will start eagerly walking a Chromium user-data dir and the watcher will report its churn. */
@@ -481,7 +481,7 @@ describe(`state groups`, () => {
481
481
  });
482
482
 
483
483
  /* WHAT THE OWNER'S MACHINE KEEPS. The sync ignored the whole state dir, so losing a sandbox lost every persona,
484
- * skill, automation, draft and transcript in it a backup that held only the source tree. These pin the two
484
+ * skill, automation, draft and transcript in it: a backup that held only the source tree. These pin the two
485
485
  * halves of the fix: the slice that now comes down, and the credentials that still must not. */
486
486
  describe(`BACKED_UP_STATE_PATHS`, () => {
487
487
  it(`splits the table in two with nothing falling between`, () => {
@@ -503,14 +503,14 @@ describe(`BACKED_UP_STATE_PATHS`, () => {
503
503
  }
504
504
  });
505
505
 
506
- it(`leaves the rebuildable bulk behind it is size, not secrecy`, () => {
506
+ it(`leaves the rebuildable bulk behind: it is size, not secrecy`, () => {
507
507
  for (const path of stateGroupPaths(`local`)) {
508
508
  expect([path, BACKED_UP_STATE_PATHS.includes(path)]).toEqual([path, false]);
509
509
  }
510
510
  });
511
511
 
512
512
  /* The distinction `portability` alone cannot draw, and the reason the `backup` flag exists. Ownership records
513
- * may never RESTORE into another sandbox that is what `identity` means, and members.json's own entry argues
513
+ * may never RESTORE into another sandbox: that is what `identity` means, and members.json's own entry argues
514
514
  * why. None of that stops the owner keeping a copy of who could drive their own machine. */
515
515
  it(`copies the ownership records that may never travel`, () => {
516
516
  for (const path of [`.intentic/identity/owner.json`, `.intentic/identity/members.json`, `.intentic/identity/workspace.json`]) {
@@ -520,7 +520,7 @@ describe(`BACKED_UP_STATE_PATHS`, () => {
520
520
 
521
521
  it(`still withholds the tokens that authenticate against this sandbox`, () => {
522
522
  expect(BACKED_UP_STATE_PATHS).not.toContain(`.intentic/identity/control-tokens.json`);
523
- // And it is the ONLY entry that opts out by hand everything else follows from its class.
523
+ // And it is the ONLY entry that opts out by hand: everything else follows from its class.
524
524
  expect(WORKSPACE_STATE_FILES.filter((file) => file.backup === false).map((file) => file.path)).toEqual([
525
525
  `.intentic/identity/control-tokens.json`,
526
526
  ]);