@orkestrel/scaffold 0.0.107 → 0.0.109

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 (164) hide show
  1. package/dist/agents/skills/orkestrel-dispatch/scripts/bench.js +326 -91
  2. package/dist/agents/skills/orkestrel-dispatch/scripts/brief.js +110 -14
  3. package/dist/agents/skills/orkestrel-dispatch/scripts/chain.js +280 -0
  4. package/dist/agents/skills/orkestrel-dispatch/scripts/cite.js +188 -26
  5. package/dist/agents/skills/orkestrel-dispatch/scripts/launch.js +142 -43
  6. package/dist/agents/skills/orkestrel-dispatch/scripts/login.js +240 -62
  7. package/dist/agents/skills/orkestrel-dispatch/scripts/patch.js +323 -0
  8. package/dist/agents/skills/orkestrel-dispatch/scripts/result.js +169 -32
  9. package/dist/agents/skills/orkestrel-dispatch/scripts/sweep.js +368 -27
  10. package/dist/agents/skills/orkestrel-falsify/scripts/mutate.js +560 -0
  11. package/dist/agents/skills/orkestrel-harden/scripts/discovery.js +1052 -205
  12. package/dist/agents/skills/orkestrel-harden/scripts/measure.js +361 -0
  13. package/dist/agents/skills/orkestrel-harden/scripts/titles.js +455 -80
  14. package/dist/agents/skills/orkestrel-parallel/scripts/cases.js +327 -0
  15. package/dist/agents/skills/orkestrel-publish/scripts/compare.js +298 -51
  16. package/dist/agents/skills/orkestrel-publish/scripts/pins.js +170 -13
  17. package/dist/agents/skills/orkestrel-publish/scripts/swap.js +956 -191
  18. package/dist/agents/skills/orkestrel-publish/scripts/wave.js +707 -112
  19. package/dist/agents/skills/orkestrel-publish/scripts/window.js +235 -41
  20. package/dist/agents/skills/orkestrel-scout/scripts/map.js +341 -54
  21. package/dist/agents/templates/brief.md +1 -1
  22. package/dist/bin/main.js +51 -46
  23. package/dist/bin/main.js.map +1 -1
  24. package/dist/host/AGENTS.md +26 -20
  25. package/dist/host/agents/orchestration.md +98 -58
  26. package/dist/host/agents/platforms/posix.md +15 -0
  27. package/dist/host/agents/platforms/windows.md +37 -0
  28. package/dist/host/agents/skills/orkestrel-align/SKILL.md +10 -13
  29. package/dist/host/agents/skills/orkestrel-align/references/fleet.md +4 -10
  30. package/dist/host/agents/skills/orkestrel-align/references/integration.md +1 -1
  31. package/dist/host/agents/skills/orkestrel-build/SKILL.md +16 -48
  32. package/dist/host/agents/skills/orkestrel-build/agents/openai.yaml +1 -1
  33. package/dist/host/agents/skills/orkestrel-debrief/SKILL.md +19 -16
  34. package/dist/host/agents/skills/orkestrel-debrief/agents/openai.yaml +1 -1
  35. package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +15 -28
  36. package/dist/host/agents/skills/orkestrel-debrief/references/instruction-audit.md +5 -6
  37. package/dist/host/agents/skills/orkestrel-debrief/references/retention.md +12 -14
  38. package/dist/host/agents/skills/orkestrel-dispatch/SKILL.md +36 -24
  39. package/dist/host/agents/skills/orkestrel-dispatch/references/launch.md +14 -11
  40. package/dist/host/agents/skills/orkestrel-dispatch/scripts/bench.ts +366 -75
  41. package/dist/host/agents/skills/orkestrel-dispatch/scripts/brief.ts +126 -16
  42. package/dist/host/agents/skills/orkestrel-dispatch/scripts/chain.ts +323 -0
  43. package/dist/host/agents/skills/orkestrel-dispatch/scripts/cite.ts +220 -31
  44. package/dist/host/agents/skills/orkestrel-dispatch/scripts/launch.ts +176 -33
  45. package/dist/host/agents/skills/orkestrel-dispatch/scripts/login.ts +294 -59
  46. package/dist/host/agents/skills/orkestrel-dispatch/scripts/patch.ts +401 -0
  47. package/dist/host/agents/skills/orkestrel-dispatch/scripts/result.ts +199 -34
  48. package/dist/host/agents/skills/orkestrel-dispatch/scripts/sweep.ts +428 -27
  49. package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +10 -8
  50. package/dist/host/agents/skills/orkestrel-falsify/references/brief.md +1 -5
  51. package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +1 -1
  52. package/dist/host/agents/skills/orkestrel-falsify/scripts/mutate.ts +701 -0
  53. package/dist/host/agents/skills/orkestrel-harden/SKILL.md +16 -12
  54. package/dist/host/agents/skills/orkestrel-harden/references/bindings.md +23 -81
  55. package/dist/host/agents/skills/orkestrel-harden/references/centralization.md +4 -5
  56. package/dist/host/agents/skills/orkestrel-harden/references/contract.md +2 -0
  57. package/dist/host/agents/skills/orkestrel-harden/references/document.md +33 -0
  58. package/dist/host/agents/skills/orkestrel-harden/references/equivalence.md +6 -3
  59. package/dist/host/agents/skills/orkestrel-harden/references/hardening.md +8 -4
  60. package/dist/host/agents/skills/orkestrel-harden/references/research.md +2 -0
  61. package/dist/host/agents/skills/orkestrel-harden/scripts/discovery.ts +1272 -279
  62. package/dist/host/agents/skills/orkestrel-harden/scripts/measure.ts +425 -0
  63. package/dist/host/agents/skills/orkestrel-harden/scripts/titles.ts +603 -98
  64. package/dist/host/agents/skills/orkestrel-journey/SKILL.md +75 -98
  65. package/dist/host/agents/skills/orkestrel-journey/references/captures.md +54 -17
  66. package/dist/host/agents/skills/orkestrel-journey/references/decide.md +1 -20
  67. package/dist/host/agents/skills/orkestrel-journey/references/layer.md +21 -31
  68. package/dist/host/agents/skills/orkestrel-journey/references/styles.md +1 -1
  69. package/dist/host/agents/skills/orkestrel-parallel/SKILL.md +91 -30
  70. package/dist/host/agents/skills/orkestrel-parallel/agents/openai.yaml +2 -2
  71. package/dist/host/agents/skills/orkestrel-parallel/references/claude.md +34 -0
  72. package/dist/host/agents/skills/orkestrel-parallel/references/codex.md +29 -0
  73. package/dist/host/agents/skills/orkestrel-parallel/references/cursor.md +29 -0
  74. package/dist/host/agents/skills/orkestrel-parallel/references/t3.md +31 -0
  75. package/dist/host/agents/skills/orkestrel-parallel/scripts/cases.ts +389 -0
  76. package/dist/host/agents/skills/orkestrel-polish/SKILL.md +16 -20
  77. package/dist/host/agents/skills/orkestrel-polish/references/capture-harness.md +16 -26
  78. package/dist/host/agents/skills/orkestrel-publish/SKILL.md +12 -26
  79. package/dist/host/agents/skills/orkestrel-publish/agents/openai.yaml +2 -2
  80. package/dist/host/agents/skills/orkestrel-publish/references/adopt.md +23 -0
  81. package/dist/host/agents/skills/orkestrel-publish/references/release.md +4 -4
  82. package/dist/host/agents/skills/orkestrel-publish/references/wave.md +12 -10
  83. package/dist/host/agents/skills/orkestrel-publish/references/window.md +20 -25
  84. package/dist/host/agents/skills/orkestrel-publish/scripts/compare.ts +348 -59
  85. package/dist/host/agents/skills/orkestrel-publish/scripts/pins.ts +202 -22
  86. package/dist/host/agents/skills/orkestrel-publish/scripts/swap.ts +1061 -191
  87. package/dist/host/agents/skills/orkestrel-publish/scripts/wave.ts +812 -140
  88. package/dist/host/agents/skills/orkestrel-publish/scripts/window.ts +280 -38
  89. package/dist/host/agents/skills/orkestrel-scout/SKILL.md +3 -3
  90. package/dist/host/agents/skills/orkestrel-scout/scripts/map.ts +428 -70
  91. package/dist/host/agents/templates/brief.md +1 -1
  92. package/dist/host/agents/transports/claude-cli.md +3 -3
  93. package/dist/host/agents/transports/codex.md +3 -8
  94. package/dist/host/agents/transports/cursor.md +2 -3
  95. package/dist/host/claude/AGENTS.md +7 -10
  96. package/dist/host/claude/agents/analyst.md +3 -3
  97. package/dist/host/claude/agents/astra.md +3 -3
  98. package/dist/host/claude/agents/builder.md +1 -1
  99. package/dist/host/claude/agents/checker.md +1 -1
  100. package/dist/host/claude/agents/distiller.md +1 -1
  101. package/dist/host/claude/agents/grok.md +1 -1
  102. package/dist/host/claude/agents/opus.md +2 -2
  103. package/dist/host/claude/agents/orkestrel.md +57 -90
  104. package/dist/host/claude/agents/researcher.md +1 -1
  105. package/dist/host/claude/agents/reviewer.md +4 -0
  106. package/dist/host/claude/agents/scout.md +1 -1
  107. package/dist/host/claude/agents/verifier.md +1 -1
  108. package/dist/host/claude/rules/application.md +14 -21
  109. package/dist/host/claude/rules/architecture.md +34 -44
  110. package/dist/host/claude/rules/browser.md +3 -4
  111. package/dist/host/claude/rules/campaign.md +55 -0
  112. package/dist/host/claude/rules/documentation.md +26 -11
  113. package/dist/host/claude/rules/names.md +9 -4
  114. package/dist/host/claude/rules/patterns.md +6 -7
  115. package/dist/host/claude/rules/portability.md +2 -3
  116. package/dist/host/claude/rules/quality.md +4 -5
  117. package/dist/host/claude/rules/styles.md +2 -2
  118. package/dist/host/claude/rules/tests.md +57 -114
  119. package/dist/host/claude/rules/typescript.md +8 -10
  120. package/dist/host/claude/rules/workspace.md +21 -19
  121. package/dist/host/claude/rules/writing.md +3 -3
  122. package/dist/host/claude/skills/orkestrel-dispatch/SKILL.md +1 -1
  123. package/dist/host/claude/skills/orkestrel-harden/SKILL.md +1 -1
  124. package/dist/host/claude/skills/orkestrel-parallel/SKILL.md +1 -1
  125. package/dist/host/claude/skills/orkestrel-publish/SKILL.md +1 -1
  126. package/dist/host/claude/skills/orkestrel-scout/SKILL.md +1 -1
  127. package/dist/host/codex/agents/analyst.toml +9 -3
  128. package/dist/host/codex/agents/astra.toml +3 -4
  129. package/dist/host/codex/agents/builder.toml +1 -1
  130. package/dist/host/codex/agents/opus.toml +3 -4
  131. package/dist/host/codex/agents/orkestrel.toml +1 -1
  132. package/dist/host/codex/agents/researcher.toml +2 -2
  133. package/dist/host/codex/agents/verifier.toml +1 -1
  134. package/dist/host/codex/config.toml +3 -3
  135. package/dist/host/configs/helpers.ts +21 -1
  136. package/dist/host/configs/policy.ts +405 -16
  137. package/dist/host/cursor/rules/orchestration.mdc +1 -0
  138. package/dist/host/dotfiles/oxlintrc.json +141 -5
  139. package/dist/host/guides/agent.md +111 -134
  140. package/dist/host/guides/capture.md +801 -0
  141. package/dist/host/guides/probe.md +27 -8
  142. package/dist/host/guides/scaffold.md +152 -19
  143. package/dist/host/guides/study.md +534 -440
  144. package/dist/host/guides/test.md +111 -44
  145. package/dist/host/manifest.json +199 -242
  146. package/dist/host/tests/config.test.ts +1558 -219
  147. package/dist/host/tests/policy.test.ts +127 -1
  148. package/dist/host/tests/setupPolicy.ts +1389 -39
  149. package/dist/src/core/index.cjs +239 -87
  150. package/dist/src/core/index.cjs.map +1 -1
  151. package/dist/src/core/index.d.cts +123 -39
  152. package/dist/src/core/index.d.ts +123 -39
  153. package/dist/src/core/index.js +234 -86
  154. package/dist/src/core/index.js.map +1 -1
  155. package/dist/src/server/index.cjs +20 -27
  156. package/dist/src/server/index.cjs.map +1 -1
  157. package/dist/src/server/index.js +21 -28
  158. package/dist/src/server/index.js.map +1 -1
  159. package/package.json +11 -11
  160. package/dist/agents/skills/orkestrel-dispatch/scripts/helpers.js +0 -283
  161. package/dist/host/agents/skills/orkestrel-dispatch/references/bench.md +0 -25
  162. package/dist/host/agents/skills/orkestrel-dispatch/scripts/helpers.ts +0 -333
  163. package/dist/host/agents/skills/orkestrel-parallel/references/harnesses.md +0 -19
  164. package/dist/host/guides/supervisor.md +0 -4890
