velocious 1.0.604 → 1.0.605

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 (49) hide show
  1. package/README.md +19 -10
  2. package/build/cli/commands/test/timing-manifest/merge.js +14 -0
  3. package/build/environment-handlers/base.js +9 -0
  4. package/build/environment-handlers/node/cli/commands/test/timing-manifest/merge.js +108 -0
  5. package/build/environment-handlers/node/cli/commands/test.js +48 -7
  6. package/build/environment-handlers/node.js +10 -0
  7. package/build/src/cli/commands/test/timing-manifest/merge.d.ts +10 -0
  8. package/build/src/cli/commands/test/timing-manifest/merge.d.ts.map +1 -0
  9. package/build/src/cli/commands/test/timing-manifest/merge.js +13 -0
  10. package/build/src/environment-handlers/base.d.ts +6 -0
  11. package/build/src/environment-handlers/base.d.ts.map +1 -1
  12. package/build/src/environment-handlers/base.js +9 -1
  13. package/build/src/environment-handlers/node/cli/commands/test/timing-manifest/merge.d.ts +32 -0
  14. package/build/src/environment-handlers/node/cli/commands/test/timing-manifest/merge.d.ts.map +1 -0
  15. package/build/src/environment-handlers/node/cli/commands/test/timing-manifest/merge.js +97 -0
  16. package/build/src/environment-handlers/node/cli/commands/test.d.ts +3 -3
  17. package/build/src/environment-handlers/node/cli/commands/test.d.ts.map +1 -1
  18. package/build/src/environment-handlers/node/cli/commands/test.js +36 -9
  19. package/build/src/environment-handlers/node.d.ts +6 -0
  20. package/build/src/environment-handlers/node.d.ts.map +1 -1
  21. package/build/src/environment-handlers/node.js +10 -1
  22. package/build/src/testing/test-profile-output.d.ts +11 -0
  23. package/build/src/testing/test-profile-output.d.ts.map +1 -1
  24. package/build/src/testing/test-profile-output.js +21 -7
  25. package/build/src/testing/test-profiler.d.ts.map +1 -1
  26. package/build/src/testing/test-profiler.js +3 -2
  27. package/build/src/testing/test-suite-splitter.d.ts +33 -0
  28. package/build/src/testing/test-suite-splitter.d.ts.map +1 -1
  29. package/build/src/testing/test-suite-splitter.js +84 -8
  30. package/build/src/testing/timing-manifest.d.ts +94 -0
  31. package/build/src/testing/timing-manifest.d.ts.map +1 -0
  32. package/build/src/testing/timing-manifest.js +284 -0
  33. package/build/testing/test-profile-output.js +23 -8
  34. package/build/testing/test-profiler.js +2 -1
  35. package/build/testing/test-suite-splitter.js +91 -7
  36. package/build/testing/timing-manifest.js +346 -0
  37. package/build/tsconfig.tsbuildinfo +1 -1
  38. package/package.json +1 -1
  39. package/scripts/bootstrap-provider-runtime.sh +249 -0
  40. package/scripts/verify-docker-dev-environment.js +219 -29
  41. package/src/cli/commands/test/timing-manifest/merge.js +14 -0
  42. package/src/environment-handlers/base.js +9 -0
  43. package/src/environment-handlers/node/cli/commands/test/timing-manifest/merge.js +108 -0
  44. package/src/environment-handlers/node/cli/commands/test.js +48 -7
  45. package/src/environment-handlers/node.js +10 -0
  46. package/src/testing/test-profile-output.js +23 -8
  47. package/src/testing/test-profiler.js +2 -1
  48. package/src/testing/test-suite-splitter.js +91 -7
  49. package/src/testing/timing-manifest.js +346 -0
