@bjoernrochel/sevro 0.1.0-rc.7 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/CONTRIBUTING.md +22 -6
  2. package/README.md +5 -1
  3. package/docs/README.md +19 -15
  4. package/docs/architecture.md +138 -17
  5. package/docs/assets/README.md +5 -4
  6. package/docs/builtin-graders-v1.md +17 -3
  7. package/docs/codex-host.md +155 -49
  8. package/docs/contracts.md +2 -1
  9. package/docs/creating-cases.md +9 -2
  10. package/docs/development-cli.md +187 -19
  11. package/docs/documentation-quality.md +12 -2
  12. package/docs/evaluation-workflow.md +110 -0
  13. package/docs/extension-protocol-v1.md +26 -3
  14. package/docs/getting-started.md +23 -2
  15. package/docs/guide-evaluation.md +33 -1
  16. package/docs/identity-v1.md +25 -2
  17. package/docs/installing.md +11 -11
  18. package/docs/native-controls.md +76 -5
  19. package/docs/native-hosts.md +196 -7
  20. package/docs/reading-results.md +102 -0
  21. package/docs/recipes.md +489 -0
  22. package/docs/releases.md +31 -12
  23. package/docs/report-v1.md +29 -1
  24. package/docs/results-v1.md +141 -4
  25. package/docs/run-settings-v1.md +147 -0
  26. package/docs/runtime-v1.md +307 -18
  27. package/docs/troubleshooting.md +12 -12
  28. package/docs/typescript-quality.md +89 -26
  29. package/examples/recipes/candidate.ts +26 -0
  30. package/examples/recipes/cases/coding-change.json +21 -0
  31. package/examples/recipes/cases/host-tools.json +21 -0
  32. package/examples/recipes/cases/mise.json +21 -0
  33. package/examples/recipes/cases/node.json +21 -0
  34. package/examples/recipes/cases/python-uv.json +21 -0
  35. package/examples/recipes/cases/semantic.json +17 -0
  36. package/examples/recipes/coding-change/check.ts +23 -0
  37. package/examples/recipes/extension-artifacts.ts +65 -0
  38. package/examples/recipes/extension-methods.ts +174 -0
  39. package/examples/recipes/extension.ts +37 -0
  40. package/examples/recipes/repository-skill/.agents/skills/startup-guide/SKILL.md +7 -0
  41. package/examples/recipes/repository-skill/.claude/skills/startup-guide/SKILL.md +7 -0
  42. package/examples/recipes/repository-skill/README.md +3 -0
  43. package/examples/recipes/run-inputs.ts +188 -0
  44. package/examples/recipes/run-options.ts +131 -0
  45. package/examples/recipes/run.ts +63 -0
  46. package/examples/recipes/runtime/empty.json +1 -0
  47. package/examples/recipes/runtime/goals.json +1 -0
  48. package/examples/recipes/runtime/mise.json +6 -0
  49. package/examples/recipes/runtime/path.json +1 -0
  50. package/examples/recipes/runtime/plugins.json +4 -0
  51. package/examples/recipes/runtime/python-uv.json +10 -0
  52. package/examples/recipes/semantic-judge.ts +29 -0
  53. package/examples/templates/coding/adapter.ts +17 -0
  54. package/examples/templates/coding/case.json +15 -0
  55. package/examples/templates/repository-skill/.agents/skills/startup-guide/SKILL.md +6 -0
  56. package/examples/templates/repository-skill/extension-data.ts +64 -0
  57. package/examples/templates/repository-skill/extension.ts +31 -0
  58. package/examples/templates/response/adapter.ts +12 -0
  59. package/examples/templates/response/case.json +13 -0
  60. package/package.json +11 -5
  61. package/schemas/report-v1.schema.json +9 -0
  62. package/schemas/run-evidence-v1.schema.json +2 -1
  63. package/schemas/runtime-v1.schema.json +63 -7
  64. package/src/advisory.ts +2 -2
  65. package/src/candidate-artifacts.ts +206 -0
  66. package/src/candidate-codex-state.ts +185 -0
  67. package/src/case-validation.ts +73 -50
  68. package/src/cli/cancellation.ts +20 -0
  69. package/src/cli/command.ts +87 -0
  70. package/src/cli/completion.ts +15 -0
  71. package/src/cli/demo.ts +60 -0
  72. package/src/cli/evaluation.ts +55 -0
  73. package/src/cli/extension.ts +79 -0
  74. package/src/cli/help.ts +198 -0
  75. package/src/cli/hosts.ts +55 -0
  76. package/src/cli/init-settings.ts +30 -0
  77. package/src/cli/init.ts +111 -0
  78. package/src/{cli-invocation.ts → cli/invocation.ts} +214 -218
  79. package/src/cli/loading.ts +151 -0
  80. package/src/cli/preparation.ts +154 -0
  81. package/src/cli/presentation.ts +93 -0
  82. package/src/cli/progress.ts +6 -0
  83. package/src/cli/run-manifest.ts +128 -0
  84. package/src/cli/run-options.ts +403 -0
  85. package/src/cli/run-overrides.ts +69 -0
  86. package/src/cli/settings.ts +154 -0
  87. package/src/cli/summary.ts +43 -0
  88. package/src/cli.ts +13 -591
  89. package/src/contract-types.ts +94 -0
  90. package/src/engine.ts +13 -5
  91. package/src/evaluation/advisory.ts +150 -0
  92. package/src/{evaluation-check-plan.ts → evaluation/check-plan.ts} +103 -67
  93. package/src/evaluation/collection.ts +88 -0
  94. package/src/evaluation/context.ts +10 -0
  95. package/src/{evaluation-grading-host.ts → evaluation/grading-host.ts} +5 -5
  96. package/src/evaluation/grading.ts +235 -0
  97. package/src/{evaluation-invocation-plan.ts → evaluation/invocation-plan.ts} +147 -58
  98. package/src/{evaluation-plan.ts → evaluation/plan.ts} +208 -141
  99. package/src/evaluation/progress.ts +19 -0
  100. package/src/evaluation/resources.ts +241 -0
  101. package/src/evaluation/run.ts +206 -0
  102. package/src/{evaluation-semantic.ts → evaluation/semantic.ts} +133 -115
  103. package/src/evaluation/trial.ts +800 -0
  104. package/src/evaluation/types.ts +207 -0
  105. package/src/evidence/host-results.ts +85 -0
  106. package/src/evidence/retained-artifacts.ts +18 -0
  107. package/src/evidence/trial-writer.ts +203 -0
  108. package/src/evidence/usage.ts +17 -0
  109. package/src/extension-client.ts +34 -96
  110. package/src/extension-session.ts +24 -48
  111. package/src/{advisory-fixture.ts → fixtures/advisory.ts} +15 -76
  112. package/src/{fixture-bin.ts → fixtures/bin.ts} +1 -1
  113. package/src/{fixture-cleanup.ts → fixtures/cleanup.ts} +13 -7
  114. package/src/fixtures/create.ts +183 -0
  115. package/src/{generated-fixture.ts → fixtures/generated.ts} +10 -53
  116. package/src/{git-hooks.ts → fixtures/git-hooks.ts} +1 -1
  117. package/src/{preparation.ts → fixtures/preparation.ts} +6 -16
  118. package/src/{repository-fixture.ts → fixtures/repository.ts} +30 -64
  119. package/src/{fixture-setup.ts → fixtures/setup.ts} +33 -51
  120. package/src/{fixture-tools.ts → fixtures/tools.ts} +1 -1
  121. package/src/fixtures/workspace-state.ts +212 -0
  122. package/src/generated/cli-result.d.cts +2 -1
  123. package/src/generated/extension.d.cts +2 -1
  124. package/src/generated/report.cjs +1 -1
  125. package/src/generated/report.d.cts +2 -1
  126. package/src/generated/run-evidence.cjs +1 -1
  127. package/src/generated/run-evidence.d.cts +2 -1
  128. package/src/generated/runtime.cjs +1 -1
  129. package/src/generated/runtime.d.cts +2 -1
  130. package/src/git.ts +93 -0
  131. package/src/graders/git-head.ts +29 -84
  132. package/src/graders/shell.ts +57 -68
  133. package/src/host-diagnostic-redaction.ts +52 -0
  134. package/src/host-diagnostics.ts +331 -0
  135. package/src/hosts/claude-continuation.ts +57 -40
  136. package/src/hosts/claude-credential.ts +160 -63
  137. package/src/hosts/claude-events.ts +34 -18
  138. package/src/hosts/claude-keychain.ts +78 -21
  139. package/src/hosts/claude-login-recovery.ts +85 -0
  140. package/src/hosts/claude-login-refresh.ts +178 -0
  141. package/src/hosts/claude-login-store.ts +162 -0
  142. package/src/hosts/claude-login.ts +107 -0
  143. package/src/hosts/claude-native-goal.ts +15 -12
  144. package/src/hosts/claude-process.ts +10 -50
  145. package/src/hosts/claude-refresh-lock.ts +81 -0
  146. package/src/hosts/claude-repository-invocation.ts +9 -34
  147. package/src/hosts/claude-settings.ts +91 -14
  148. package/src/hosts/claude-skill-activations.ts +169 -0
  149. package/src/hosts/claude-state.ts +46 -16
  150. package/src/hosts/claude-tool-calls.ts +13 -10
  151. package/src/hosts/claude.ts +122 -73
  152. package/src/hosts/codex-app-server.ts +78 -52
  153. package/src/hosts/codex-configuration.ts +5 -10
  154. package/src/hosts/codex-exec-controls.ts +516 -0
  155. package/src/hosts/codex-marketplace.ts +12 -10
  156. package/src/hosts/codex-native-calls.ts +15 -122
  157. package/src/hosts/codex-process.ts +10 -54
  158. package/src/hosts/codex-profile.ts +50 -21
  159. package/src/hosts/codex-session-files.ts +1 -9
  160. package/src/hosts/codex-state.ts +141 -19
  161. package/src/hosts/codex.ts +221 -179
  162. package/src/hosts/isolation-common.ts +15 -11
  163. package/src/hosts/isolation-roots.ts +13 -20
  164. package/src/hosts/linux-sandbox.ts +27 -3
  165. package/src/hosts/mac-sandbox.ts +111 -18
  166. package/src/hosts/native-controls.ts +72 -22
  167. package/src/hosts/native-request.ts +55 -0
  168. package/src/hosts/process-lifecycle.ts +221 -45
  169. package/src/hosts/protected-worktrees.ts +1 -1
  170. package/src/hosts/skill-hook-catalog.ts +131 -0
  171. package/src/hosts/skill-hook-handler.ts +374 -0
  172. package/src/hosts/skill-hooks.ts +577 -0
  173. package/src/instrumentation.ts +7 -8
  174. package/src/native-transcript-state.ts +22 -3
  175. package/src/native-transcript-view.ts +52 -13
  176. package/src/native-transcripts.ts +34 -15
  177. package/src/paths.ts +22 -0
  178. package/src/process-lifecycle.ts +150 -0
  179. package/src/provenance.ts +27 -7
  180. package/src/prune.ts +300 -0
  181. package/src/report.ts +63 -36
  182. package/src/resolved-case.ts +1 -1
  183. package/src/results-lock.ts +42 -0
  184. package/src/results.ts +14 -18
  185. package/src/run-owner.ts +4 -4
  186. package/src/{runtime-config.ts → runtime/config.ts} +182 -44
  187. package/src/{runtime-discovery-apple.ts → runtime/discovery-apple.ts} +3 -3
  188. package/src/{runtime-discovery-providers.ts → runtime/discovery-providers.ts} +3 -9
  189. package/src/{runtime-discovery.ts → runtime/discovery.ts} +4 -4
  190. package/src/{runtime-hook-processes.ts → runtime/hook-processes.ts} +1 -1
  191. package/src/{runtime-hooks.ts → runtime/hooks.ts} +33 -22
  192. package/src/{runtime-paths.ts → runtime/paths.ts} +6 -18
  193. package/src/{runtime-query.ts → runtime/query.ts} +5 -5
  194. package/src/{runtime-seeds.ts → runtime/seeds.ts} +3 -3
  195. package/src/runtime/shared-cache.ts +130 -0
  196. package/src/{runtime-state.ts → runtime/state.ts} +28 -11
  197. package/src/schema-types.ts +73 -161
  198. package/src/shell.ts +4 -0
  199. package/src/transcript-retention.ts +146 -0
  200. package/src/value-guards.ts +22 -0
  201. package/docs/assets/sevro-logo.png +0 -0
  202. package/docs/assets/sevro-symbol.svg +0 -1
  203. package/docs/assets/sevro-wordmark.svg +0 -1
  204. package/docs/cache-ownership-validation.md +0 -49
  205. package/docs/claude-continuation-validation.md +0 -47
  206. package/docs/concurrency-validation.md +0 -86
  207. package/docs/configuration-root-validation.md +0 -70
  208. package/docs/engine-slice.md +0 -114
  209. package/docs/evidence/runtime-startup-30/README.md +0 -185
  210. package/docs/evidence/runtime-startup-30/after.json +0 -319
  211. package/docs/evidence/runtime-startup-30/baseline-timeout.jsonl +0 -2
  212. package/docs/evidence/runtime-startup-30/before.json +0 -403
  213. package/docs/evidence/runtime-startup-30/consumer-smoke.json +0 -63
  214. package/docs/evidence/runtime-startup-30/full-policy-diagnostic.jsonl +0 -3
  215. package/docs/evidence/runtime-startup-30/minimal-policy-diagnostic.jsonl +0 -3
  216. package/docs/evidence/runtime-startup-30/plugin-template-assessment.json +0 -20
  217. package/docs/evidence/runtime-startup-30/seedless-timeout.jsonl +0 -2
  218. package/docs/evidence.md +0 -17
  219. package/docs/fixture-cleanup-validation.md +0 -70
  220. package/docs/guide-verification.json +0 -239
  221. package/src/evaluation-context.ts +0 -257
  222. package/src/evaluation-fixture.ts +0 -313
  223. package/src/evaluation-run.ts +0 -198
  224. package/src/evaluation-trial.ts +0 -946
  225. package/src/evaluation-types.ts +0 -149
  226. package/src/hosts/claude-nested-skills.ts +0 -272
  227. package/src/hosts/codex-command-output.ts +0 -510
  228. package/src/hosts/codex-executor-command.ts +0 -291
  229. package/src/hosts/codex-skill-reads.ts +0 -609
  230. /package/src/{trial-scheduler.ts → evaluation/scheduler.ts} +0 -0