@@ -0,0 +1,801 @@
1
+ # Capture
2
+
3
+ > An evidence protocol, integrity engine, and runner that stamp browser journey frames with their source provenance and classify each frame's freshness.
4
+
5
+ A browser application that proves its screens through journeys also emits frames: one image per state and output variant. This package is the evidence behind those frames. Its core declares the protocol a journey writes and a reader checks: the request a runner hands to Vitest, the final record a variant writes, the receipt and registry that pin a complete run, the stamp and manifest that bind each image to the sources and the commit that produced it, and the freshness classes a reader reports. Its guards validate each document total over hostile input, its helpers hold the pure rules that compare them, and its failures carry a condition the executable maps to an exit code. Its server face implements integrity checks, selected journey and frame stages, preflight, registry refresh, and contact sheets.
6
+
7
+ The package names no product group, title, variant, or path. A host supplies every one of them as data: its portfolios, its owners, its variants, its final case, its first-stage variant, its input patterns, and its optional uniform scopes.
8
+
9
+ The core helpers compare selectors, classify freshness, and read portable paths:
10
+
11
+ ```ts
12
+ import {
13
+ matchesCaptureSelector,
14
+ readCaptureDirty,
15
+ resolveCaptureStatus,
16
+ isCapturePath,
17
+ } from '@orkestrel/capture'
18
+
19
+ matchesCaptureSelector('pages:mail', 'pages') // true
20
+ matchesCaptureSelector('pages:mailbox', 'pages:mail') // false
21
+ resolveCaptureStatus(['stale-own', 'missing']) // 'missing'
22
+ readCaptureDirty('R src/new.ts\0src/old.ts\0') // ['src/new.ts', 'src/old.ts']
23
+ isCapturePath('../escape.png') // false
24
+ ```
25
+
26
+ ## Surface
27
+
28
+ ### Types
29
+
30
+ Protocol shapes from [`types.ts`](../src/core/types.ts).
31
+
32
+ | Name | Kind | Summary |
33
+ | -------------------------- | --------- | --------------------------------------------------------------------------------------------- |
34
+ | `CaptureErrorCode` | type | Names one condition axis a capture failure carries. |
35
+ | `CaptureErrorContext` | type | Carries structured facts about a capture failure beside its message. |
36
+ | `CaptureErrorOptions` | interface | Configures one capture failure's condition, optional context, and optional cause. |
37
+ | `CaptureSelection` | interface | Carries the selection fields a request and a parking check read. |
38
+ | `CaptureIdentity` | interface | Identifies one runner invocation's producing stage and configuration. |
39
+ | `CaptureRequest` | interface | Carries a runner request through Vitest's provided-context protocol. |
40
+ | `CaptureSources` | interface | Attributes product ownership and behavioral inputs separately. |
41
+ | `CaptureDigest` | interface | Records declared patterns, expanded file hashes, and their combined digest. |
42
+ | `CaptureDestination` | interface | Identifies one required output pair and its declaring case. |
43
+ | `CaptureDeclaration` | interface | Preserves a collected case's full behavioral declaration. |
44
+ | `CaptureEntry` | interface | Records an emitted destination and its producing case duration. |
45
+ | `CaptureArtifact` | interface | Names the journey artifact a final case wrote and the SHA-256 of its bytes. |
46
+ | `CaptureRun` | interface | Records collected declarations, successful observations, and emitted output separately. |
47
+ | `CaptureReceipt` | interface | Binds a complete registry reading to its report and input hashes. |
48
+ | `CaptureRegistry` | interface | Collects validated registry receipts and their required output pairs. |
49
+ | `CaptureLookup` | interface | Binds a state-to-case lookup to its registry, inputs, and passing run. |
50
+ | `CaptureStamp` | interface | Stamps one emitted frame with execution, source, image, and registry provenance. |
51
+ | `CaptureManifest` | interface | Holds the registry, frame stamps, and validated case lookups in one document. |
52
+ | `CaptureStatus` | type | Names the primary freshness classification of a required or discovered frame. |
53
+ | `CaptureFreshness` | interface | Reports a frame's freshness classification and every supporting reason. |
54
+ | `CaptureWarning` | interface | Identifies changed behavioral inputs and conservatively affected cases. |
55
+ | `CaptureCollectionReceipt` | interface | Binds a successful full collection to the unchanged complete anchor and exact evidence bytes. |
56
+ | `CaptureStoredManifest` | interface | Extends the image manifest with independently authenticated collection evidence. |
57
+ | `CaptureLookupJournal` | interface | Binds capture-free lookup publication to the exact manifest it supplements. |
58
+ | `CaptureViewport` | interface | Declares a host viewport's name and pixel geometry without inferring it from a file name. |
59
+ | `CaptureBox` | interface | Describes a logged viewport box a journey artifact records for pixel sampling. |
60
+ | `CaptureAssertion` | interface | Carries the owner-bearing projection of a Vitest JSON assertion. |
61
+ | `CaptureReport` | interface | Holds validated reporter assertions without copying Vitest's unrelated wire members. |
62
+
63
+ ### Constants
64
+
65
+ From [`constants.ts`](../src/core/constants.ts).
66
+
67
+ | Name | Kind | Summary |
68
+ | --------------------------- | ----- | --------------------------------------------------------------------------------------------- |
69
+ | `CAPTURE_ERROR_CODES` | const | Lists the condition axes a capture failure carries, in the order the executable reports them. |
70
+ | `CAPTURE_ERROR_BRAND` | const | Keys the global brand a capture error carries, so a duplicate installation recognizes it. |
71
+ | `CAPTURE_STATUS_PRECEDENCE` | const | Lists the non-current freshness classifications from highest precedence to lowest. |
72
+ | `CAPTURE_REQUEST_PREFIX` | const | Names the file prefix of a runner request, completed by the runner's process id and `.json`. |
73
+ | `CAPTURE_VERSION` | const | Holds the protocol version every request, record, receipt, registry, and manifest declares. |
74
+ | `CAPTURE_PNG_SIGNATURE` | const | Holds the eight bytes every PNG file opens with. |
75
+ | `CAPTURE_REGISTRY_FILE` | const | Names the default accepted complete-run registry file inside a portfolio directory. |
76
+ | `CAPTURE_MANIFEST_FILE` | const | Names the default image-stamp manifest file inside a portfolio directory. |
77
+ | `CAPTURE_LOOKUPS_FILE` | const | Names the default lookup journal file inside a portfolio directory. |
78
+
79
+ ### Errors
80
+
81
+ From [`errors.ts`](../src/core/errors.ts). The executable maps a `USAGE` condition to exit 64 and every other condition to exit 1.
82
+
83
+ | Name | Kind | Summary |
84
+ | ---------------- | -------- | ---------------------------------------------------------- |
85
+ | `CaptureError` | class | Reports one capture failure under a stable condition axis. |
86
+ | `isCaptureError` | function | Checks whether an unknown value is a capture error. |
87
+
88
+ ### Validators
89
+
90
+ Total guards from [`validators.ts`](../src/core/validators.ts). Each returns `false` for a value that throws on read, a cycle, or a non-record, and never throws.
91
+
92
+ | Name | Kind | Summary |
93
+ | ------------------------- | -------- | ------------------------------------------------------------------------------------------------ |
94
+ | `isCapturePath` | function | Checks whether a serialized path stays within the portable relative-path vocabulary. |
95
+ | `isCapturePattern` | function | Checks whether a native glob declaration uses relative portable path segments. |
96
+ | `isCaptureSegment` | function | Checks whether a protocol identity can occupy one portable path segment. |
97
+ | `isCaptureSelector` | function | Checks whether an owner selector uses canonical colon-separated segments. |
98
+ | `isCaptureHash` | function | Checks whether a value is a SHA-256 hexadecimal digest. |
99
+ | `isCaptureHashes` | function | Checks whether a file-hash map has portable keys and valid digests. |
100
+ | `isCaptureSources` | function | Checks whether source attribution separates own, shared, and behavioral patterns. |
101
+ | `isCaptureIdentity` | function | Checks whether an execution identity carries its required authorization fields. |
102
+ | `isCaptureRequest` | function | Checks whether a request has the supported version and complete execution identity. |
103
+ | `isCaptureDeclaration` | function | Checks whether a collected declaration preserves its full owner and state population. |
104
+ | `isCaptureDestination` | function | Checks whether a destination supplies canonical identity and source attribution fields. |
105
+ | `isCaptureEntry` | function | Checks whether an emitted entry includes a finite nonnegative case duration. |
106
+ | `isCaptureArtifact` | function | Checks whether a record names its journey artifact by portable path and SHA-256 digest. |
107
+ | `isCaptureRun` | function | Checks whether a final record preserves declarations, observations, and output separately. |
108
+ | `isCaptureDigest` | function | Checks whether a shared-source digest retains both patterns and expanded file hashes. |
109
+ | `isCaptureReceipt` | function | Checks whether a complete-run receipt binds the report, inputs, and declaration registry. |
110
+ | `isCaptureRegistry` | function | Checks whether a registry document retains its receipts and required output pairs. |
111
+ | `isCaptureLookup` | function | Checks whether a lookup binds a passing case to its full run record and input hashes. |
112
+ | `isCaptureLookupJournal` | function | Checks the separate lookup journal and its optional predecessor manifest digest. |
113
+ | `isCaptureStamp` | function | Checks whether a stamp binds an emitted image to source and execution provenance. |
114
+ | `isCaptureManifest` | function | Checks whether an inherited manifest has the supported schema without discarding malformed rows. |
115
+ | `isCaptureCollection` | function | Checks whether collection evidence pins complete input membership and both external artifacts. |
116
+ | `isCaptureStoredManifest` | function | Checks optional collection metadata without upgrading historical image evidence. |
117
+ | `isCaptureGeometry` | function | Checks the host's declared viewport without deriving geometry from an output name. |
118
+ | `isCaptureBox` | function | Checks a journey artifact's recorded sampling box. |
119
+
120
+ ### Helpers
121
+
122
+ Pure leaves from [`helpers.ts`](../src/core/helpers.ts). A helper that refuses a document throws a `CaptureError` with the `EVIDENCE` condition.
123
+
124
+ | Name | Kind | Summary |
125
+ | ---------------------------- | -------- | ------------------------------------------------------------------------------------------------ |
126
+ | `describeCaptureError` | function | Describes a caught failure as one reportable line. |
127
+ | `collectCaptureDuplicates` | function | Finds repeated identities before a keyed collection could discard them. |
128
+ | `requireCaptureUnique` | function | Refuses duplicate declarations or portable storage-key collisions before indexing. |
129
+ | `matchesCaptureMembers` | function | Compares complete populations without erasing duplicates. |
130
+ | `matchesCaptureSelector` | function | Compares an owner with an explicit selector at colon boundaries. |
131
+ | `resolveCaptureOwner` | function | Builds the canonical owner from a destination's registry identity. |
132
+ | `resolveCaptureKey` | function | Builds a manifest identity independently of the physical image path. |
133
+ | `describeCaptureDestination` | function | Serializes a destination's identity and attribution in a deterministic field order. |
134
+ | `describeCaptureIdentity` | function | Serializes only the registry's execution and output identity, independent of input revisions. |
135
+ | `describeCaptureDeclaration` | function | Serializes a complete behavioral declaration without dropping eligibility or states. |
136
+ | `describeCaptureRequest` | function | Serializes every request field, retaining the exact argv order. |
137
+ | `collectCaptureChanges` | function | Names every changed, added, or removed file in hash populations. |
138
+ | `resolveLookupKey` | function | Builds the stable case identity used to merge separate lookup receipts. |
139
+ | `readCaptureReport` | function | Reads successful Vitest JSON output and validates every owner-bearing assertion. |
140
+ | `validateCaptureRun` | function | Validates declaration, reporter, observation, completeness, and output-projection agreement. |
141
+ | `resolveCaptureStatus` | function | Chooses the primary classification while retaining all findings separately. |
142
+ | `readCaptureDirty` | function | Reads Git's NUL-delimited porcelain paths without treating spaces or quoted names as separators. |
143
+ | `validateCaptureReceipt` | function | Validates a complete receipt against full reporter declarations and eligible output coverage. |
144
+
145
+ ### Server types
146
+
147
+ Engine, runner, preflight, and contact-sheet contracts from [`types.ts`](../src/server/types.ts).
148
+
149
+ | Name | Kind | Summary |
150
+ | -------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------- |
151
+ | `CaptureCommand` | type | Names one command the runner executes: a journey, a frame capture, or a stale reading. |
152
+ | `CapturePortfolioRoot` | interface | Supplies a host-authorized physical portfolio root without repeating its states or variants. |
153
+ | `CaptureHashHandler` | type | Digests the exact bytes of one file reading and returns the lowercase hexadecimal digest. |
154
+ | `CaptureIntegrityOptions` | interface | Configures the engine's workspace and separate evidence documents. |
155
+ | `CaptureStageProject` | interface | Supplies one selected project's expected final record and bounded input population. |
156
+ | `CaptureParsedArguments` | type | Carries the typed values and tokens of a strictly parsed capture command line. |
157
+ | `CaptureExecutionResult` | interface | Carries the supervised child outcome without treating a signal as a successful exit. |
158
+ | `CaptureExecutionHandler` | type | Executes the runner's child after the engine captures its input snapshot. |
159
+ | `CaptureStageOptions` | interface | Binds a stage transaction to its exact request, projects, reporter, and process boundary. |
160
+ | `CaptureFileSnapshot` | interface | Records one physical file's bytes and native modification readings at a stage boundary. |
161
+ | `CaptureProjectSnapshot` | interface | Preserves one project's declared patterns and sorted expansion before execution. |
162
+ | `CaptureSnapshot` | interface | Holds a stage boundary's input populations and existing output identities. |
163
+ | `CaptureProjectResult` | interface | Reports validated project evidence, retaining empty successful selections explicitly. |
164
+ | `CaptureStageResult` | interface | Reports the successful stage's committed stamps and validated lookup evidence. |
165
+ | `CaptureReadOptions` | interface | Selects a declaration population without requiring current images or passing state lookups. |
166
+ | `CaptureStaleOptions` | interface | Selects a freshness population and names the producing variants the host wrapper declares. |
167
+ | `CaptureCheckOptions` | interface | Selects required output pairs and attributable extras for a freshness reading. |
168
+ | `CaptureReadResult` | interface | Returns the exact current declaration destinations and their complete-anchor identity. |
169
+ | `CaptureCheckResult` | interface | Reports the exact judged population, every mismatch reason, and conservative test warnings. |
170
+ | `CaptureLookupOptions` | interface | Requests a state-to-case resolution before the runner starts any selected project. |
171
+ | `CaptureLookupResult` | interface | Resolves one declaring case and its actual output pairs with their passing evidence. |
172
+ | `CaptureRunEvidence` | interface | Binds a decoded run record to the physical file whose digest authenticated it. |
173
+ | `CaptureDirectoryResult` | interface | Carries one physical-directory ancestry while enumerating logical portfolio paths. |
174
+ | `CaptureEvidence` | interface | Carries accepted registry anchors and separately persisted passing run evidence. |
175
+ | `CaptureCollectionEvidence` | interface | Carries the authenticated external record beside its collection receipt. |
176
+ | `CaptureReceiptInput` | interface | Supplies a verifier's actual process outcome and exact reporter bytes for registry import. |
177
+ | `CaptureIntegrityInterface` | interface | Defines the shared stage transaction, freshness reading, and state resolution engine. |
178
+ | `CaptureProject` | interface | Preserves the wrapper's host declarations, including optional uniform scopes, for consumers. |
179
+ | `CaptureRunnerOptions` | interface | Configures one runner invocation: its command, its workspace, and the command line it reads. |
180
+ | `CaptureRunnerArguments` | interface | Carries parsed runner arguments while leaving omitted selectors available to the usage diagnostic. |
181
+ | `CaptureRunnerInterface` | interface | Drives selected owners through one journey stage or staged frame captures. |
182
+ | `CaptureChange` | interface | Classifies one changed Git path against accepted source attribution. |
183
+ | `CaptureChangePlan` | interface | Holds changed paths and the stable owner selection reduced by ancestry. |
184
+ | `CaptureOwnerOutcome` | interface | Records one changed owner's exit and whether an execution child started. |
185
+ | `CaptureWriteHandler` | type | Writes one chunk of runner output to the stream the host chooses. |
186
+ | `CaptureCLIOptions` | interface | Configures the executable's workspace and its report and diagnostic writers. |
187
+ | `CaptureCLIInterface` | interface | Runs capture command lines through injected output writers. |
188
+ | `CaptureIntegrityHandler` | type | Creates the integrity engine the runner reads and stamps through, from the options it derives. |
189
+ | `CaptureStagePlan` | interface | Names one planned stage and the variants it runs in one Vitest process. |
190
+ | `CaptureArgumentsOptions` | interface | Configures the Vitest argument list one stage spawns with. |
191
+ | `CaptureRequestOptions` | interface | Configures how a wrapper reads the runner's request for its own Vitest process. |
192
+ | `CaptureOutcome` | interface | Carries one project's owner-bearing passed work count from a finished stage. |
193
+ | `CaptureRunPlan` | interface | Carries everything every stage of one invocation shares. |
194
+ | `CaptureInvocationOptions` | interface | Configures one runner invocation's identity, workspace, and output. |
195
+ | `CaptureChildTerminationHandler` | type | Terminates one supervised child and reports whether its exit was confirmed. |
196
+ | `CaptureInvocationInterface` | interface | Owns one runner invocation's identity, request file, children, and interruption. |
197
+ | `CaptureDiscoveryRequest` | interface | Carries the request the runner publishes for the discovery child that reads its wrapper. |
198
+ | `CaptureHostEntry` | interface | Describes one wrapper project to the runner: its host declarations and, when it has one, its viewport. |
199
+ | `CaptureHostDescription` | interface | Carries the description a wrapper writes back to the runner that discovered it. |
200
+ | `CaptureDescriptionExpectation` | interface | Names what a description must echo for the runner to accept it. |
201
+ | `CaptureDiscoveryOptions` | interface | Configures how a wrapper answers the runner's discovery request. |
202
+ | `CaptureDiscoveryRunOptions` | interface | Configures one discovery: the workspace, the wrapper, the invocation that owns the child, and its bounds. |
203
+ | `CaptureDiscoveryInterface` | interface | Reads a consumer's wrapper by running its installed Vitest in discovery mode. |
204
+ | `CapturePicture` | interface | Carries a decoded RGB image for the image checks and the contact sheets. |
205
+ | `CaptureReaderOptions` | interface | Configures a read-only capture consumer using the wrapper's host declarations. |
206
+ | `CaptureProducer` | interface | Declares the viewport one wrapper project produces beside the project's own name. |
207
+ | `CaptureReaderDefaults` | interface | Supplies the wrapper path, the journey artifact directory, and the request directory a reader command line falls back to. |
208
+ | `CaptureReaderSelection` | interface | Returns a reader's authenticated selection, uniform scopes, and engine for pixel classification. |
209
+ | `CaptureArtifactResult` | interface | Carries a judged run record and the verified text of the journey artifact it names. |
210
+ | `CaptureJudgedRecord` | interface | Names the run record a judged frame's stamp points at and the case that stamped it. |
211
+ | `CaptureJudgedFrame` | interface | Pairs a judged frame with its verified artifact, when one was read, and its coverage issues. |
212
+ | `CaptureDecodedImage` | interface | Records one selected image's real bytes, geometry, and canonical destination. |
213
+ | `CaptureImageResult` | interface | Retains source geometry and a bounded sheet thumbnail after a full frame has been judged. |
214
+ | `CaptureOwnership` | interface | Identifies the process and invocation that own a request directory. |
215
+ | `CaptureOwnershipHost` | interface | Supplies the filesystem and liveness boundary for ownership arbitration. |
216
+ | `CapturePngLimits` | interface | Bounds a PNG's decoded pixels, side length, and encoded bytes. |
217
+ | `CaptureCheckFrame` | interface | Carries one judged frame to a host check: its destination, its decoded pixels, and its artifact. |
218
+ | `CaptureCheckHandler` | type | Reads one judged frame against a host rule and returns the issues it finds, none when it passes. |
219
+ | `CapturePreflightResult` | interface | Reports selected image checks without upgrading their engine freshness classifications. |
220
+ | `CapturePreflightInterface` | interface | Reads the selected population's frames and reports their freshness, mixed observations, and coverage. |
221
+ | `CaptureSheetOptions` | interface | Carries a contact-sheet output request separately from the population selection. |
222
+ | `CaptureSheetCell` | interface | Holds one contact-sheet cell: a frame's destination, its shrunk picture, and what the sheet reports about it. |
223
+ | `CaptureSheetGroup` | interface | Holds the cells of one portfolio, owner, and state side by side as one composite picture. |
224
+ | `CaptureSheetRow` | interface | Holds a pair of groups side by side as one composite picture. |
225
+ | `CaptureSheetPage` | interface | Holds one written sheet: its file name, the rows it stacks, and the composite picture. |
226
+ | `CaptureReaderCommand` | interface | Carries parsed reader arguments and an optional contact-sheet output directory. |
227
+ | `CaptureSheetsInterface` | interface | Composes exactly the authenticated sparse state and output pairs for iteration review. |
228
+ | `CaptureRegistryArguments` | interface | Carries the parsed registry command line: the wrapper, the variants to refresh, and the mode. |
229
+ | `CaptureRegistrarOptions` | interface | Configures one registry refresh: its workspace, wrapper default, request directory, and command line. |
230
+ | `CaptureRegistrarInterface` | interface | Refreshes the accepted registry from one complete capture-free journey per producing variant. |
231
+ | `CaptureInputHandler` | type | Reads a contained input through an owning operation's file snapshot memo. |
232
+ | `CaptureRetractionHandler` | type | Removes one invocation's published request during shutdown. |
233
+ | `CaptureTerminationHandler` | type | Begins one independent child termination only after request removal has been attempted. |
234
+
235
+ ### Server constants
236
+
237
+ The runner's names and bounds from [`constants.ts`](../src/server/constants.ts).
238
+
239
+ | Name | Kind | Summary |
240
+ | ------------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
241
+ | `CAPTURE_COMMAND_NAMES` | const | Names the executable subcommand each runner command answers to. |
242
+ | `CAPTURE_COMMAND_OPTIONS` | const | Defines each command's frozen options for Node's standard argument parser. |
243
+ | `CAPTURE_EXIT` | const | Holds the exit status each runner outcome maps to. |
244
+ | `CAPTURE_OWNER_FILE` | const | Names the exclusive workspace invocation marker. |
245
+ | `CAPTURE_REFUSAL_LIMIT` | const | Caps each refusal population so hostile records cannot flood a terminal or agent context. |
246
+ | `CAPTURE_STAGES` | const | Names the selected journey stage and the narrow and remaining frame stages. |
247
+ | `CAPTURE_TAG_OPERATORS` | const | Lists the words a tag name can never be, because a tag filter reads them as operators. |
248
+ | `CAPTURE_OWNER_PREFIX` | const | Names the tag prefix that marks a case's owner path and each ancestor of it. |
249
+ | `CAPTURE_FLAG` | const | Names the environment variable that turns frame capture on for a Vitest child. |
250
+ | `CAPTURE_FLAG_VALUE` | const | Holds the one value of the capture variable that turns frame capture on. |
251
+ | `CAPTURE_VITEST_ENTRY` | const | Names the repository-relative path of the Vitest entry the runner spawns through Node. |
252
+ | `CAPTURE_RUNS_DIRECTORY` | const | Names the directory beneath a portfolio or request directory that holds one invocation's records. |
253
+ | `CAPTURE_SURVIVORS_FILE` | const | Names the create-only child record beneath an invocation's run directory. |
254
+ | `CAPTURE_MINIMUM_IMAGE` | const | Holds the byte count under which an image is too small to be a real frame. |
255
+ | `CAPTURE_REPORT_LINES` | const | Names the first word of the line a reader prints, followed by its JSON report. |
256
+ | `CAPTURE_SHEET` | const | Holds the contact-sheet geometry: how wide a cell shrinks toward, how tall it may grow, the mark that stands in for an unreadable image, the gap between neighbors, and how many cells, rows, and pairs a row and a sheet hold. |
257
+ | `CAPTURE_SHEET_FILES` | const | Names the contact-sheet files: the sheet prefix and digits, and the two index documents. |
258
+ | `CAPTURE_REGISTRY_STAGE` | const | Names the stage each producing variant's complete journey runs in, ahead of the variant's name. |
259
+ | `CAPTURE_REGISTRY_LINE` | const | Names the first word of the line the registry refresh prints, followed by its JSON summary. |
260
+ | `CAPTURE_REQUESTS_FLAG` | const | Names the environment variable that carries the absolute request directory to a Vitest child. |
261
+ | `CAPTURE_DISCOVERY` | const | Holds the discovery exchange's fixed names and bound. |
262
+ | `CAPTURE_SUBCOMMANDS` | const | Names the executable's subcommands in the order its usage text lists them. |
263
+ | `CAPTURE_DEFAULTS` | const | Holds the repository-relative paths the executable falls back to when the command line names none. |
264
+ | `CAPTURE_USAGE` | const | Holds the usage text the executable prints when it is given no subcommand or an unknown one. |
265
+ | `CAPTURE_PNG_LIMITS` | const | Bounds decoded image memory, row metadata, and PNG input bytes independently of untrusted headers. |
266
+ | `CAPTURE_OWNER_TIMEOUT` | const | Bounds a synchronous ownership retry sequence in milliseconds, including host contention. |
267
+ | `CAPTURE_OWNER_BACKOFF` | const | Yields between ownership probes in milliseconds so filesystem contention can settle. |
268
+ | `CAPTURE_MESSAGE_LIMIT` | const | Bounds external diagnostic text while retaining the number of omitted characters. |
269
+
270
+ ### Server helpers
271
+
272
+ Leaves from [`helpers.ts`](../src/server/helpers.ts): hashing, digests, registry import, flag reading, the PNG codec, the runner's parsing, and the few reads and writes of the filesystem the runner and the registry refresh make (`resolveCaptureWrapperProject`, `answerCaptureDiscovery`, `readCaptureInputs`, `readCaptureRunning`, `readCaptureRequest`, `writeCaptureDocument`). A helper that refuses an image or a receipt throws a `CaptureError` with the `EVIDENCE` condition.
273
+
274
+ | Name | Kind | Summary |
275
+ | ------------------------------ | -------- | --------------------------------------------------------------------------------------------------------- |
276
+ | `computeCaptureHash` | function | Hashes exact source or image bytes with SHA-256. |
277
+ | `normalizeSlashes` | function | Normalizes a host path to forward slashes. |
278
+ | `buildCaptureDigest` | function | Computes the digest of both pattern declarations and expanded path and hash membership. |
279
+ | `buildCaptureLookupJournal` | function | Stores each distinct lookup input population once in a manifest-bound journal. |
280
+ | `readCaptureLookups` | function | Resolves journal lookup references after verifying each stored input population's digest. |
281
+ | `computeRegistryDigest` | function | Computes the registry identity from its authenticated receipts and exact output population. |
282
+ | `buildCaptureRegistry` | function | Imports complete receipts only after their actual exits and exact report bytes validate. |
283
+ | `matchesCaptureSignature` | function | Checks whether bytes open with the eight-byte PNG signature. |
284
+ | `predictCaptureByte` | function | Computes the PNG Paeth predictor used by both image readers. |
285
+ | `readCaptureChunks` | function | Reads PNG compressed-data chunks while refusing truncated or corrupt image bytes. |
286
+ | `unfilterCaptureRow` | function | Reconstructs one filtered PNG scanline from its filter byte and the row above. |
287
+ | `inflateCaptureStream` | function | Inflates a PNG's compressed stream, refusing one that is corrupt or longer than declared. |
288
+ | `decodeCapturePicture` | function | Decodes the eight-bit, non-interlaced PNGs the capture readers consume. |
289
+ | `encodeCaptureWord` | function | Encodes an unsigned 32-bit integer as four big-endian bytes. |
290
+ | `encodeCaptureChunk` | function | Encodes one checked PNG chunk for contact-sheet output. |
291
+ | `encodeCapturePicture` | function | Encodes an RGB picture using the contact-sheet writer's native PNG format. |
292
+ | `shrinkCapturePicture` | function | Scales a picture with an exact area average and clips its displayed height. |
293
+ | `composeCapturePictures` | function | Composes pictures on the contact-sheet gray background. |
294
+ | `readCaptureRequestOwner` | function | Reads the process id that a request file, or its temporary sibling, names. |
295
+ | `resolveCaptureRelative` | function | Resolves a path to the slash-separated form the evidence documents store, relative to a root. |
296
+ | `escapeCapturePattern` | function | Escapes every regular-expression metacharacter so a case name matches only itself. |
297
+ | `buildCaptureFilter` | function | Builds the tag expression that selects an owner population, and its capture tags for frames. |
298
+ | `resolveCaptureStages` | function | Plans one selected journey stage or narrow and remaining frame stages. |
299
+ | `resolveCaptureVariants` | function | Resolves selected producing variants in declaration order and requires their projects. |
300
+ | `buildCaptureChangePlan` | function | Classifies changed paths through accepted patterns and reduces owners by ancestry. |
301
+ | `renderCaptureCommand` | function | Renders an exact owner command with the selected variants and filters in host shell syntax. |
302
+ | `buildCaptureArguments` | function | Builds the exact Node argument array that spawns one Vitest stage. |
303
+ | `readCaptureOptions` | function | Parses standard option tokens and refuses empty values, terminators, and repeated single options. |
304
+ | `readCaptureArguments` | function | Reads the runner's command line into one selector, its flags, and the resolved wrapper path. |
305
+ | `readCapturePortfolios` | function | Reads the physical portfolio roots a wrapper declares and refuses a malformed or repeated root. |
306
+ | `readCaptureProject` | function | Reads one wrapper project's host declarations from its Vitest test configuration. |
307
+ | `describeCapturePortfolios` | function | Describes a project's portfolio roots as comparable lines, primary roots apart from standalone ones. |
308
+ | `requireCaptureProjects` | function | Refuses a wrapper project set whose members repeat or disagree on a host declaration. |
309
+ | `describeCaptureSelectorFault` | function | Describes why a selector names nothing the wrapper declares, naming the first unknown segment. |
310
+ | `buildCaptureRequestRefusal` | function | Builds the refusal a wrapper raises for a request file it cannot accept as its parent's. |
311
+ | `readCaptureRequest` | function | Reads and validates the request the runner wrote for the wrapper process that is its child. |
312
+ | `readCaptureReaderArguments` | function | Reads a reader's command line into its selection, its flags, and the resolved paths. |
313
+ | `readCaptureViewport` | function | Reads the viewport a wrapper project's producing variant declares. |
314
+ | `buildCaptureIntegrityOptions` | function | Derives the integrity engine's options from a wrapper project's host declarations. |
315
+ | `scanCaptureFrames` | function | Reports a portfolio, owner, and viewport whose frames mix cropped, viewport-sized, and page-sized images. |
316
+ | `collectCaptureIdentical` | function | Groups the selected images whose bytes are identical. |
317
+ | `collectCaptureErrors` | function | Finds the journey artifact lines that report an uncaught, unhandled, or logged error. |
318
+ | `readCaptureRunning` | function | Checks whether a process with this id is running, counting one the caller may not signal. |
319
+ | `resolveCaptureWrapperProject` | function | Resolves one entry of a wrapper's project list to the project it declares. |
320
+ | `readCaptureEntry` | function | Reads one wrapper project, as the description file holds it, into its host declarations and viewport. |
321
+ | `resolveCaptureRequests` | function | Resolves the directory that holds the runner's request files for the process that reads them. |
322
+ | `buildCaptureDiscoveryConfig` | function | Builds the empty Node configuration a wrapper returns once it has answered a discovery request. |
323
+ | `readCaptureDescription` | function | Accepts the description a wrapper wrote only when it answers this exchange and no other. |
324
+ | `answerCaptureDiscovery` | function | Answers the runner's discovery request from the wrapper, before the wrapper does any other work. |
325
+ | `readCaptureRegistryArguments` | function | Reads the registry command line into the wrapper path, the variants to refresh, and the mode. |
326
+ | `readCaptureInputs` | function | Reads the bounded input population a project declares, as file paths and content hashes. |
327
+ | `writeCaptureDocument` | function | Writes one JSON document atomically, so a reader sees the old bytes or the new and never a part. |
328
+ | `readCaptureAncestor` | function | Finds the nearest existing path without following a dangling link. |
329
+ | `resolveCapturePhysical` | function | Resolves an existing path or its nearest existing ancestor to its physical destination. |
330
+ | `requireCaptureContained` | function | Validates a physical destination against a physical root before any read or write. |
331
+ | `resolveCaptureSheetIdentity` | function | Names a sheet group by portfolio, owner, and state independently of its output variant. |
332
+ | `splitCapturePages` | function | Splits a list into consecutive pages with at most the requested size. |
333
+ | `matchesCaptureFile` | function | Compares file content, size, modification time, and change time across two snapshots. |
334
+ | `mergeCaptureLookups` | function | Merges unique lookup populations with incoming identities replacing inherited ones. |
335
+ | `describeCaptureFault` | function | Describes a refusal with its key path, bounded preview, correction, and member differences. |
336
+ | `resolveCaptureExit` | function | Maps usage refusals to exit 64 and every other failure to exit 1. |
337
+ | `requireCaptureArgument` | function | Validates a supplied path argument before work and classifies an outside value as usage. |
338
+ | `matchesCaptureTransient` | function | Recognizes filesystem contention codes that ownership arbitration can retry. |
339
+ | `settleCaptureShutdown` | function | Settles independent request removal and child termination without hiding either failure. |
340
+ | `describeCaptureMembers` | function | Describes a bounded member population and the number omitted from its preview. |
341
+ | `describeCaptureOutput` | function | Caps external diagnostic text and names the number of omitted characters. |
342
+ | `requireCaptureSelection` | function | Refuses unknown command selections from the wrapper before authenticating disk evidence. |
343
+ | `readCaptureVariants` | function | Reads output variant candidates without authenticating or accepting registry evidence. |
344
+ | `readCaptureImage` | function | Reads an image only after its opened file's size is inside the compressed-byte budget. |
345
+ | `readCaptureSeparator` | function | Reads the installed workspace Vitest's exact title separator, refusing unsupported majors. |
346
+ | `readCaptureTitles` | function | Reads original title segments from the accepted registry's authenticated JSON reporter files. |
347
+ | `requireCaptureImageSize` | function | Refuses compressed image bytes before file reads or chunk-buffer allocation. |
348
+
349
+ ### Server classes and factories
350
+
351
+ The integrity engine from [`CaptureIntegrity.ts`](../src/server/CaptureIntegrity.ts) and its factory from [`factories.ts`](../src/server/factories.ts).
352
+
353
+ | Name | Kind | Summary |
354
+ | ------------------------ | -------- | ------------------------------------------------------------------------------------------------------------ |
355
+ | `CaptureIntegrity` | class | Validates capture transactions, freshness, and state lookup against real filesystem evidence. |
356
+ | `createCaptureIntegrity` | function | Creates the integrity engine bound to an explicit workspace and host portfolio mapping. |
357
+ | `CaptureRunner` | class | Drives selected owners through one journey stage or staged frame captures. |
358
+ | `CaptureInvocation` | class | Owns one runner invocation's identity, request file, children, and interruption. |
359
+ | `CaptureDiscovery` | class | Reads a consumer's wrapper by running the consumer's installed Vitest in discovery mode. |
360
+ | `createCaptureDiscovery` | function | Creates the discovery that reads a consumer's wrapper through the consumer's own Vitest. |
361
+ | `createCaptureRunner` | function | Creates the runner that drives owner-selected journeys for one command line. |
362
+ | `CapturePreflight` | class | Reads the selected population's frames and reports their freshness, mixed observations, and coverage. |
363
+ | `CaptureSheets` | class | Composes exactly the authenticated sparse state and output pairs for iteration review. |
364
+ | `createCapturePreflight` | function | Creates the preflight that judges the selected frames' freshness, mixed observations, and coverage. |
365
+ | `createCaptureSheets` | function | Creates the contact-sheet writer for the selected frames. |
366
+ | `CaptureRegistrar` | class | Refreshes the accepted registry from one complete capture-free journey per producing variant. |
367
+ | `createCaptureRegistrar` | function | Creates the registry refresh that runs each producing variant's complete journey and publishes the registry. |
368
+ | `CaptureCLI` | class | Dispatches capture commands and maps usage and evidence refusals to their exit codes. |
369
+ | `createCaptureCLI` | function | Creates the executable's command dispatcher over a workspace and injected writers. |
370
+
371
+ ## Methods
372
+
373
+ #### `CaptureIntegrityInterface`
374
+
375
+ The integrity interface exposes the following methods:
376
+
377
+ | Name | Summary |
378
+ | --------- | ---------------------------------------------------------------------------------------- |
379
+ | `read` | Reads current full-collection evidence and selects exact declaration destinations. |
380
+ | `stamp` | Validates an executed stage before atomically publishing its authorized evidence. |
381
+ | `check` | Checks the explicitly selected output population without changing evidence or images. |
382
+ | `lookup` | Resolves an explicitly requested state through fresh registry and passing case evidence. |
383
+ | `changed` | Classifies changes since a Git merge-base through the accepted registry. |
384
+
385
+ #### `CaptureDiscoveryInterface`
386
+
387
+ The discovery interface exposes the following method:
388
+
389
+ | Name | Summary |
390
+ | ---------- | -------------------------------------------------------------------------------------------------------------- |
391
+ | `describe` | Publishes the discovery request, runs the wrapper's Vitest as a bounded child, and reads back its description. |
392
+
393
+ #### `CapturePreflightInterface`
394
+
395
+ The preflight interface exposes the following methods:
396
+
397
+ | Name | Summary |
398
+ | --------- | ---------------------------------------------------------------------------------------------- |
399
+ | `read` | Reads the authenticated selection, uniform scopes, and the host's declared viewports. |
400
+ | `check` | Judges every selected frame and returns the mixed observations, freshness, images, and issues. |
401
+ | `execute` | Executes the check and prints its report. |
402
+
403
+ #### `CaptureRunnerInterface`
404
+
405
+ The runner interface exposes the following method:
406
+
407
+ | Name | Summary |
408
+ | --------- | -------------------------------------------------------------------------- |
409
+ | `execute` | Executes the command line without widening its owner or variant selection. |
410
+
411
+ #### `CaptureInvocationInterface`
412
+
413
+ The invocation interface exposes the following methods:
414
+
415
+ | Name | Summary |
416
+ | ---------- | ----------------------------------------------------------------------------------------------------------------------- |
417
+ | `attach` | Installs the interrupt handler on the process. |
418
+ | `release` | Releases confirmed ownership and removes interrupt handlers even when shutdown fails. |
419
+ | `prepare` | Claims invocation ownership, refuses an existing marker, and removes temporary claims and requests from dead processes. |
420
+ | `holders` | Lists the other live processes that hold a request file in the request directory. |
421
+ | `publish` | Writes the stage or discovery request atomically, so no reader sees a partial file. |
422
+ | `retract` | Removes this process's request file and its temporary sibling. |
423
+ | `launched` | Reports whether a child with this log path has been spawned by this invocation. |
424
+ | `spawn` | Spawns the Vitest child and tees its output to the host streams and the log. |
425
+
426
+ #### `CaptureSheetsInterface`
427
+
428
+ The sheets interface exposes the following method:
429
+
430
+ | Name | Summary |
431
+ | --------- | ----------------------------------------------------------------------------------------- |
432
+ | `execute` | Reads selected physical images, writes complete indexed sheets, and retains all refusals. |
433
+
434
+ #### `CaptureRegistrarInterface`
435
+
436
+ The registrar interface exposes the following method:
437
+
438
+ | Name | Summary |
439
+ | --------- | ------------------------------------------------------------------------------------------------- |
440
+ | `execute` | Runs each producing variant's complete journey, imports the receipts, and publishes the registry. |
441
+
442
+ #### `CaptureCLIInterface`
443
+
444
+ The CLI interface exposes the following method:
445
+
446
+ | Name | Summary |
447
+ | --------- | -------------------------------------------------------------------------- |
448
+ | `execute` | Returns 0 for passed work, 1 for failed evidence, or 64 for refused usage. |
449
+
450
+ ## Server modules
451
+
452
+ The server modules implement the evidence transaction, process protocol, readers, registry refresh, and executable.
453
+
454
+ ### Integrity
455
+
456
+ The stage transaction, the freshness reading, and the state resolution engine. `createCaptureIntegrity` binds an engine to a workspace root, the evidence documents it reads and writes, the bounded input patterns, and the authorized portfolio roots. The engine infers no portfolio, owner, source pattern, or application path from a name; the host supplies each one.
457
+
458
+ Bind the integrity engine to explicit evidence paths and read a scoped freshness population:
459
+
460
+ ```ts
461
+ import { createCaptureIntegrity } from '@orkestrel/capture/server'
462
+
463
+ const integrity = createCaptureIntegrity({
464
+ root: process.cwd(),
465
+ directory: 'tmp/captures/states',
466
+ inputs: ['src/**'],
467
+ registry: 'tmp/captures/states/registry.json',
468
+ manifest: 'tmp/captures/states/manifest.json',
469
+ lookups: 'tmp/captures/states/lookups.json',
470
+ portfolios: [{ portfolio: 'states', directory: 'tmp/captures/states', scoped: true }],
471
+ })
472
+ const result = await integrity.check({ selector: 'pages:mail' })
473
+ result.entries.map((entry) => entry.status) // one classification per selected frame
474
+ ```
475
+
476
+ `stamp` is the only method that writes. It snapshots the bounded inputs, runs the runner's child once, and validates every selected final record against the reporter before it replaces the manifest in one atomic rename; a failure before that rename leaves the prior manifest bytes untouched, and a journey publishes lookups and full-collection receipts in the separate journal instead. `check` classifies each selected frame as `duplicate`, `orphan`, `missing`, `unstamped`, `stale-shared`, `stale-own`, or `current`, and keeps every finding that justified the class. `read` and `lookup` start no process: they resolve a selection or a state from evidence, and a refusal names the explicit refresh command to run.
477
+
478
+ Every refusal is a `CaptureError`. An unknown value for a declared selector, variant, state, case, or portfolio is `USAGE`, as is an empty selector; internal selection disagreements carry `SELECTION`. A wrapper fault is `HOST`; a stage that did not complete or whose inputs moved is `STAGE`; a record, report, registry, or manifest that fails its protocol is `EVIDENCE`; and a path that leaves the integrity root or an authorized portfolio root is `CONTAINMENT`. The executable maps `USAGE` to exit 64 and every other failure to exit 1. Commands check selectors against the discovered wrapper before registry authentication. Reader variants derive from the selected owner and portfolio before state and case validation, so an unknown state or case names its own candidates.
479
+
480
+ The engine reads each file once per pass through a memo it discards when the pass ends, so a source hashed before the child, in the record pass, and in the final check costs three readings, never one per destination. Evidence reading also memoizes validated physical paths for that pass. The record pass and final check each validate an identical own/shared/tests pattern triple once against its pre-stage input population. Physical-image comparisons read each path's identity once per pass. The final check rereads disk with fresh memos, so a file changed between passes refuses the stage.
481
+
482
+ **Lookup journal.** `lookups.json` holds `version`, an optional predecessor `manifest` digest, an `inputs` table, `lookups`, and optional `collections`. Each table key is the digest from `buildCaptureDigest([], files)`, where `files` is the stored path-to-hash map. Each lookup's `inputs` field names a table key, so lookups that share the same input population store that map once. The engine verifies each table map's digest before expanding the active journal. A missing reference refuses with `Lookup names an unknown inputs digest` and the digest; an altered map refuses with `Lookup input digest differs from its map`. Malformed schemas, duplicate lookup identities, missing or changed run records, unauthorized runner records, registry declaration disagreements, and mismatches with passing declarations retain their refusals. Stale input hashes still refuse state resolution. Journal publication uses the same atomic rename, and the predecessor manifest digest still determines whether the journal applies. Image manifests and returned lookup results retain expanded input maps.
483
+
484
+ ### Runner
485
+
486
+ The request protocol, the stage order, and the process supervision. `createCaptureRunner` binds one command (`journey`, `capture`, or `stale`), a workspace root, the wrapper's default path, the request directory, and the command line after the entry name. The runner never loads the wrapper itself, so the package imports neither Vite nor Vitest: it discovers the wrapper through the consumer's own Vitest, as the following Discovery paragraph describes. It reads every name it uses from the wrapper's provided values: `variant` and `variants`, `first`, `final`, `uniform`, `portfolios`, `standalone`, `directory`, `record`, and `inputs`, and the project tags that carry `owner:` paths. It checks command syntax and the wrapper path before discovery. The discovery child runs before the runner validates host declarations and resolves the selection; those checks precede every execution child.
487
+
488
+ **Host data.** Each project's `test.provide` can declare `uniform`, a list of unique valid owner selectors whose frames the preflight judges for mixed observations. Omission judges every scope; `uniform: []` judges none. Selectors match owners at colon boundaries, so `pages` includes `pages:mail`, and `pages:mail` includes its sections. Every project must agree on the declaration as a population, regardless of order, and omission differs from an empty list. A malformed list or selector refuses as `HOST`, duplicate entries refuse as `EVIDENCE`, and disagreeing projects refuse as `HOST`. Discovery preserves the declaration in each project's host description, and the preflight carries it into its reader selection. To judge page and example frames, put the following line beside `final`, `first`, and `portfolios` in every project's `test.provide` object.
489
+
490
+ ```ts
491
+ const provide = {
492
+ // The wrapper's other host declarations are omitted.
493
+ uniform: ['pages', 'examples'],
494
+ }
495
+ ```
496
+
497
+ Bind the runner to its command, workspace, wrapper, request directory, and arguments.
498
+
499
+ ```ts
500
+ import { createCaptureRunner } from '@orkestrel/capture/server'
501
+
502
+ const runner = createCaptureRunner({
503
+ command: 'journey',
504
+ root: process.cwd(),
505
+ config: 'configs/app/vite.journey.config.ts',
506
+ requests: 'tmp/units',
507
+ argv: ['pages:mail', '--variant', 'light-390'],
508
+ })
509
+ const exit = await runner.execute() // 0 when every stage passed and work ran
510
+ ```
511
+
512
+ The exit is 0 when every stage is green and work ran, 64 for `USAGE`, and 1 for every other failure. Unknown selector, variant, state, case, and portfolio arguments, empty selectors, and refused command syntax carry `USAGE`. A wrapper fault is `HOST`; a failed or interrupted stage is `STAGE`. Discovery, ownership, and evidence failures can occur before an execution child starts and still exit 1. The runner maps the condition, never the message text or whether a child started. Before any execution stage, an explicit case is checked against the accepted registry's authenticated reporter collection. A missing registry refuses with `capture registry`; an unknown case refuses with its candidates and exit 64.
513
+
514
+ **Journey stage.** A journey runs all selected variants in declaration order in one execution child with `--bail=1`, under the stage name `selected`. Its stage line names every selected variant, its request carries that same list, and one stamping transaction validates all selected records before publishing lookup and collection receipts. Records remain keyed by run, stage, and variant at `<directory>/runs/<id>/selected/<variant>.json`. A failed, bailed, or interrupted child publishes no evidence from that stage. An invocation with no applicable work exits 1.
515
+
516
+ **Frame stages.** `frames` keeps the `first` variant alone in `narrow`, then every other selected variant together in `rest`, so narrow frames can be inspected first. Omitting the first variant runs only `rest`; the runner adds no unselected variant. A failed stage stops later stages. An inapplicable narrow stage prints why the rest stage carries the selector and continues. A `STAGES` JSON line names the stages run, passed, failed, and not run, with the reason, followed by an exact `NEXT` command.
517
+
518
+ **Changed selection.** For journey or frames, use `--changed <ref>` instead of a selector. The flag takes exactly one Git ref and cannot accompany a positional selector. `HEAD` selects uncommitted work. The engine authenticates the accepted registry, resolves the ref's merge-base with HEAD, reads `git diff --name-only --no-renames -z <merge-base> --`, and adds `git ls-files --others --exclude-standard -z`. Renames contribute both paths; deleted paths and names containing spaces remain intact. Every Git read uses `executeSync` with a 30-second deadline and a 64 MiB output limit; failed or truncated reads are refused before any NUL-separated names are consumed. An unknown ref, failed Git read, timeout, or truncated result exits 1 as a `HOST` failure. A missing registry prints `Missing accepted registry; run the explicit complete-registry refresh` and the exact registry command.
519
+
520
+ Each path gets exactly one class through the registry's native glob patterns. `own` matches a destination's own sources and names every matching owner. `shared` matches any shared or test pattern and names every destination owner; it takes precedence when a path also matches own sources. `outside` matches no source pattern and names no owner. The selected owners come from own paths, sorted and reduced by colon-delimited ancestry, so selecting a page drops its selected sections.
521
+
522
+ **Plan.** Add `--plan` to `--changed` to print a `CHANGE` JSON line per path, then `COMMAND` lines containing the exact per-owner commands. The plan uses the read-only discovery child to learn the wrapper declarations and starts no execution child. It exits 0 for a runnable or empty plan, 64 for usage refusals including shared-input narrowing, and 1 for other failures including Git and evidence failures. A `SUMMARY` JSON line says what ran, passed, failed, and did not run, with the reason; `NEXT` lines give the commands to execute. For a mail source change, the plan includes these lines:
523
+
524
+ ```text
525
+ CHANGE {"path":"src/mail/view.ts","classification":"own","owners":["pages:mail"]}
526
+ COMMAND capture journey pages:mail --config configs/app/vite.journey.config.ts
527
+ SUMMARY {"run":[],"passed":[],"failed":[],"notrun":["pages:mail"],"reason":"--plan starts no execution child"}
528
+ NEXT capture journey pages:mail --config configs/app/vite.journey.config.ts
529
+ ```
530
+
531
+ Any shared path refuses the whole changed run with exit 64, listing those paths and explaining that every destination reads them, so they cannot be narrowed. This refusal is usage because the invocation must change to an explicit journey selection. The output includes per-owner commands for own changes and `capture journey all` with the effective wrapper and variant flags for the shared proof. No owner starts automatically. Without own or shared paths, an execution exits 1 with `No journey input changed` and lists the outside paths.
532
+
533
+ Without `--plan`, the runner executes each reduced owner through the ordinary single-selector path in stable order and stops at the first red owner. Every owner gets a distinct run ID, so its stage records remain available. The final `SUMMARY` names owners run, passed, failed, and not run, gives the stopping reason, and prints the exact commands for failed and unrun owners. `--variant`, `--case`, and the frames-only `--state` retain their ordinary meanings; a command printed for the shared proof carries no case or state narrowing. The changed flags never enter the request protocol.
534
+
535
+ ### Choosing what to run
536
+
537
+ **Argument paths.** A held ownership marker refuses before argument values are judged. A supplied `--config`, `--journeys`, `--checks`, or `--output` path that resolves outside the repository refuses as `USAGE` (64). Containment caught on a resolved target at write time stays `CONTAINMENT` (1). Explicit cases must belong to the selected selector's owners; cases registered only under another owner refuse with candidates and no execution stage.
538
+
539
+ Climb from the smallest applicable selection to the gate:
540
+
541
+ - One case on one variant: `capture journey pages:mail --case 'mail shows the inbox' --variant light-390`.
542
+ - The changed set: inspect `capture journey --changed HEAD --plan`, then run `capture journey --changed HEAD`. For frames, use `capture frames --changed HEAD --plan`, then `capture frames --changed HEAD`.
543
+ - A section or page: `capture journey pages:mail:inbox` or `capture journey pages:mail`.
544
+ - A group: `capture journey pages`.
545
+ - The gate: `capture journey all`.
546
+
547
+ Use only owner and case names the wrapper declares. Follow a refusal's exact next commands before widening a selection.
548
+
549
+ ### Runner selection and process protocol
550
+
551
+ **Selection.** A selector is `all` or an owner path the wrapper's tags declare; each segment is checked, and the refusal names the first unknown one, labelled `scope` at the first depth and `owner` below it. A journey filters on `owner:<selector>`. A capture also intersects the capture tag, which is the selector's scope and part, and `all` unions every declared scope. `--case` selects one case by its exact full name, and the runner passes the anchored pattern for that case and the final case with every regular-expression metacharacter quoted. The runner reads original title segments from authenticated registry reporters and joins them with the installed Vitest major's separator: a space for Vitest 4 and a `>` character surrounded by spaces for Vitest 5. `--state` asks the engine for the declaring case after discovery and refuses before any execution child when the engine's evidence is missing, stale, or ambiguous.
552
+
553
+ **Discovery.** Before it plans anything, the runner publishes an atomic request whose phase is `discover` and spawns the consumer's installed Vitest entry directly (`process.execPath` with `node_modules/vitest/vitest.mjs`) as `list --filesOnly --config <wrapper> --configLoader runner`, with the capture variable set so a wrapper's destructive no-capture initialization never runs. The wrapper calls `answerCaptureDiscovery` before it does any other work, writes its description atomically beside the invocation's logs, and returns `buildCaptureDiscoveryConfig()`, an empty Node configuration, so Vitest sets up no browser, initializes no output, and collects no test. The runner accepts the description only after the child exited zero and every echo in it matches the exchange: the invocation, a fresh challenge, the wrapper, the runner as the wrapper's parent, and the spawned child as its writer. A wrapper that never answers, answers another exchange, writes an unreadable or empty description, exits nonzero, or outlives the deadline (`CAPTURE_DISCOVERY.deadline`, replaceable through the `deadline` option) is refused as a `HOST` condition, and a request from another parent, with other arguments, version, identity, or wrapper, is refused by the wrapper. A syntax error in the command line refuses before any child; an unknown selector or variant refuses after discovery and before any execution child.
554
+
555
+ The wrapper answers discovery before initializing outputs:
556
+
557
+ ```ts
558
+ import { answerCaptureDiscovery, buildCaptureDiscoveryConfig } from '@orkestrel/capture/server'
559
+
560
+ const projects = [/* the wrapper's own test.projects */]
561
+ const answered = await answerCaptureDiscovery({
562
+ ppid: process.ppid,
563
+ argv: process.argv.slice(1),
564
+ config: 'configs/app/vite.journey.config.ts',
565
+ projects,
566
+ })
567
+ export default answered ? buildCaptureDiscoveryConfig() : { test: { projects } }
568
+ ```
569
+
570
+ **Request directory.** The one default is `CAPTURE_DEFAULTS.requests`. The runner hands the directory it publishes to every child it spawns in `CAPTURE_REQUESTS`, and `readCaptureRequest` and `answerCaptureDiscovery` read it from there when the wrapper names no `directory` of its own, so a consumer never repeats a constant it could mismatch. A wrapper that does name one declares it, and it must be the runner's.
571
+
572
+ **Request.** Before each stage the runner writes `journey-selection-<pid>.json` in the request directory, named by its own process id, through a temporary sibling and a rename, and removes both in a `finally` block. Before it writes, it removes every request file named for a process that is no longer running, and never one that is. The wrapper reads the file back through `readCaptureRequest`, which accepts only the file its parent's id names, only a record whose `pid` matches, and only arguments equal to its own process's.
573
+
574
+ The execution wrapper reads its parent's selection request:
575
+
576
+ ```ts
577
+ import { readCaptureRequest } from '@orkestrel/capture/server'
578
+
579
+ const request = readCaptureRequest({
580
+ ppid: process.ppid,
581
+ argv: process.argv.slice(1),
582
+ config: 'configs/app/vite.journey.config.ts',
583
+ owners: ['pages:mail'],
584
+ variants: ['light-390'],
585
+ })
586
+ request?.selector // 'pages:mail' when a runner is the parent, otherwise undefined
587
+ ```
588
+
589
+ **Processes.** The runner spawns `process.execPath` with the installed `node_modules/vitest/vitest.mjs`, an argument array, and no shell. The child inherits the environment with every case-folded `capture` name removed and `CAPTURE_REQUESTS` set to the request directory, and a `capture` command, like discovery, sets exactly one `CAPTURE=1`. Each stage writes its own log and JSON report under `<requests>/runs/<id>/`, and the child's output is teed to the host's streams and the log. Each child runs through `@orkestrel/process` `createSession` with stdin closed immediately. Its byte output is decoded with `StringDecoder`, preserving split UTF-8, carriage returns, and missing final newlines, and the decoder is flushed at termination. A writer or log failure, or a session whose output did not drain before its observation bound, refuses the stage. An interrupt aborts the stage's signal and ends each child's tree: Windows tree termination, or POSIX `SIGTERM` followed by `SIGKILL` after a 250 ms grace. A pre-aborted invocation launches no child. The invocation then removes its request and releases its handlers.
590
+
591
+ **Host evidence (2026-10-28).** A real console Ctrl+C on Windows and a process-group SIGINT through the built executable on Linux each ended the stage tree, started no later stage, removed the request, and left the manifest bytes unchanged. The Windows reading uses a console-capable terminal; the POSIX automated case runs only where its process-group mechanism is available.
592
+
593
+ Every command removes its invocation's run directory on a usage refusal (exit 64). A failure with exit 1 after discovery retains that directory and its logs as evidence.
594
+
595
+ Discovery uses `@orkestrel/timeout` `createTimeout`, linked to the invocation's signal. Expiry is read from the armed signal before cleanup, so clearing the handle or receiving a later interrupt cannot erase it. An expired discovery is a `HOST` refusal; interruption remains a `STAGE` refusal and retains precedence if both are present when discovery settles. Neither a deadline nor a writer failure sets the invocation's interrupted state.
596
+
597
+ Atomic evidence writes use an exclusive temporary sibling and `writeFileSync` with `flush: true`, then check cancellation before renaming. Integrity publication also rechecks containment of both paths before the rename. A refused write preserves the prior document and cleans its temporary sibling.
598
+
599
+ ### Preflight and sheets
600
+
601
+ The image checks and the contact sheets over the authenticated selection. `createCapturePreflight` binds a workspace root, the wrapper's absolute path, a selection, and the absolute directory of the journey artifacts. It discovers the wrapper through the consumer's Vitest the way the runner does, with a `requests` option for the request directory and a `deadline` option for the child, and reads each project's `variant`, its `variants` with their pixel geometry, and the portfolio roots it provides, so it names no portfolio, owner, variant, or path itself. `createCaptureSheets` takes the same reader options and a new output directory.
602
+
603
+ Use the same reader selection for preflight and sheets:
604
+
605
+ ```ts
606
+ import { createCapturePreflight, createCaptureSheets } from '@orkestrel/capture/server'
607
+
608
+ const reader = {
609
+ root: process.cwd(),
610
+ config: `${process.cwd()}/configs/app/vite.journey.config.ts`,
611
+ selection: { selector: 'pages:mail', variants: ['light-390'] },
612
+ journeys: `${process.cwd()}/tmp/journey`,
613
+ }
614
+ const exit = await createCapturePreflight(reader).execute() // 0 when every frame is current and sound
615
+ await createCaptureSheets({ reader, output: `${process.cwd()}/tmp/sheets` }).execute()
616
+ ```
617
+
618
+ **Preflight.** `check` judges freshness, images, artifact coverage, logged errors, and host checks; both `execute` and the sheets consume that result. `execute` prints one `READER` line of JSON and returns 0 when every selected frame is current and no issue was found. It returns 64 for `USAGE`, including unknown or empty selectors, and 1 for every other failure, including wrapper, checks-module, containment, and evidence failures. A thrown refusal goes to the error writer without a report. The report carries the selection, the freshness the engine classified, the mixed observations, every issue, the groups of byte-identical images, the size of each image, and `acceptance: false`: a passing preflight is iteration evidence and never assembled-portfolio acceptance. The preflight upgrades no freshness classification, and it widens no selection.
619
+
620
+ **Images.** Each selected destination is read from disk, refused below 4,000 bytes, and decoded. The reader judges each decoded frame and runs its host checks before moving to the next frame. The returned `images` hold source dimensions in `picture` and a bounded `thumbnail` for sheets; full pixel buffers reach host checks and are not retained in the result. A frame whose image cannot be read stays in the report as an issue and, in the sheets, as a red mark indexed `missing`. The frame scan groups the source geometry by portfolio, owner, and declared viewport width, and names a group whose frames mix cropped, viewport-sized, and page-sized images. It reads only a frame at its own producing variant, and it never decides which framing was intended. When the wrapper declares `uniform`, only images whose owner matches a declared selector enter those groups; an empty list produces no mixed observations. Without the declaration, every scope enters. Frames outside the declared scopes retain their freshness, coverage, image issues, host checks, and sheet cells.
621
+
622
+ `CAPTURE_PNG_LIMITS` caps decoded frames at 20,971,520 pixels, with about 19.5% margin over the fleet's largest measured Chromium full-page frame, 1170 × 15000; 2880 × 6000 also fits. Each side is capped at 131,072 pixels. The byte budget is `ceil((4 × pixels + side) × 1.2)`, or 100,820,583 bytes: maximum RGBA scanlines plus a 20% allowance for deflate overhead, chunk framing, and metadata. The file size is checked before reading and before compressed chunks are joined; a single IDAT is inflated directly from its view. Pixel and side checks precede inflation. PNG chunks are parsed iteratively and bounded by file bytes, so a valid many-chunk encoder stream is admitted. Isolated Node runs on Windows on 2026-10-28 peaked at 276–333 MiB process RSS for the fleet frames, 438 MiB for stored-deflate RGBA at the pixel boundary, 456 MiB for 131072 × 160, and 171 MiB for a valid 1 × 1 PNG padded to the byte boundary. A file one byte over refused before reading at 74 MiB process RSS. These are fixture measurements, not a universal RSS ceiling. The budget limits each decode; selected frame counts do not multiply retained full-frame storage.
623
+
624
+ **Artifacts.** The reader judges each frame by the run record its own stamp names, never by a variant's latest file. It reads that record, requires the journey artifact the record names to sit inside the journeys directory and to match the record's digest, and then requires the record to cover the frame, the artifact to bind the record with a `record=` line, and the artifact to show the stamping case with a `case-ran=` line. It also reports every artifact line that logs an uncaught, unhandled, or error entry. A frame with no stamp is an issue, never a guess. A standalone portfolio root the wrapper declares has no stamping route, so a selection that names one reads that root alone and reports its frames as unstamped.
625
+
626
+ **Host checks.** Rules that belong to one product do not belong to the package. The `checks` option names a module, absolute or relative to the root, and the reader loads it with a dynamic `import()` of its file URL. The module exports `CAPTURE_CHECKS`, a list of readers from a frame to its findings, and the reader hands each one the frame's destination, its decoded picture, and its verified artifact. The picture is `undefined` when the image could not be read and the artifact is `undefined` when none covers the frame, so a check decides what an absent one means. Every finding joins the report's issues.
627
+
628
+ For example, a host check can refuse a frame narrower than its product requires:
629
+
630
+ ```ts
631
+ import type { CaptureCheckFrame } from '@orkestrel/capture/server'
632
+
633
+ export const CAPTURE_CHECKS = [
634
+ (frame: CaptureCheckFrame): readonly string[] =>
635
+ frame.picture !== undefined && frame.picture.width < 100
636
+ ? [`Frame is too narrow: ${frame.entry.file}`]
637
+ : [],
638
+ ]
639
+ ```
640
+
641
+ A module that cannot be loaded, exports no list of functions, or holds a reader that returns something other than a list of strings refuses the run as a `HOST` condition before a report prints.
642
+
643
+ **Sheets.** `execute` validates the supplied output path after claiming ownership and before preflight work. It reads the selection through the preflight, so it judges the same population and keeps its refusals. It requires an output directory that does not exist, lies inside the workspace, has an existing parent, and lies outside every selected capture directory. A supplied outside or unreadable path refuses with exit 64; other output refusals and containment caught at write time exit 1 before writing. It shrinks each frame by a whole factor toward 320 pixels wide with an exact box area average over typed arrays. Every source pixel in a displayed block contributes once, and the divisor is that block's actual pixel count, including partial edge blocks. Accumulators hold one output row. It clips thumbnails at 400 pixels tall, places a state's variants side by side, pairs two states per row, and stacks four rows per sheet. Work is proportional to the source pixels used by the displayed blocks; the 131072 × 160 and largest accepted frame performance proofs enforce a 1,000 ms ceiling. It writes `sheet-001.png` and onward, an `index.json` of every cell with its source and displayed size, its freshness, and the mixed observations, and an `index.md` that links each frame. It prints the index as one `SHEETS` line and returns the same exit status the preflight would.
644
+
645
+ ### Registry refresh
646
+
647
+ The command that bootstraps or replaces the accepted registry. The stage transaction refuses to run until a registry exists, so the refresh cannot stamp through the engine: it imports complete runs instead. `createCaptureRegistrar` takes the same options as the runner minus the command, and `execute` reads the wrapper through the same `CaptureDiscovery` the runner uses, forwarding `--mode` to it, takes the same invocation entity, request file, and Vitest child, and runs one stage per producing variant with the `all` selector, the `journey` command, and no capture variable.
648
+
649
+ Refresh a selected producing variant through the registrar:
650
+
651
+ ```ts
652
+ import { createCaptureRegistrar } from '@orkestrel/capture/server'
653
+
654
+ const registrar = createCaptureRegistrar({
655
+ root: process.cwd(),
656
+ config: 'configs/app/vite.journey.config.ts',
657
+ requests: 'tmp/capture',
658
+ argv: ['--variant', 'dark-390'],
659
+ })
660
+ const exit = await registrar.execute() // 0 when the registry was published
661
+ ```
662
+
663
+ **Flags.** `--variant` repeats and names the producing variants to refresh; without it every variant the wrapper declares is refreshed. `--config` replaces the wrapper. `--mode` is forwarded to the wrapper's Vitest process as `--mode <name>` and recorded in the request, so a wrapper that selects another registry by mode is refreshed by naming it. The command takes no selector, and every flag takes one value.
664
+
665
+ **Stages.** The stage for a variant is named `registry-<variant>`, so each variant's records sit under `<directory>/runs/<id>/registry-<variant>/`. The command takes the project's declared input population and its content hashes before the child starts and again after, and refuses when they differ. It then reads the reporter and the final record, requires the record to carry the stage's own request, to fall between the start of the refresh and the end of the reporter, to agree with every collected declaration, and to be complete, and builds one receipt from them: the variant, the record's time, the reporter's path and digest, the input hashes, and the record's declarations and registry.
666
+
667
+ **Publication.** `buildCaptureRegistry` imports the receipts only after each exit is zero and each reporter digest still equals its receipt. The registry is written to a candidate beside `registry.json`, read back through the integrity engine pointed at the candidate, and only then renamed over the target, so a registry the engine would refuse never replaces a good one and the old bytes survive a failed or interrupted refresh. The command prints one `REGISTRY` line of JSON: the file, the digest, the variants in the registry, the variants this run refreshed, and the entry count.
668
+
669
+ **Partial refresh.** With `--variant`, the receipts the existing registry holds for the other declared variants are kept: each is read again with its reporter bytes and imported beside the new ones before any journey runs, so a reporter that changed since it was recorded refuses the run first. An existing registry the package cannot read refuses a partial refresh and names the full refresh that replaces it. A receipt for a variant the wrapper no longer declares is dropped.
670
+
671
+ **Lease.**
672
+
673
+ Serialize capture commands and the host's Vitest work per workspace. Preparation creates the
674
+ request directory and checks that it is a directory. An invocation writes `{pid,id}` once to
675
+ a unique `.capture-owner.<pid>.<id>.tmp` file with flag `wx`, then claims `.capture-owner`
676
+ with `linkSync`. The marker stays byte-identical from that link until its own invocation
677
+ unlinks it after confirmed child termination. It stays held between stages. The temporary
678
+ is always removed after the claim attempt. A dead pid's leftover claim temporary is swept
679
+ best effort; sweep errors are ignored, and a live pid's temporary is never touched.
680
+ The marker does not coordinate another request directory or unrelated Vitest processes.
681
+
682
+ A held marker is parsed as `{pid,id}`, ignoring extra members. EPERM from the pid probe
683
+ counts as alive. After the liveness probe the claim re-reads the marker; a vanished or
684
+ changed marker retries. Deletion advice for a dead holder requires identical content on
685
+ that re-read. A live-holder refusal makes any deletion conditional on confirming that no
686
+ capture process or surviving child is running. Empty or malformed markers require
687
+ identical reads separated by backoff and ask for inspection. A permanent read error
688
+ refuses as unreadable without deletion advice.
689
+
690
+ Marker reads, links, and removals retry only `EBUSY` and `EMFILE` on POSIX, and those
691
+ codes plus `EPERM` and `EACCES` on Windows, with synchronous 10 ms backoff within
692
+ 1,000 ms. On POSIX, a link failure with `EPERM`, `ENOTSUP`, `EOPNOTSUPP`, `ENOSYS`,
693
+ or `EXDEV` refuses immediately with the hard-link requirement. Exhaustion names the
694
+ error code and gives no deletion advice. Other temporary-write or link errors refuse
695
+ permanently and name the operation and code.
696
+
697
+ Release awaits signal-driven termination, retracts its request, and stops every child.
698
+ It reads its marker and checks its invocation id before acting; an absent or foreign
699
+ marker is left alone. Unconfirmed termination keeps the marker byte-identical and the
700
+ lease held. Release writes `runs/<id>/survivors.json` with flag `wx`, treats `EEXIST`
701
+ as already recorded, and on `ENOENT` creates the run directory and retries once.
702
+ Other write errors join the shutdown failure. The record contains the owner pid, run id,
703
+ command, every unconfirmed child pid, and the ISO timestamp of the attempt:
704
+
705
+ ```text
706
+ {"pid":1234,"id":"RUN_ID","command":"journey","children":[5678],"at":"2026-10-28T00:00:00.000Z"}
707
+ ```
708
+
709
+ Release throws STAGE, naming the unconfirmed pids, marker, and record. A later release
710
+ in the same process retries the stop. After termination is confirmed, release removes
711
+ only its own marker, re-reading and checking its id on every removal retry. A successful
712
+ release leaves no claim temporary or marker from that invocation. An exhausted or
713
+ permanent removal failure retains the marker and joins the shutdown failure.
714
+
715
+ A dead-holder refusal reads `runs/<holder id>/survivors.json` once, without retry.
716
+ A well-formed record names its pids and path. ENOENT reports absence; other errors report
717
+ the code, and malformed content reports EINVAL. The record is advisory; the marker
718
+ provides ownership exclusion. A recorded pid is unconfirmed, not a claim that it is alive.
719
+ Stop any recorded children and confirm the marker's content before deleting it.
720
+
721
+ The following messages use `PID`, `RUN_ID`, `PIDS`, `MARKER`, `RECORD`,
722
+ `TEMPORARY`, `DIRECTORY`, `CONTENT`, and `CODE` as placeholders for the actual values. `RECORD_STATUS`
723
+ is one of the record messages that follows the dead-owner refusal.
724
+
725
+ ```text
726
+ Capture ownership process PID is not running; invocation "RUN_ID". RECORD_STATUS Confirm no capture process or surviving child is running and re-read MARKER to confirm it still names pid PID before you delete this file and rerun
727
+ Unconfirmed child processes: PIDS; stop them before deleting. Child record: RECORD.
728
+ No child record exists at RECORD.
729
+ Child record could not be read (CODE): RECORD.
730
+ Another runner holds this workspace (process PID); invocation "RUN_ID"; serialize capture commands. If this pid is not a capture run, confirm no capture process or surviving child is running and re-read MARKER to confirm it still names pid PID before deleting
731
+ Capture shutdown failed; child termination was not confirmed for process PIDS; ownership marker retained: MARKER; child record: RECORD
732
+ Cannot write capture child record (CODE): RECORD
733
+ Cannot write capture ownership temporary (CODE): TEMPORARY
734
+ Cannot link capture ownership (CODE): MARKER; the store directory must support hard links
735
+ Cannot link capture ownership (CODE): MARKER; inspect the filesystem error and rerun
736
+ Capture ownership marker is temporarily unavailable (CODE): MARKER; rerun after serializing capture commands
737
+ Capture ownership marker is unreadable: MARKER; rerun after inspecting the filesystem error
738
+ Capture request directory is unavailable: DIRECTORY
739
+ Capture ownership marker is empty; confirm no capture process or surviving child is running and inspect this file: MARKER; its holder cannot be established
740
+ Capture ownership marker is malformed; expected { pid: positive integer, id: nonempty string }, received CONTENT; confirm no capture process or surviving child is running and inspect this file: MARKER
741
+ Capture shutdown failed
742
+ ```
743
+
744
+ Every USAGE refusal (exit 64) removes its invocation's run directory and leaves no marker.
745
+ An exit-1 failure after discovery keeps its run directory and logs.
746
+ Signal listeners are removed even when shutdown fails. The optional `terminate` callback returns
747
+ whether child exit was confirmed; omission uses the session's `stop`. The optional `ownership`
748
+ host supplies read, write, link, remove, platform, and liveness operations; its write operation
749
+ uses flag `wx` for both claim temporaries and survivor records. Omitted members use the local
750
+ host. Request publication and retraction retain their own lifecycle. Runner, registrar, and
751
+ preflight report release failures without changing their decided result. Registry refresh also
752
+ checks live request-file holders.
753
+
754
+ **Exit.** 0 for a published registry, 64 for `USAGE` such as a refused command line, unknown variant, or supplied wrapper path outside the workspace, and 1 for every other failure, including a refused lease, failed or interrupted stage, altered or incomplete record, or refused registry. A refresh writes no frame, and it widens no scoped run: it is a named command, never a tail of `journey`.
755
+
756
+ ### Executable
757
+
758
+ The `capture` executable dispatches the following subcommands through the server implementations:
759
+
760
+ - `capture journey <selector>` calls `createCaptureRunner` with the `journey` command and reads `--variant` (repeats), `--case`, and `--config`. Replace the selector with `--changed <ref>` and optionally add `--plan`.
761
+ - `capture frames <selector>` calls `createCaptureRunner` with the `capture` command and reads `--variant` (repeats), `--case`, `--state`, and `--config`. Replace the selector with `--changed <ref>` and optionally add `--plan`.
762
+ - `capture stale <selector>` calls `createCaptureRunner` with the `stale` command and reads `--variant` (repeats), `--case`, `--state`, `--portfolio`, and `--config`.
763
+ - `capture preflight <selector>` calls `createCapturePreflight` and reads `--variant` (repeats), `--state`, `--case`, `--portfolio`, `--config`, `--journeys`, and `--checks`.
764
+ - `capture sheets <selector> --output <directory>` calls `createCaptureSheets` with the preflight's flags and a required `--output`.
765
+ - `capture registry` calls `createCaptureRegistrar` and reads `--variant` (repeats), `--mode`, and `--config`.
766
+
767
+ `frames` runs the request command `capture`, because `capture capture` would stutter. Node's `node:util` `parseArgs` reads one frozen option table per command. Every flag except the valueless `--plan` takes one nonempty value, either `--flag value` or `--flag=value`. A value beginning with `-` must use equals form, such as `--case=-draft`. Only `--variant` repeats; a repeated boolean, a repeated single-value option (including mixed forms), an empty value, an unknown or command-inappropriate option, and the `--` terminator are refused. Runner flags may precede or follow the selector; preflight and sheets retain a leading selector; registry takes no positional arguments. Parser refusals carry `USAGE`, the parser diagnostic, and the command's usage text. The executable falls back to `CAPTURE_DEFAULTS` when the command line names none: the wrapper `configs/app/vite.journey.config.ts`, the request directory `tmp/capture`, and the journey artifact directory `tmp/journey`. The request directory reaches the wrapper through `CAPTURE_REQUESTS`, so the wrapper repeats nothing.
768
+
769
+ The bin entry declares nothing: it calls `createCaptureCLI().execute(process.argv.slice(2))` and assigns the returned code to `process.exitCode`. `CaptureCLI` takes optional `root`, `stdout`, and `stderr` options (`CaptureCLIOptions`) and exposes `execute(argv): Promise<number>`; every command's report and refusal uses those writers, so a host can drive the whole executable in process.
770
+
771
+ **Exit.** 0 when the work passed, 64 for `USAGE`, and 1 for every other failure. An unknown or empty selector is checked against the wrapper before registry authentication and is usage under journey, frames, stale, preflight, and sheets; registry refuses every selector as usage. An unknown variant is usage under every command. Git, wrapper, plan-derived owner, ownership, containment, and evidence failures exit 1. A runner reports a release failure without replacing its already decided exit. Parser refusals include command usage, and an unknown or missing subcommand prints the whole usage text on the error stream. Selector lists and shared-input paths in diagnostics carry capped previews and the number omitted; Git stderr is capped with an omitted-character count. The preflight takes no `--output`, which belongs to the sheets, and the sheets require one.
772
+
773
+ The subcommand names are declared in `CAPTURE_SUBCOMMANDS`; `frames` maps to the protocol's `capture` command. The dated interrupt evidence is recorded in the Runner section.
774
+
775
+ ## Tests
776
+
777
+ The package's proofs cover the protocol, server engines, executable, and built consumer:
778
+
779
+ - [`tests/src/core/validators.test.ts`](../tests/src/core/validators.test.ts)
780
+ - [`tests/src/core/helpers.test.ts`](../tests/src/core/helpers.test.ts)
781
+ - [`tests/src/core/errors.test.ts`](../tests/src/core/errors.test.ts)
782
+ - [`tests/src/core/index.test.ts`](../tests/src/core/index.test.ts)
783
+ - [`tests/src/server/CaptureIntegrity.test.ts`](../tests/src/server/CaptureIntegrity.test.ts)
784
+ - [`tests/src/server/CaptureRunner.test.ts`](../tests/src/server/CaptureRunner.test.ts)
785
+ - [`tests/src/server/CaptureInvocation.test.ts`](../tests/src/server/CaptureInvocation.test.ts)
786
+ - [`tests/src/server/CaptureDiscovery.test.ts`](../tests/src/server/CaptureDiscovery.test.ts)
787
+ - [`tests/src/server/CapturePreflight.test.ts`](../tests/src/server/CapturePreflight.test.ts)
788
+ - [`tests/src/server/CaptureSheets.test.ts`](../tests/src/server/CaptureSheets.test.ts)
789
+ - [`tests/src/server/CaptureRegistrar.test.ts`](../tests/src/server/CaptureRegistrar.test.ts)
790
+ - [`tests/src/server/integration.test.ts`](../tests/src/server/integration.test.ts)
791
+ - [`tests/src/server/helpers.test.ts`](../tests/src/server/helpers.test.ts)
792
+ - [`tests/src/server/factories.test.ts`](../tests/src/server/factories.test.ts)
793
+ - [`tests/src/server/index.test.ts`](../tests/src/server/index.test.ts)
794
+ - [`tests/src/bin/main.test.ts`](../tests/src/bin/main.test.ts)
795
+ - [`tests/src/bin/integration.test.ts`](../tests/src/bin/integration.test.ts)
796
+ - [`tests/distribution.test.ts`](../tests/distribution.test.ts)
797
+ - [`tests/setupServer.test.ts`](../tests/setupServer.test.ts)
798
+ - [`tests/setup.test.ts`](../tests/setup.test.ts)
799
+ - [`tests/integration.test.ts`](../tests/integration.test.ts)
800
+
801
+ The integration proof at `tests/integration.test.ts` runs the built executable through real Vitest and Chromium as part of `npm test` or with `npm run test:integration`. Run `npm run build` first; the `@vitest/browser-playwright` browser provider and the Chromium runtime must be installed.