@@ -0,0 +1,249 @@
1
+ #!/usr/bin/env bash
2
+ # Bootstrap durable provider homes and immutable agent-context discovery links.
3
+ set -euo pipefail
4
+
5
+ fail() {
6
+ printf 'Provider runtime bootstrap failed: %s\n' "$1" >&2
7
+ exit 1
8
+ }
9
+
10
+ if (($# < 4)); then
11
+ fail "usage: $0 <home> <provider-runtime> <agent-context> <command> [args...]"
12
+ fi
13
+
14
+ dev_home=$1
15
+ provider_runtime=$2
16
+ agent_context=$3
17
+ shift 3
18
+
19
+ for required_path in "$dev_home" "$provider_runtime" "$agent_context"; do
20
+ if [[ $required_path != /* ]]; then
21
+ fail "Required path must be absolute: $required_path"
22
+ fi
23
+
24
+ if [[ ! -d $required_path ]]; then
25
+ fail "Required directory does not exist: $required_path"
26
+ fi
27
+ done
28
+
29
+ if [[ ! -w $dev_home ]]; then
30
+ fail "Development home is not writable: $dev_home"
31
+ fi
32
+
33
+ if [[ ! -w $provider_runtime ]]; then
34
+ fail "Provider runtime is not writable: $provider_runtime"
35
+ fi
36
+
37
+ if [[ ! -f $agent_context/AGENTS.md || ! -d $agent_context/skills ]]; then
38
+ fail "Agent context must contain AGENTS.md and skills/: $agent_context"
39
+ fi
40
+
41
+ umask 077
42
+ backup_directory=
43
+
44
+ ensure_writable_parent() {
45
+ local parent_path=$1
46
+
47
+ if [[ -L $parent_path ]]; then
48
+ fail "Required parent directory must be lane-local, not a symlink: $parent_path"
49
+ fi
50
+
51
+ if [[ -e $parent_path && ! -d $parent_path ]]; then
52
+ fail "Required parent path is not a directory: $parent_path"
53
+ fi
54
+
55
+ if [[ ! -d $parent_path ]] && ! mkdir -p -- "$parent_path"; then
56
+ fail "Could not create required parent directory; verify ownership: $parent_path"
57
+ fi
58
+
59
+ if [[ ! -w $parent_path ]]; then
60
+ fail "Required parent directory is not writable; verify ownership: $parent_path"
61
+ fi
62
+ }
63
+
64
+ backup_relative_path() {
65
+ local target_path=$1
66
+
67
+ case $target_path in
68
+ "$dev_home"/*)
69
+ printf '%s\n' "${target_path#"$dev_home"/}"
70
+ ;;
71
+ "$provider_runtime"/*)
72
+ printf '%s\n' "provider-runtime/${target_path#"$provider_runtime"/}"
73
+ ;;
74
+ *)
75
+ fail "Refusing to migrate a path outside the development home or provider runtime: $target_path"
76
+ ;;
77
+ esac
78
+ }
79
+
80
+ preserve_conflict() {
81
+ local target_path=$1
82
+ local relative_path
83
+ local backup_path
84
+
85
+ if [[ -z $backup_directory ]]; then
86
+ ensure_writable_parent "$dev_home/.provider-runtime-migration-backups"
87
+
88
+ if ! backup_directory=$(mktemp -d "$dev_home/.provider-runtime-migration-backups/$(date -u +%Y%m%dT%H%M%SZ)-XXXXXX"); then
89
+ fail "Could not create a timestamped migration backup under $dev_home/.provider-runtime-migration-backups"
90
+ fi
91
+ fi
92
+
93
+ relative_path=$(backup_relative_path "$target_path")
94
+ backup_path="$backup_directory/$relative_path"
95
+
96
+ if [[ -e $backup_path || -L $backup_path ]]; then
97
+ fail "Migration backup target already exists: $backup_path"
98
+ fi
99
+
100
+ if ! mkdir -p -- "$(dirname -- "$backup_path")"; then
101
+ fail "Could not create migration backup parent: $(dirname -- "$backup_path")"
102
+ fi
103
+
104
+ if ! mv -- "$target_path" "$backup_path"; then
105
+ fail "Could not preserve conflicting path at $backup_path: $target_path"
106
+ fi
107
+
108
+ printf 'Provider runtime bootstrap preserved %s at %s\n' "$target_path" "$backup_path" >&2
109
+ }
110
+
111
+ ensure_real_directory() {
112
+ local directory_path=$1
113
+
114
+ ensure_writable_parent "$(dirname -- "$directory_path")"
115
+
116
+ if [[ -L $directory_path || (-e $directory_path && ! -d $directory_path) ]]; then
117
+ preserve_conflict "$directory_path"
118
+ fi
119
+
120
+ if [[ ! -d $directory_path ]] && ! mkdir -- "$directory_path"; then
121
+ fail "Could not create required directory: $directory_path"
122
+ fi
123
+
124
+ if [[ ! -w $directory_path ]]; then
125
+ fail "Required directory is not writable; verify ownership: $directory_path"
126
+ fi
127
+ }
128
+
129
+ ensure_exact_link() {
130
+ local target_path=$1
131
+ local source_path=$2
132
+
133
+ ensure_writable_parent "$(dirname -- "$target_path")"
134
+
135
+ if [[ -L $target_path ]] && [[ $(readlink -- "$target_path") == "$source_path" ]]; then
136
+ return
137
+ fi
138
+
139
+ if [[ -e $target_path || -L $target_path ]]; then
140
+ preserve_conflict "$target_path"
141
+ fi
142
+
143
+ if ! ln -s -- "$source_path" "$target_path"; then
144
+ fail "Could not create required symlink: $target_path -> $source_path"
145
+ fi
146
+ }
147
+
148
+ require_real_runtime_directory() {
149
+ local directory_path=$1
150
+
151
+ if [[ -L $directory_path || ! -d $directory_path ]]; then
152
+ fail "Provider runtime directory must be a real directory: $directory_path"
153
+ fi
154
+ }
155
+
156
+ require_exact_runtime_alias() {
157
+ local alias_path=$1
158
+ local expected_link=$2
159
+ local resolved_directory=$3
160
+ local resolved_alias_path
161
+ local resolved_directory_path
162
+
163
+ if [[ ! -L $alias_path ]] || [[ $(readlink -- "$alias_path") != "$expected_link" ]]; then
164
+ fail "Provider runtime alias must be exact: $alias_path -> $expected_link"
165
+ fi
166
+
167
+ if [[ -L $resolved_directory || ! -d $resolved_directory ]]; then
168
+ fail "Provider runtime alias target must be an existing real directory: $resolved_directory"
169
+ fi
170
+
171
+ if ! resolved_alias_path=$(realpath -e -- "$alias_path"); then
172
+ fail "Provider runtime alias does not resolve: $alias_path"
173
+ fi
174
+
175
+ if ! resolved_directory_path=$(realpath -e -- "$resolved_directory"); then
176
+ fail "Provider runtime alias target does not resolve: $resolved_directory"
177
+ fi
178
+
179
+ if [[ $resolved_alias_path != "$resolved_directory_path" ]]; then
180
+ fail "Provider runtime alias must resolve to $resolved_directory: $alias_path"
181
+ fi
182
+
183
+ if [[ ! -w $resolved_directory ]]; then
184
+ fail "Provider runtime alias target is not writable; verify ownership: $resolved_directory"
185
+ fi
186
+ }
187
+
188
+ validate_runtime_aliases() {
189
+ require_real_runtime_directory "$provider_runtime/.local"
190
+ require_real_runtime_directory "$provider_runtime/.local/share"
191
+ require_exact_runtime_alias "$provider_runtime/.codex" "codex" "$provider_runtime/codex"
192
+ require_exact_runtime_alias "$provider_runtime/.kimi-code" "kimi-code" "$provider_runtime/kimi-code"
193
+ require_exact_runtime_alias "$provider_runtime/.opencode" "opencode" "$provider_runtime/opencode"
194
+ require_exact_runtime_alias "$provider_runtime/.local/share/opencode" "../../opencode" "$provider_runtime/opencode"
195
+ }
196
+
197
+ validate_runtime_aliases
198
+
199
+ if ! exec {provider_lock_fd}<"$provider_runtime"; then
200
+ fail "Could not open the provider runtime for shared-link locking: $provider_runtime"
201
+ fi
202
+
203
+ if ! flock --exclusive "$provider_lock_fd"; then
204
+ fail "Could not lock shared provider links: $provider_runtime"
205
+ fi
206
+
207
+ validate_runtime_aliases
208
+
209
+ for provider_home in \
210
+ "$provider_runtime/codex" \
211
+ "$provider_runtime/kimi-code"; do
212
+ ensure_exact_link "$provider_home/AGENTS.md" "$agent_context/AGENTS.md"
213
+ ensure_exact_link "$provider_home/CLAUDE.md" "$agent_context/AGENTS.md"
214
+ ensure_exact_link "$provider_home/skills" "$agent_context/skills"
215
+ done
216
+
217
+ if ! flock --unlock "$provider_lock_fd"; then
218
+ fail "Could not unlock shared provider links: $provider_runtime"
219
+ fi
220
+
221
+ exec {provider_lock_fd}<&-
222
+
223
+ for lane_parent in \
224
+ "$dev_home/.config" \
225
+ "$dev_home/.local" \
226
+ "$dev_home/.local/share" \
227
+ "$dev_home/.local/state" \
228
+ "$dev_home/.cache"; do
229
+ ensure_writable_parent "$lane_parent"
230
+ done
231
+
232
+ for lane_directory in \
233
+ "$dev_home/.config/opencode" \
234
+ "$dev_home/.local/share/opencode" \
235
+ "$dev_home/.local/state/opencode" \
236
+ "$dev_home/.cache/opencode"; do
237
+ ensure_real_directory "$lane_directory"
238
+ done
239
+
240
+ ensure_exact_link "$dev_home/.codex" "$provider_runtime/.codex"
241
+ ensure_exact_link "$dev_home/.kimi-code" "$provider_runtime/.kimi-code"
242
+ ensure_exact_link "$dev_home/.opencode" "$provider_runtime/.opencode"
243
+ ensure_exact_link "$dev_home/.local/share/opencode/auth.json" "$provider_runtime/.local/share/opencode/auth.json"
244
+
245
+ ensure_exact_link "$dev_home/.config/opencode/AGENTS.md" "$agent_context/AGENTS.md"
246
+ ensure_exact_link "$dev_home/.config/opencode/CLAUDE.md" "$agent_context/AGENTS.md"
247
+ ensure_exact_link "$dev_home/.config/opencode/skills" "$agent_context/skills"
248
+
249
+ exec "$@"
@@ -3,10 +3,11 @@
3
3
  * Static contract verifier for the canonical Docker development environment.
4
4
  *
5
5
  * Dependency-free: checks the tracked Dockerfile, compose.yml, .env.example,
6
- * .dockerignore, .gitignore, and scripts/docker-run.sh against the canonical
7
- * development-environment contract, then runs negative probes derived from
8
- * the real files to prove the checks actually catch regressions (for example
9
- * a standalone project `npm install` or a `COPY` of project source).
6
+ * .dockerignore, .gitignore, and development bootstrap scripts against the
7
+ * canonical development-environment contract, then runs negative probes
8
+ * derived from the real files to prove the checks actually catch regressions
9
+ * (for example a standalone project `npm install` or a `COPY` of project
10
+ * source).
10
11
  */
11
12
 
12
13
  import fs from "node:fs"
@@ -280,7 +281,8 @@ function composeServiceNames(content) {
280
281
  }
281
282
 
282
283
  /**
283
- * Extracts the volume entries of the first service-level volumes block.
284
+ * Extracts normalized volume entries from the first service-level volumes
285
+ * block.
284
286
  *
285
287
  * @param {string} content - The compose.yml content.
286
288
  * @returns {string[]} - The volume entries.
@@ -289,6 +291,8 @@ function composeServiceVolumes(content) {
289
291
  const lines = content.split("\n")
290
292
  const volumes = []
291
293
  let volumesIndent = null
294
+ /** @type {string[] | null} */
295
+ let currentVolume = null
292
296
 
293
297
  for (const line of lines) {
294
298
  if (volumesIndent === null) {
@@ -301,16 +305,27 @@ function composeServiceVolumes(content) {
301
305
 
302
306
  const entryMatch = line.match(/^(\s+)-\s+(.+?)\s*$/u)
303
307
 
304
- if (entryMatch && entryMatch[1].length > volumesIndent) {
305
- volumes.push(entryMatch[2])
308
+ if (entryMatch && entryMatch[1].length == volumesIndent + 2) {
309
+ if (currentVolume) volumes.push(currentVolume.join("\n"))
310
+
311
+ currentVolume = [`- ${entryMatch[2]}`]
306
312
  continue
307
313
  }
308
314
 
309
315
  if (line.trim() == "" || line.trimStart().startsWith("#")) continue
310
316
 
317
+ const indentation = line.match(/^\s*/u)?.[0].length || 0
318
+
319
+ if (currentVolume && indentation > volumesIndent + 2) {
320
+ currentVolume.push(line.trim())
321
+ continue
322
+ }
323
+
311
324
  break
312
325
  }
313
326
 
327
+ if (currentVolume) volumes.push(currentVolume.join("\n"))
328
+
314
329
  return volumes
315
330
  }
316
331
 
@@ -337,10 +352,6 @@ function verifyCompose(content) {
337
352
  problems.push(`compose.yml must not contain credential values (matched: ${content.match(COMPOSE_CREDENTIAL_VALUE_PATTERN)?.[0].trim()})`)
338
353
  }
339
354
 
340
- if (/threadwire/iu.test(content)) {
341
- problems.push("compose.yml must not couple the development service to Threadwire")
342
- }
343
-
344
355
  if (!/^\s+init:\s*true\s*$/mu.test(content)) {
345
356
  problems.push("The dev service must set init: true")
346
357
  }
@@ -353,24 +364,44 @@ function verifyCompose(content) {
353
364
  problems.push("The dev service must set working_dir /home/dev/velocious")
354
365
  }
355
366
 
356
- for (const envLine of ["GH_CONFIG_DIR: /home/dev/.config/gh", "HOME: /home/dev", "NODE_ENV: development"]) {
367
+ for (const envLine of [
368
+ "GH_CONFIG_DIR: /home/dev/.config/gh",
369
+ "HOME: /home/dev",
370
+ "KIMI_CODE_HOME: /home/dev/.kimi-code",
371
+ "NODE_ENV: development",
372
+ "THREADWIRE_CODEX_BIN: /usr/local/bin/codex",
373
+ "THREADWIRE_KIMI_BIN: /usr/local/bin/kimi",
374
+ "THREADWIRE_OPENCODE_BIN: /usr/local/bin/opencode"
375
+ ]) {
357
376
  if (!content.includes(envLine)) {
358
377
  problems.push(`The dev service is missing environment entry: ${envLine}`)
359
378
  }
360
379
  }
361
380
 
362
381
  const expectedVolumes = [
363
- "${DEV_HOME_PATH:-/home/dev}:/home/dev",
364
- "${GH_CONFIG_SOURCE_PATH:?Set GH_CONFIG_SOURCE_PATH in .env}:/home/dev/.config/gh:ro"
382
+ "- type: bind\nsource: ${DEV_HOME_PATH:-/home/dev}\ntarget: /home/dev\nbind:\ncreate_host_path: false",
383
+ "- type: bind\nsource: ${AI_PROVIDER_RUNTIME_SOURCE_PATH:?Set AI_PROVIDER_RUNTIME_SOURCE_PATH in .env}\ntarget: /opt/hermes-dind-shared/auth/provider-runtime\nbind:\ncreate_host_path: false",
384
+ "- type: bind\nsource: ${AGENT_CONTEXT_SOURCE_PATH:?Set AGENT_CONTEXT_SOURCE_PATH in .env}\ntarget: /opt/hermes-agent-context\nread_only: true\nbind:\ncreate_host_path: false",
385
+ "- type: bind\nsource: ${GH_CONFIG_SOURCE_PATH:?Set GH_CONFIG_SOURCE_PATH in .env}\ntarget: /home/dev/.config/gh\nread_only: true\nbind:\ncreate_host_path: false"
365
386
  ]
366
387
  const actualVolumes = composeServiceVolumes(content)
367
388
 
368
389
  if (actualVolumes.length != expectedVolumes.length || !expectedVolumes.every((volume) => actualVolumes.includes(volume))) {
369
- problems.push(`The dev service must mount exactly the development-home and read-only GH config binds (found: ${actualVolumes.join(", ") || "none"})`)
390
+ problems.push(`The dev service must mount exactly the development home, provider runtime, immutable agent context, and read-only GH config binds (found: ${actualVolumes.join(" | ") || "none"})`)
391
+ }
392
+
393
+ const expectedEntrypoint = " entrypoint: [\"/home/dev/velocious/scripts/bootstrap-provider-runtime.sh\", \"/home/dev\", \"/opt/hermes-dind-shared/auth/provider-runtime\", \"/opt/hermes-agent-context\"]"
394
+
395
+ if (!content.includes(expectedEntrypoint)) {
396
+ problems.push("The dev service must use the provider runtime bootstrap as its entrypoint so Compose command overrides remain bootstrapped")
370
397
  }
371
398
 
372
399
  if (!/^\s+command:\s*(\[\s*"sleep",\s*"infinity"\s*\]|sleep\s+infinity)\s*$/mu.test(content)) {
373
- problems.push("The dev service must run sleep infinity")
400
+ problems.push("The dev service must run sleep infinity by default")
401
+ }
402
+
403
+ if (/\.npmrc|NPM_CONFIG_USERCONFIG/iu.test(content)) {
404
+ problems.push("The normal dev service must not mount or configure npm credentials")
374
405
  }
375
406
 
376
407
  if (/container_name:/u.test(content)) {
@@ -401,6 +432,14 @@ function verifyEnvExample(content) {
401
432
  problems.push(".env.example must document GH_CONFIG_SOURCE_PATH")
402
433
  }
403
434
 
435
+ if (!content.includes("AI_PROVIDER_RUNTIME_SOURCE_PATH")) {
436
+ problems.push(".env.example must document AI_PROVIDER_RUNTIME_SOURCE_PATH")
437
+ }
438
+
439
+ if (!content.includes("AGENT_CONTEXT_SOURCE_PATH")) {
440
+ problems.push(".env.example must document AGENT_CONTEXT_SOURCE_PATH")
441
+ }
442
+
404
443
  if (!content.includes("DEV_HOME_PATH")) {
405
444
  problems.push(".env.example must document DEV_HOME_PATH")
406
445
  }
@@ -459,8 +498,79 @@ function verifyDockerRunSh(content) {
459
498
  problems.push("scripts/docker-run.sh must execute commands through docker compose run")
460
499
  }
461
500
 
462
- if (/threadwire/iu.test(content)) {
463
- problems.push("scripts/docker-run.sh must not reference Threadwire: it stays parent orchestration")
501
+ return problems
502
+ }
503
+
504
+ /**
505
+ * Verifies the documented source-path preflight.
506
+ *
507
+ * @param {string} content - The Docker development guide content.
508
+ * @returns {string[]} - The problems found.
509
+ */
510
+ function verifyDockerDevelopmentDocs(content) {
511
+ const problems = []
512
+
513
+ for (const containmentCheck of [
514
+ 'case "$agent_context_source/" in "$provider_runtime_source/"*) exit 1;; esac',
515
+ 'case "$provider_runtime_source/" in "$agent_context_source/"*) exit 1;; esac'
516
+ ]) {
517
+ if (!content.includes(containmentCheck)) {
518
+ problems.push(`Docker preflight must reject bidirectional provider-runtime/agent-context containment: ${containmentCheck}`)
519
+ }
520
+ }
521
+
522
+ return problems
523
+ }
524
+
525
+ /**
526
+ * Verifies that the provider bootstrap preserves the canonical runtime aliases.
527
+ *
528
+ * @param {string} content - The bootstrap script content.
529
+ * @returns {string[]} - The problems found.
530
+ */
531
+ function verifyProviderBootstrap(content) {
532
+ const problems = []
533
+ const firstAliasValidation = content.indexOf('require_exact_runtime_alias "$provider_runtime/.codex"')
534
+
535
+ for (const requiredParent of [
536
+ 'require_real_runtime_directory "$provider_runtime/.local"',
537
+ 'require_real_runtime_directory "$provider_runtime/.local/share"'
538
+ ]) {
539
+ const parentValidation = content.indexOf(requiredParent)
540
+
541
+ if (parentValidation == -1 || firstAliasValidation == -1 || parentValidation > firstAliasValidation) {
542
+ problems.push(`scripts/bootstrap-provider-runtime.sh must validate the real runtime parent before aliases: ${requiredParent}`)
543
+ }
544
+ }
545
+
546
+ for (const requiredAlias of [
547
+ 'require_exact_runtime_alias "$provider_runtime/.codex" "codex" "$provider_runtime/codex"',
548
+ 'require_exact_runtime_alias "$provider_runtime/.kimi-code" "kimi-code" "$provider_runtime/kimi-code"',
549
+ 'require_exact_runtime_alias "$provider_runtime/.opencode" "opencode" "$provider_runtime/opencode"',
550
+ 'require_exact_runtime_alias "$provider_runtime/.local/share/opencode" "../../opencode" "$provider_runtime/opencode"'
551
+ ]) {
552
+ if (!content.includes(requiredAlias)) {
553
+ problems.push(`scripts/bootstrap-provider-runtime.sh must validate canonical provider runtime alias: ${requiredAlias}`)
554
+ }
555
+ }
556
+
557
+ for (const resolutionCheck of [
558
+ 'resolved_alias_path=$(realpath -e -- "$alias_path")',
559
+ 'resolved_directory_path=$(realpath -e -- "$resolved_directory")',
560
+ 'if [[ $resolved_alias_path != "$resolved_directory_path" ]]; then'
561
+ ]) {
562
+ if (!content.includes(resolutionCheck)) {
563
+ problems.push(`scripts/bootstrap-provider-runtime.sh must verify canonical provider runtime aliases resolve to their target: ${resolutionCheck}`)
564
+ }
565
+ }
566
+
567
+ for (const lockOperation of [
568
+ 'flock --exclusive "$provider_lock_fd"',
569
+ 'flock --unlock "$provider_lock_fd"'
570
+ ]) {
571
+ if (!content.includes(lockOperation)) {
572
+ problems.push(`scripts/bootstrap-provider-runtime.sh must serialize shared provider-link mutations: ${lockOperation}`)
573
+ }
464
574
  }
465
575
 
466
576
  return problems
@@ -470,7 +580,7 @@ function verifyDockerRunSh(content) {
470
580
  * Builds negative probes derived from the real files. Each probe mutates the
471
581
  * real content with a regression and must be caught by the matching verifier.
472
582
  *
473
- * @param {{dockerfile: string, compose: string}} contents - The real file contents.
583
+ * @param {{dockerDocs: string, dockerfile: string, compose: string, providerBootstrap: string}} contents - The real file contents.
474
584
  * @returns {{name: string, problems: string[], expected: string}[]} - The probes.
475
585
  */
476
586
  function negativeProbes(contents) {
@@ -502,7 +612,7 @@ function negativeProbes(contents) {
502
612
  },
503
613
  {
504
614
  name: "compose credential mount regression",
505
- problems: verifyCompose(contents.compose.replace(" command:", " - $" + "{HOME}/.kimi:/home/dev/.kimi\n command:")),
615
+ problems: verifyCompose(contents.compose.replace(" entrypoint:", " - type: bind\n source: $" + "{HOME}/.kimi\n target: /home/dev/.kimi\n entrypoint:")),
506
616
  expected: "mount exactly"
507
617
  },
508
618
  {
@@ -511,14 +621,74 @@ function negativeProbes(contents) {
511
621
  expected: "credential values"
512
622
  },
513
623
  {
514
- name: "compose Threadwire coupling regression",
515
- problems: verifyCompose(contents.compose.replace(" HOME: /home/dev", " HOME: /home/dev\n THREADWIRE_TOKEN: secret")),
516
- expected: "Threadwire"
624
+ name: "compose stale provider runtime target regression",
625
+ problems: verifyCompose(contents.compose.replace("target: /opt/hermes-dind-shared/auth/provider-runtime", "target: /run/ai-provider-runtime")),
626
+ expected: "mount exactly"
627
+ },
628
+ {
629
+ name: "compose mutable agent context source regression",
630
+ problems: verifyCompose(contents.compose.replace("${AGENT_CONTEXT_SOURCE_PATH:?Set AGENT_CONTEXT_SOURCE_PATH in .env}", "/opt/hermes-dind-shared/agent-context/current")),
631
+ expected: "mount exactly"
632
+ },
633
+ {
634
+ name: "compose stale agent context target regression",
635
+ problems: verifyCompose(contents.compose.replaceAll("/opt/hermes-agent-context", "/opt/agent-context")),
636
+ expected: "mount exactly"
637
+ },
638
+ {
639
+ name: "compose npm credential mount regression",
640
+ problems: verifyCompose(contents.compose.replace(" entrypoint:", " - type: bind\n source: $" + "{HOME}/.npmrc\n target: /home/dev/.npmrc\n read_only: true\n entrypoint:")),
641
+ expected: "npm credentials"
642
+ },
643
+ {
644
+ name: "compose bootstrap entrypoint regression",
645
+ problems: verifyCompose(contents.compose.replace(" entrypoint:", " x-entrypoint:")),
646
+ expected: "command overrides"
517
647
  },
518
648
  {
519
649
  name: "compose second service regression",
520
650
  problems: verifyCompose(`${contents.compose}\n worker:\n image: ubuntu\n`),
521
651
  expected: "dev"
652
+ },
653
+ {
654
+ name: "provider runtime alias regression",
655
+ problems: verifyProviderBootstrap(contents.providerBootstrap.replace(
656
+ 'require_exact_runtime_alias "$provider_runtime/.local/share/opencode" "../../opencode" "$provider_runtime/opencode"',
657
+ 'require_exact_runtime_alias "$provider_runtime/.local/share/opencode" "opencode" "$provider_runtime/opencode"'
658
+ )),
659
+ expected: "canonical provider runtime alias"
660
+ },
661
+ {
662
+ name: "provider runtime alias resolution regression",
663
+ problems: verifyProviderBootstrap(contents.providerBootstrap.replace(
664
+ 'resolved_alias_path=$(realpath -e -- "$alias_path")',
665
+ 'resolved_alias_path=$(realpath -e -- "$resolved_directory")'
666
+ )),
667
+ expected: "resolve to their target"
668
+ },
669
+ {
670
+ name: "provider runtime real parent regression",
671
+ problems: verifyProviderBootstrap(contents.providerBootstrap.replace(
672
+ 'require_real_runtime_directory "$provider_runtime/.local/share"',
673
+ 'require_real_runtime_directory "$provider_runtime/.local-share"'
674
+ )),
675
+ expected: "real runtime parent"
676
+ },
677
+ {
678
+ name: "provider runtime serialization regression",
679
+ problems: verifyProviderBootstrap(contents.providerBootstrap.replace(
680
+ 'flock --exclusive "$provider_lock_fd"',
681
+ 'flock --shared "$provider_lock_fd"'
682
+ )),
683
+ expected: "serialize shared provider-link mutations"
684
+ },
685
+ {
686
+ name: "provider runtime and agent context containment regression",
687
+ problems: verifyDockerDevelopmentDocs(contents.dockerDocs.replace(
688
+ 'case "$agent_context_source/" in "$provider_runtime_source/"*) exit 1;; esac',
689
+ ""
690
+ )),
691
+ expected: "bidirectional provider-runtime/agent-context containment"
522
692
  }
523
693
  ]
524
694
  }
@@ -558,17 +728,37 @@ function main() {
558
728
  }
559
729
 
560
730
  const dockerRunPath = path.join(repoRoot, "scripts", "docker-run.sh")
731
+ const dockerDocsPath = path.join(repoRoot, "docs", "docker-development-environment.md")
732
+ const providerBootstrapPath = path.join(repoRoot, "scripts", "bootstrap-provider-runtime.sh")
733
+ const dockerDocs = fs.existsSync(dockerDocsPath) ? fs.readFileSync(dockerDocsPath, "utf8") : null
734
+ const providerBootstrap = fs.existsSync(providerBootstrapPath) ? fs.readFileSync(providerBootstrapPath, "utf8") : null
735
+
736
+ if (dockerDocs === null) {
737
+ problems.push("Missing required file: docs/docker-development-environment.md")
738
+ } else {
739
+ problems.push(...verifyDockerDevelopmentDocs(dockerDocs))
740
+ }
741
+
742
+ if (providerBootstrap === null) {
743
+ problems.push("Missing required file: scripts/bootstrap-provider-runtime.sh")
744
+ } else {
745
+ problems.push(...verifyProviderBootstrap(providerBootstrap))
746
+ }
561
747
 
562
- if (fs.existsSync(dockerRunPath) && process.platform != "win32") {
563
- const mode = fs.statSync(dockerRunPath).mode
748
+ if (process.platform != "win32") {
749
+ for (const executablePath of [dockerRunPath, providerBootstrapPath]) {
750
+ if (!fs.existsSync(executablePath)) continue
564
751
 
565
- if ((mode & 0o111) == 0) {
566
- problems.push("scripts/docker-run.sh must be executable")
752
+ const mode = fs.statSync(executablePath).mode
753
+
754
+ if ((mode & 0o111) == 0) {
755
+ problems.push(`${path.relative(repoRoot, executablePath)} must be executable`)
756
+ }
567
757
  }
568
758
  }
569
759
 
570
- if (dockerfile !== null && compose !== null) {
571
- for (const probe of negativeProbes({dockerfile, compose})) {
760
+ if (dockerDocs !== null && dockerfile !== null && compose !== null && providerBootstrap !== null) {
761
+ for (const probe of negativeProbes({dockerDocs, dockerfile, compose, providerBootstrap})) {
572
762
  if (!probe.problems.some((problem) => problem.toLowerCase().includes(probe.expected.toLowerCase()))) {
573
763
  problems.push(`Negative probe was not caught by the verifier: ${probe.name}`)
574
764
  }
@@ -0,0 +1,14 @@
1
+ // @ts-check
2
+
3
+ import BaseCommand from "../../../base-command.js"
4
+
5
+ /** CLI command for merging complete rich test-profile shards into timing history. */
6
+ export default class TestTimingManifestMerge extends BaseCommand {
7
+ /**
8
+ * Runs execute.
9
+ * @returns {Promise<ReturnType<typeof JSON.parse>>} - Resolves with the merged manifest.
10
+ */
11
+ async execute() {
12
+ return await this.getConfiguration().getEnvironmentHandler().cliCommandsTestTimingManifestMerge(this)
13
+ }
14
+ }
@@ -406,6 +406,15 @@ export default class VelociousEnvironmentHandlerBase {
406
406
  throw new Error("cliCommandsTest not implemented")
407
407
  }
408
408
 
409
+ /**
410
+ * Runs cli commands test timing manifest merge.
411
+ * @param {import("../cli/base-command.js").default} _command - Command.
412
+ * @returns {Promise<ReturnType<typeof JSON.parse>>} - Resolves with the command result.
413
+ */
414
+ async cliCommandsTestTimingManifestMerge(_command) {
415
+ throw new Error("cliCommandsTestTimingManifestMerge not implemented")
416
+ }
417
+
409
418
  /**
410
419
  * Runs cli commands background jobs main.
411
420
  * @param {import("../cli/base-command.js").default} _command - Command.