@@ -1,8 +1,19 @@
1
- import { runtimeSeedSources } from "../runtime-config";
1
+ import { runtimeLocalHttpEnabled, runtimeSeedSources } from "../runtime/config";
2
+ import { shellQuote } from "../shell";
3
+ import {
4
+ candidateArtifactEnvironment,
5
+ candidateArtifactReadRoots,
6
+ } from "../candidate-artifacts";
7
+ import {
8
+ codexStateEnabled,
9
+ retainCandidateCodexState,
10
+ } from "../candidate-codex-state";
2
11
  import { randomUUID } from "node:crypto";
3
12
  import { existsSync } from "node:fs";
13
+ import { isMissingFile } from "../fixtures/tools";
4
14
  import {
5
15
  cp,
16
+ lstat,
6
17
  mkdir,
7
18
  readFile,
8
19
  realpath,
@@ -17,12 +28,17 @@ import {
17
28
  candidateTranscriptEnvironment,
18
29
  candidateTranscriptRoots,
19
30
  } from "../native-transcripts";
20
- import { prepareHookMounts } from "../runtime-hooks";
31
+ import { prepareHookMounts } from "../runtime/hooks";
32
+ import { requireNativeHookRoots } from "../native-transcript-state";
21
33
  import {
22
34
  requireRuntimeReadRoots,
23
35
  runtimeExecutableProtection,
24
- } from "../runtime-paths";
25
- import { prepareRuntimeState } from "../runtime-state";
36
+ } from "../runtime/paths";
37
+ import { prepareRuntimeState } from "../runtime/state";
38
+ import {
39
+ sharedCacheDenyRoots,
40
+ sharedCacheRoots,
41
+ } from "../runtime/shared-cache";
26
42
  import { codexPermissionProfile } from "./codex-profile";
27
43
  import { evaluationProtectedRoots } from "./isolation-roots";
28
44
  type Request = Parameters<HostAdapter["run"]>[0];
@@ -40,6 +56,7 @@ export interface CodexHostOptions {
40
56
  agentConcurrencyLimit?: number | null;
41
57
  timeoutMs?: number;
42
58
  entrypoint?: "exec" | "app-server";
59
+ retainHostDiagnostics?: boolean;
43
60
  /** Integration-test seam; production uses the same CLI for execution and preflight. */
44
61
  sandboxBinary?: string;
45
62
  }
@@ -60,23 +77,22 @@ async function copyAuth(source: string, target: string): Promise<void> {
60
77
  await writeFile(target, bytes, { flag: "wx", mode: 0o600 });
61
78
  }
62
79
 
63
- function shellQuote(value: string): string {
64
- return `'${value.replaceAll("'", `'"'"'`)}'`;
65
- }
66
-
67
80
  export async function codexHookMounts(
68
81
  request: Request,
69
82
  state: CodexState,
70
83
  installedPluginRoots: string[],
71
84
  ) {
72
- if (!request.runtimePolicy) return undefined;
73
85
  const hooks = await prepareHookMounts({
74
86
  workspace: request.workspace,
75
- stateRoot: join(request.workspace, ".git", "sevro-runtime"),
87
+ stateRoot: state.stateRoot,
76
88
  privateRoot: state.codexHome,
77
89
  pluginRoots: installedPluginRoots,
78
90
  protectedRoots: state.protectedRoots,
79
- policy: request.runtimePolicy,
91
+ policy: request.runtimePolicy ?? {
92
+ format: "sevro.runtime.v1",
93
+ environment: {},
94
+ readOnlyRoots: [],
95
+ },
80
96
  });
81
97
  for (const [index, root] of installedPluginRoots.entries()) {
82
98
  const curated = hooks.directories[index];
@@ -84,9 +100,28 @@ export async function codexHookMounts(
84
100
  await rm(root, { recursive: true });
85
101
  await cp(curated, root, { recursive: true });
86
102
  }
103
+ await grantCodexHookReads(state, hooks.directories);
87
104
  return hooks;
88
105
  }
89
106
 
107
+ /** Hooks advertise paths relative to these curated copies, not the install cache. */
108
+ async function grantCodexHookReads(
109
+ state: CodexState,
110
+ roots: string[],
111
+ ): Promise<void> {
112
+ if (roots.length === 0) return;
113
+ await requireNativeHookRoots(roots, state.codexHome);
114
+ const path = join(state.codexHome, "config.toml");
115
+ const config = await readFile(path, "utf8");
116
+ const header = `[permissions.${state.profileId}.filesystem]\n`;
117
+ const grants = roots
118
+ .map((root) => `${JSON.stringify(root)} = "read"\n`)
119
+ .join("");
120
+ await writeFile(path, config.replace(header, header + grants), {
121
+ mode: 0o600,
122
+ });
123
+ }
124
+
90
125
  async function privateCodexDirectories(stateRoot: string) {
91
126
  const codexHome = join(stateRoot, "codex-home"),
92
127
  parentHome = join(stateRoot, "home"),
@@ -141,9 +176,14 @@ async function writeCodexProfile(
141
176
  protectedRoots: string[],
142
177
  limit: number | null,
143
178
  commandEnvironment = request.runtimePolicy?.environment,
144
- runtimeWriteRoot?: string,
179
+ runtimeWriteRoot: string | undefined,
180
+ writableCacheRoots: string[],
145
181
  ): Promise<void> {
146
182
  const profile = codexPermissionProfile({
183
+ localHttp: runtimeLocalHttpEnabled(
184
+ request.runtimePolicy,
185
+ request.runtimeRole ?? "candidate",
186
+ ),
147
187
  commandEnvironment: codexProfileEnvironment(
148
188
  request,
149
189
  paths,
@@ -152,6 +192,10 @@ async function writeCodexProfile(
152
192
  runtimeWriteRoot,
153
193
  runtimeReadRoots: request.runtimePolicy?.readOnlyRoots,
154
194
  nativeReadRoots: codexNativeReadRoots(request, paths.codexHome),
195
+ nativeWriteRoots: [
196
+ ...codexStateWriteRoots(request, paths),
197
+ ...writableCacheRoots,
198
+ ],
155
199
  id: profileId,
156
200
  workspace: request.workspace,
157
201
  commandHome: paths.commandHome,
@@ -162,11 +206,17 @@ async function writeCodexProfile(
162
206
  });
163
207
  await writeFile(
164
208
  join(paths.codexHome, "config.toml"),
165
- profile + concurrencyConfiguration(limit),
209
+ profile +
210
+ concurrencyConfiguration(limit) +
211
+ fixtureTrustConfiguration(request),
166
212
  { flag: "wx", mode: 0o600 },
167
213
  );
168
214
  }
169
215
 
216
+ function fixtureTrustConfiguration(request: Request): string {
217
+ return `\n[projects.${JSON.stringify(request.workspace)}]\ntrust_level = "untrusted"\n`;
218
+ }
219
+
170
220
  function codexProfileEnvironment(
171
221
  request: Request,
172
222
  paths: Awaited<ReturnType<typeof privateCodexDirectories>>,
@@ -176,6 +226,12 @@ function codexProfileEnvironment(
176
226
  ...codexCommandEnvironment(request, environment),
177
227
  ...codexTranscriptEnvironment(request, paths.codexHome),
178
228
  ...candidateTranscriptEnvironment(request.candidateTranscriptRoot),
229
+ ...candidateArtifactEnvironment(request.candidateArtifactRoots),
230
+ ...(request.runtimePolicy?.codexState === true
231
+ ? {
232
+ SEVRO_CODEX_AGENT_SKILLS: join(paths.parentHome, ".agents", "skills"),
233
+ }
234
+ : {}),
179
235
  };
180
236
  }
181
237
 
@@ -183,16 +239,26 @@ function codexTranscriptEnvironment(
183
239
  request: Request,
184
240
  home: string,
185
241
  ): Record<string, string> {
186
- return request.runtimePolicy?.nativeTranscripts === true
187
- ? { CODEX_HOME: home, SEVRO_NATIVE_TRANSCRIPT_ROOT: join(home, "sessions") }
188
- : {};
242
+ return {
243
+ ...(request.runtimePolicy?.codexState === true ? { CODEX_HOME: home } : {}),
244
+ ...(request.runtimePolicy?.nativeTranscripts === true
245
+ ? {
246
+ CODEX_HOME: home,
247
+ SEVRO_NATIVE_TRANSCRIPT_ROOT: join(home, "sessions"),
248
+ }
249
+ : {}),
250
+ };
189
251
  }
190
252
 
191
253
  function codexNativeReadRoots(request: Request, home: string) {
192
254
  return [
193
255
  ...linuxArg0ReadRoots(home),
256
+ ...(request.runtimePolicy?.codexState === true
257
+ ? [join(home, "config.toml"), join(home, "skills", ".system")]
258
+ : []),
194
259
  ...codexTranscriptRoots(request, home),
195
260
  ...candidateTranscriptRoots(request.candidateTranscriptRoot),
261
+ ...candidateArtifactReadRoots(request.candidateArtifactRoots),
196
262
  ];
197
263
  }
198
264
 
@@ -293,7 +359,7 @@ async function codexRuntime(
293
359
  request.workspace,
294
360
  request.runtimePolicy,
295
361
  request.runtimeRole ?? "candidate",
296
- join(stateRoot, "runtime"),
362
+ request.candidateRuntimeRoot ?? join(stateRoot, "runtime"),
297
363
  );
298
364
  paths.commandHome = runtime.home;
299
365
  paths.commandTemp = runtime.temp;
@@ -315,7 +381,11 @@ function applyCodexRuntime(
315
381
  function codexRuntimeProfile(
316
382
  runtime: Awaited<ReturnType<typeof codexRuntime>>,
317
383
  ) {
318
- return { environment: runtime?.environment, root: runtime?.root };
384
+ return {
385
+ environment: runtime?.environment,
386
+ root: runtime?.root,
387
+ writableCacheRoots: runtime?.writableCacheRoots ?? [],
388
+ };
319
389
  }
320
390
 
321
391
  export async function prepareCodexState(
@@ -325,6 +395,7 @@ export async function prepareCodexState(
325
395
  request: Request,
326
396
  helperRoot?: string,
327
397
  ) {
398
+ await requireIsolatedGitMetadata(request.workspace);
328
399
  const paths = await privateCodexDirectories(stateRoot);
329
400
  const runtime = await codexRuntime(request, paths, stateRoot);
330
401
  await copyAuth(options.authFile, join(paths.codexHome, "auth.json"));
@@ -350,6 +421,14 @@ export async function prepareCodexState(
350
421
  );
351
422
  if (helperRoot) readRoots.push(helperRoot);
352
423
  requireCodexRuntimeReadRoots(request, protectedRoots);
424
+ await sharedCacheRoots(
425
+ request.runtimePolicy,
426
+ request.runtimeRole,
427
+ protectedRoots,
428
+ );
429
+ protectedRoots.push(
430
+ ...sharedCacheDenyRoots(request.runtimePolicy, request.runtimeRole),
431
+ );
353
432
  requireUnprotectedExecutables(
354
433
  readRoots,
355
434
  runtimeExecutableProtection(
@@ -369,11 +448,54 @@ export async function prepareCodexState(
369
448
  limit,
370
449
  runtimeProfile.environment,
371
450
  runtimeProfile.root,
451
+ runtimeProfile.writableCacheRoots,
372
452
  );
373
453
  const shellRoot = await fixtureShellRoot(request, paths.parentHome);
374
454
  const env = codexEnvironment(paths, request, shellRoot);
375
455
  applyCodexRuntime(runtime, env, paths);
376
- return { ...paths, sandboxBinary, profileId, env, protectedRoots };
456
+ await prepareCodexStateEvidence(request, stateRoot, paths);
457
+ return { ...paths, stateRoot, sandboxBinary, profileId, env, protectedRoots };
458
+ }
459
+
460
+ function codexStateWriteRoots(
461
+ request: Request,
462
+ paths: Awaited<ReturnType<typeof privateCodexDirectories>>,
463
+ ): string[] {
464
+ return codexStateEnabled(request.runtimePolicy)
465
+ ? [
466
+ join(paths.codexHome, "skills"),
467
+ join(paths.parentHome, ".agents", "skills"),
468
+ join(paths.commandHome, ".agents", "skills"),
469
+ join(paths.commandHome, ".codex", "skills"),
470
+ ]
471
+ : [];
472
+ }
473
+
474
+ async function prepareCodexStateEvidence(
475
+ request: Request,
476
+ stateRoot: string,
477
+ paths: Awaited<ReturnType<typeof privateCodexDirectories>>,
478
+ ): Promise<void> {
479
+ if (!codexStateEnabled(request.runtimePolicy)) return;
480
+ const skillRoots = codexStateWriteRoots(request, paths);
481
+ await Promise.all(
482
+ skillRoots.map((root) => mkdir(root, { recursive: true, mode: 0o700 })),
483
+ );
484
+ await mkdir(join(paths.codexHome, "skills", ".system"), { mode: 0o700 });
485
+ await retainCandidateCodexState(
486
+ request.candidateRuntimeRoot,
487
+ stateRoot,
488
+ paths,
489
+ );
490
+ }
491
+
492
+ async function requireIsolatedGitMetadata(workspace: string): Promise<void> {
493
+ try {
494
+ if (!(await lstat(join(workspace, ".git"))).isDirectory())
495
+ throw new Error("Codex Git metadata must be an isolated directory");
496
+ } catch (cause) {
497
+ if (!isMissingFile(cause)) throw cause;
498
+ }
377
499
  }
378
500
 
379
501
  function requireCodexRuntimeReadRoots(