@orkestrel/scaffold 0.0.84 → 0.0.86

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.
@@ -34,7 +34,7 @@ configs/ thin target wrappers around root Vite/TypeScript configuration
34
34
 
35
35
  - **NEVER** use `any`; accept `unknown` and narrow with guards.
36
36
  - **NEVER** use non-null assertions (`!`) or type assertions (`as`); narrow or validate.
37
- - **NEVER** use `@ts-nocheck`, `@ts-ignore`, `@ts-expect-error`, or a lint-disable directive; fix the cause.
37
+ - **NEVER** use `@ts-nocheck`, `@ts-ignore`, `@ts-expect-error`, a lint-disable directive, or a formatter-ignore directive, and never exclude a source file from a gate; fix the cause.
38
38
  - **NEVER** add an npm package unless the user explicitly requests it; prefer native APIs.
39
39
  - **NEVER** remove a symbol to silence lint. Implement it or annotate `// TODO: [Feature] Brief purpose`.
40
40
  - **NEVER** write `public`, `protected`, `private`, or a parameter property; use `#` fields.
@@ -20,12 +20,12 @@ description: Run an Orkestrel release from layer order to registry confirmation.
20
20
 
21
21
  ## Scripts
22
22
 
23
- | Script | Does |
24
- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
25
- | `scripts/compare.ts` | `[--package NAME] [--version V] [--tarball PATH] [--dist dist] [--json]`, run from the package root after its build: fetches the published tarball and lists the material differences from the rebuilt `dist/` (sourcemaps excluded, whitespace-only differences ignored). Exit 0 when nothing differs, 3 when something does, 2 when the tarball cannot be read, 64 on usage (no package name, no `dist/`, or `--tarball` with no path). |
26
- | `scripts/pins.ts` | `--version PRIOR [--range PRIOR_RANGE ...] [--paths src,tests] [--json]`, run from the package root: lists every line under the paths carrying the prior version literal or a prior range literal, once per line, sweeping a one-comparator range such as `^8.3.0` for its bare version in any form. Exit 0 with no hits, 3 with hits, 64 on usage (no literal, or a flag with no value). |
27
- | `scripts/wave.ts` | `--visit [--target DIR] [--offline] [--from STEP] [--to STEP] [--prior VERSION] [--dry-run] [--json] [--out FILE]` runs one repository's visit in the order [wave.md](references/wave.md) § Visit a repository fixes (pin, commit, overwrite, verify, install, pins, format, gates, compare), stops at the first failing step, and prints each step's exit, the bump ruling (the rebuilt dist against the published tarball, and the final runtime dependency set against the published manifest), and a row for `.orkestrel/release.md`; `--prior` names the version a pre-ruled bump replaced so the self-pin sweep finds it; `--plan` prints the packages by layer from the catalog table. Exit 0 when every step passed, 1 when one failed, 2 when a reading failed, 64 on usage. |
28
- | `scripts/window.ts` | `--whoami [--wait SECONDS]` reads or polls `npm whoami`; `--login` prints the operator's login command and polls `whoami` until it answers; `--publish DIR... --otp CODE` uploads back to back with `npm publish --ignore-scripts --otp`, journals each upload under `tmp/units/`, stops at the first refusal naming where to resume, and confirms each accepted version against the registry; `--confirm NAME@VERSION...` re-reads the registry until it serves the version. Exit 0 on success, 3 when not. |
23
+ | Script | Does |
24
+ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
25
+ | `scripts/compare.ts` | `[--package NAME] [--version V] [--tarball PATH] [--dist dist] [--json]`, run from the package root after its build: fetches the published tarball and lists the material differences from the rebuilt `dist/` (sourcemaps excluded, whitespace-only differences ignored). Exit 0 when nothing differs, 3 when something does, 2 when the tarball cannot be read, 64 on usage (no package name, no `dist/`, or `--tarball` with no path). |
26
+ | `scripts/pins.ts` | `--version PRIOR [--range PRIOR_RANGE ...] [--paths src,tests] [--json]`, run from the package root: lists every line under the paths carrying the prior version literal or a prior range literal, once per line, sweeping a one-comparator range such as `^8.3.0` for its bare version in any form. Exit 0 with no hits, 3 with hits, 64 on usage (no literal, or a flag with no value). |
27
+ | `scripts/wave.ts` | `--visit [--target DIR] [--offline] [--from STEP] [--to STEP] [--prior VERSION] [--dry-run] [--json] [--out FILE]` runs one repository's visit in the order [wave.md](references/wave.md) § Visit a repository fixes (pin, commit, overwrite, verify, install, pins, format, gates, compare), stops at the first failing step, and prints each step's exit (a failed step names the `code: message` refusal or the `note` a JSON verb writes on stdout, else the last stderr lines), the bump ruling (the rebuilt dist against the published tarball, and the final runtime dependency set against the published manifest), and a row for `.orkestrel/release.md`; `--prior` names the version a pre-ruled bump replaced so the self-pin sweep finds it; `--plan` prints the packages by layer from the catalog table. Exit 0 when every step passed, 1 when one failed, 2 when a reading failed, 64 on usage. |
28
+ | `scripts/window.ts` | `--whoami [--wait SECONDS]` reads or polls `npm whoami`; `--login` prints the operator's login command and polls `whoami` until it answers; `--publish DIR... --otp CODE` uploads back to back with `npm publish --ignore-scripts --otp`, journals each upload under `tmp/units/`, stops at the first refusal naming where to resume, and confirms each accepted version against the registry; `--confirm NAME@VERSION...` re-reads the registry until it serves the version. Exit 0 on success, 3 when not. |
29
29
 
30
30
  The user's current instruction wins. `references/release.md` owns the credential
31
31
  and authorization law; nothing here weakens it.
@@ -35,8 +35,9 @@ by hand only to repair it, then resume with `--from <step>`.
35
35
  again. Keep a local MCP server registration outside the repository rather than at `.mcp.json`.
36
36
  4. Force-verify every `@orkestrel` range against a registry sweep taken after the previous layer
37
37
  published.
38
- 5. Run the full install. The overwrite re-declares the toolchain ranges, so the lockfile the first
39
- install regenerated no longer matches the manifest.
38
+ 5. Run the full install. The overwrite re-declares the toolchain ranges and declares each planned
39
+ dependency the manifest lacks, so the lockfile the first install regenerated no longer matches the
40
+ manifest.
40
41
  6. Sweep the self-pins, per § Sweep the self-pins: the re-pin moves the snapshot class.
41
42
  7. Run the mutating `format` script to converge generated writes.
42
43
  8. Run the quality gates.
@@ -107,6 +108,10 @@ Prepare a published package's layer in this order, after the visit has ruled the
107
108
  bumped manifest.
108
109
  3. **Sweep the self-pins**, per the following section: the bump moves the version class.
109
110
  4. **Run each package's own `prepublishOnly` script to green.**
111
+ When the host npm is below the `MINIMUM_NPM_VERSION` floor a generated workspace declares in
112
+ `devEngines`, prepend a local npm 11 install's `node_modules/.bin` to `PATH` for the visit and
113
+ this script; a distribution proof that installs a generated workspace under an older npm fails
114
+ `EBADDEVENGINES`.
110
115
  5. **Write the release commit and push before the window opens.** The preparation commit inside
111
116
  the visit is a different commit at a different moment.
112
117
 
@@ -152,3 +157,10 @@ Refresh the registry evidence between layers and derive each round's pins from i
152
157
  name a version the registry already serves, so a dependency shipping in the same window keeps the
153
158
  resolvable previous pin and takes its development-only re-pin after the window closes. That re-pin
154
159
  takes the self-pin sweep too, because the snapshot class moves with no bump.
160
+
161
+ - After a window confirms a release, poll its tarball URL, read with
162
+ `npm view NAME@VERSION dist.tarball`, until it answers `200` before an install names that
163
+ version. The registry lists a version in the packument minutes before it serves the tarball, and
164
+ an install in that gap fails `E404`.
165
+ - Run every visit and install that follows a publish with `npm_config_prefer_online=true`. A
166
+ cached packument answers `notarget` for a version the registry already serves.
@@ -14,11 +14,12 @@
14
14
  // fatal), format, gates (format:check, lint:check, check, build, test), and compare (the rebuilt
15
15
  // dist against the published tarball, and the final runtime dependency set against the published
16
16
  // manifest `npm view NAME --json` serves; both readings are reported, not fatal). The summary
17
- // carries each step's exit and duration, the bump ruling, and a Markdown row for
18
- // `.orkestrel/release.md`. --dry-run prints the commands and runs nothing, so its ruling is
19
- // unanswered. --plan reads the marker-bounded catalog table in `.claude/agents/orkestrel.md` and
20
- // prints the packages by layer. Exit 0 when every step passed, 1 when a step failed, 2 when a
21
- // reading failed, 64 on usage.
17
+ // carries each step's exit and duration, a failed step's refusal (the `code: message` envelope or
18
+ // the `note` a JSON verb prints on stdout, else the last stderr lines), the bump ruling, and a
19
+ // Markdown row for `.orkestrel/release.md`. --dry-run prints the commands and runs nothing, so its
20
+ // ruling is unanswered. --plan reads the marker-bounded catalog table in
21
+ // `.claude/agents/orkestrel.md` and prints the packages by layer. Exit 0 when every step passed, 1
22
+ // when a step failed, 2 when a reading failed, 64 on usage.
22
23
  import type { SpawnSyncReturns } from 'node:child_process'
23
24
  import { spawnSync } from 'node:child_process'
24
25
  import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs'
@@ -158,8 +159,20 @@ function describeCommand(file: string, args: readonly string[]): string {
158
159
  return [file === process.execPath ? 'node' : file, ...args].join(' ')
159
160
  }
160
161
 
162
+ // A JSON verb writes its refusal envelope or its partial-run note on stdout, so the stderr tail
163
+ // alone reads empty for exactly the failures the operator needs named.
161
164
  function describeFailure(result: SpawnSyncReturns<string>): string | undefined {
162
165
  if (result.status === 0) return undefined
166
+ const value = readJsonObject(result.stdout)
167
+ const error = value?.error
168
+ if (typeof error === 'object' && error !== null && !Array.isArray(error)) {
169
+ const envelope = Object.fromEntries(Object.entries(error))
170
+ const code = readString(envelope, 'code')
171
+ const message = readString(envelope, 'message')
172
+ if (code !== undefined && message !== undefined) return `${code}: ${message}`
173
+ }
174
+ const note = readString(value, 'note')
175
+ if (note !== undefined) return note
163
176
  return result.stderr
164
177
  .trim()
165
178
  .split(/\r\n|\n/)
@@ -341,11 +354,11 @@ function runOverwrite(
341
354
  runner.target,
342
355
  )
343
356
  if (runner.offline && written !== undefined && written.status === 1) {
344
- const note = readString(readJsonObject(written.stdout) ?? {}, 'note')
357
+ const note = readString(readJsonObject(written.stdout), 'note')
345
358
  if (note !== undefined && note.includes(OFFLINE_REFUSAL)) {
346
359
  replaceLastStep(runner, 0, 'offline overwrite skipped the catalog step by design')
347
360
  } else {
348
- replaceLastStep(runner, 1, note ?? describeFailure(written))
361
+ replaceLastStep(runner, 1, describeFailure(written))
349
362
  }
350
363
  }
351
364
  if (!failed(runner)) {
@@ -78,7 +78,7 @@ so network-controlled descriptions never enter agent instruction context.
78
78
  | `@orkestrel/reason` | `0.0.12` | L2 | `@orkestrel/emitter` `^0.0.11`, `@orkestrel/contract` `^0.0.18` | |
79
79
  | `@orkestrel/relation` | `0.0.14` | L3 | `@orkestrel/emitter` `^0.0.11`, `@orkestrel/contract` `^0.0.18`, `@orkestrel/database` `^0.0.16` | |
80
80
  | `@orkestrel/router` | `0.0.16` | L2 | `@orkestrel/abort` `^0.0.12`, `@orkestrel/emitter` `^0.0.11`, `@orkestrel/contract` `^0.0.18` | |
81
- | `@orkestrel/scaffold` | `0.0.83` | L3 | `@orkestrel/console` `^0.0.15`, `@orkestrel/emitter` `^0.0.11`, `@orkestrel/process` `^0.0.14`, `@orkestrel/contract` `^0.0.19`, `@orkestrel/markdown` `^0.0.17`, `@orkestrel/template` `^0.0.9` | |
81
+ | `@orkestrel/scaffold` | `0.0.85` | L3 | `@orkestrel/console` `^0.0.15`, `@orkestrel/emitter` `^0.0.11`, `@orkestrel/process` `^0.0.14`, `@orkestrel/contract` `^0.0.19`, `@orkestrel/markdown` `^0.0.17`, `@orkestrel/template` `^0.0.9` | |
82
82
  | `@orkestrel/sea` | `0.0.18` | L3 | `@orkestrel/emitter` `^0.0.11`, `@orkestrel/process` `^0.0.14`, `@orkestrel/contract` `^0.0.18` | |
83
83
  | `@orkestrel/server` | `0.0.22` | L3 | `@orkestrel/abort` `^0.0.12`, `@orkestrel/codec` `^0.0.5`, `@orkestrel/router` `^0.0.16`, `@orkestrel/emitter` `^0.0.11`, `@orkestrel/timeout` `^0.0.12`, `@orkestrel/contract` `^0.0.19` | |
84
84
  | `@orkestrel/sqlite` | `0.0.13` | L1 | `@orkestrel/contract` `^0.0.18` | |
@@ -108,8 +108,8 @@ neither reads meaning.
108
108
  data-kind file, that every centralized declaration is exported, that a class sits in its matching
109
109
  implementation or errors file, and that `constants.ts` declares only UPPER_SNAKE_CASE consts with
110
110
  no bare collection literal.
111
- - The sweep proves that no source, test, config, or script file carries an `eslint-disable` or
112
- `oxlint-disable` directive.
111
+ - The sweep proves that no source, test, config, script, or sheet file carries an `eslint-disable`,
112
+ `oxlint-disable`, or `prettier-ignore` directive.
113
113
  - The sweep proves that every `.claude/rules/*.md` file has a rule-map row in `AGENTS.md` and that
114
114
  every row resolves to a file.
115
115
  - The sweep proves the host portability rules that are path- or text-shaped over the population
@@ -14,14 +14,15 @@ SCSS mirrors TypeScript centralization. Concrete token prefixes are project-spec
14
14
 
15
15
  ## Centralized files
16
16
 
17
- | File | Sole responsibility |
18
- | ------------------- | ------------------------------------------------------------- |
19
- | `_mixins.scss` | `@function` values and `@mixin` declaration emitters |
20
- | `_tokens.scss` | `:root` public custom-property tokens and cascade-layer order |
21
- | `_theme.scss` | Token overrides under theme selectors |
22
- | `_reset.scss` | The face's reset declarations, when that face owns a reset |
23
- | `themes/index.scss` | Barrel of named theme packs, compiled into its own sheet |
24
- | `index.scss` | Sole compilation barrel |
17
+ | File | Sole responsibility |
18
+ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
19
+ | `_mixins.scss` | `@function` values, `@mixin` declaration emitters, and the `!default` switch an emitter reads, which `_tokens.scss` configures through `@use … with` |
20
+ | `_tokens.scss` | `:root` public custom-property tokens and cascade-layer order |
21
+ | `_theme.scss` | Token overrides under theme selectors |
22
+ | `_reset.scss` | The face's reset declarations, when that face owns a reset |
23
+ | `_utilities.scss` | The utility map and its emission schedule, on a face that generates its utilities from data; that face has no `utilities/` folder, and the one emitter the schedule calls lives in `_mixins.scss` |
24
+ | `themes/index.scss` | Barrel of named theme packs, compiled into its own sheet |
25
+ | `index.scss` | Sole compilation barrel |
25
26
 
26
27
  - Apply this table and the folder barrels in § Folders to every sheet face: `src/styles` and each
27
28
  `src/<name>` styles extension. `.claude/rules/workspace.md` § Environments fixes the `sheet.ts`
@@ -51,6 +52,14 @@ SCSS mirrors TypeScript centralization. Concrete token prefixes are project-spec
51
52
  declared. A face whose contract is the exact recreation of a pinned external artifact keeps
52
53
  the literals, declarations, and order the pin declares, and records tokenization and
53
54
  accessibility additions in its separate authored face.
55
+ - Format a pinned recreation face like every other file: never a `prettier-ignore` directive and
56
+ never a `.prettierignore` entry for a source file. Sass re-emits every value and selector it
57
+ parses in its own form, so the formatter's canonicalisation of those vanishes at compile; a
58
+ custom-property value is the one text Sass emits verbatim, so write every custom-property
59
+ declaration in a literal partial as a Sass string interpolation (`--name: #{'VALUE'};`), which the
60
+ formatter reads as a string and Sass emits byte for byte. Keep a pinned expected output in a JSON
61
+ fixture, never in a CSS file the formatter would canonicalise. The face's byte proofs (link 1, the
62
+ per-module region cases, the sample cases) hold under `npm run format`.
54
63
  - Never repeat per-color/per-variant blocks; drive shared structure with one `@each` over a shared list.
55
64
  - If a pattern appears in at least two partials, move it to `_mixins.scss`.
56
65
  - Treat a declaration block two partials share because each records an external value as a
@@ -69,7 +78,7 @@ SCSS mirrors TypeScript centralization. Concrete token prefixes are project-spec
69
78
  ## Folders
70
79
 
71
80
  - Give each folder an `_index.scss` barrel that loads its partials with `@use`; keep the barrel when the folder is empty.
72
- - Put a rule that styles one element in `elements/`, a class skin that applies with no script running in `components/`, and a class that sets one property in `utilities/`.
81
+ - Put a rule that styles one element in `elements/`, a class skin that applies with no script running in `components/`, and a class that sets one property in `utilities/`. A face that generates its utilities from a map keeps that map and its emission schedule in `_utilities.scss` instead of a `utilities/` folder; never keep both, because `@use 'utilities'` resolves to either.
73
82
  - Add another folder only for a job `elements/`, `components/`, and `utilities/` do not hold, and give it its own barrel and its own layer.
74
83
 
75
84
  ## Proofs
@@ -287,7 +287,7 @@ Build/check config alignment:
287
287
 
288
288
  - In a publishing workspace, `prepublishOnly` runs `build:showcase` and every
289
289
  `build:showcase:<framework>` script after `npm run build`.
290
- - List `showcase/` in `.prettierignore`, so formatting never rewrites a committed page.
290
+ - List `showcase/` and `*.min.*` in `.prettierignore`, so formatting never rewrites a committed page or a minified generated record. Give the `.min` suffix only to a record an instrument writes from an installed package in compact form (a JSON inventory over 1 MB, for example), never to authored source, and read such a record through a guarded reader. Never list a source file there; `.claude/rules/styles.md` § Prohibitions states how a pinned recreation stays formatter-stable.
291
291
 
292
292
  ## Tooling
293
293
 
@@ -15,3 +15,6 @@ tests/mirrors/
15
15
 
16
16
  # The vendored host inventory: `npm run build` regenerates it (`stageInventory`), so its bytes are the generator's, not the formatter's.
17
17
  host.json
18
+
19
+ # A minified generated record (`*.min.*`, such as a compact JSON inventory an instrument writes from an installed package) keeps its compact bytes: nothing in it is authored, and a formatted copy would be a second artifact of the same bytes. Authored source never takes the suffix.
20
+ *.min.*
@@ -306,6 +306,7 @@ Exported from `@orkestrel/scaffold`, and reachable from
306
306
  | `blueprintToTestArtifacts` | function | Compiles every artifact in the `tests` group that is not vendored from the host. |
307
307
  | `blueprintToWritableScripts` | function | Projects a blueprint into the manifest scripts a region write may replace. |
308
308
  | `dependenciesToQuestions` | function | Measures one declared package list against the name and range syntax it accepts. |
309
+ | `insertManifestDependencies` | function | Inserts dependencies a package manifest does not declare into the section each one names. |
309
310
  | `overridesToQuestions` | function | Measures a blueprint's overrides against the artifacts drafted for it. |
310
311
  | `pathToCondition` | function | Builds one `exports` condition block for a built environment. |
311
312
  | `planToFindings` | function | Compares a plan against a target's current content. |
@@ -539,7 +540,7 @@ option grants a write.
539
540
  | `audit` | Nothing |
540
541
  | `repair` | Each planned path the target is missing or has let drift, and the range and script regions |
541
542
  | `catalog` | The package table, the guide mirrors, and the range region |
542
- | `overwrite` | Everything `repair` and `catalog` write, plus deletions |
543
+ | `overwrite` | Everything `repair` and `catalog` write, deletions, and missing planned dependencies |
543
544
 
544
545
  ### Baselines
545
546
 
@@ -589,7 +590,7 @@ scaffold <verb> [options]
589
590
  scaffold catalog [--all] [--from <path>] [--target <path>] [--json]
590
591
  regenerate the package table and refresh the guide mirrors
591
592
  scaffold overwrite [--groups <list>] [--dirty] [--offline] [--from <path>] [--target <path>] [--json]
592
- do everything repair and catalog do, then delete what the plan does not own and re-declare the dependency ranges
593
+ do everything repair and catalog do, then delete what the plan does not own, re-declare the dependency ranges, and declare each planned dependency the manifest lacks
593
594
 
594
595
  options
595
596
  --src <list> the published library environments to build: core, browser, server
@@ -802,9 +803,20 @@ live in either section, and how current a declared range is belongs to the regis
802
803
  Dependency floors describes rather than to this question. A present section that is not an object
803
804
  produces a question instead of a crash. This question belongs to `configs` and `tests`. `audit`
804
805
  reports it only when its selection includes either group, without changing its exit semantics.
805
- `repair` and `overwrite` refuse before writing a selected `configs` or `tests` group. A selection
806
- that excludes those groups proceeds, and no verb adds the declaration for you: `package.json` is
807
- birth-owned, and the range and script regions are the only parts of it a verb rewrites.
806
+ `repair` refuses before writing a selected `configs` or `tests` group, and a selection that
807
+ excludes those groups proceeds: `package.json` is birth-owned, and `repair` rewrites only its range
808
+ and script regions. `overwrite` declares each missing package instead, in the `devDependencies` map
809
+ the plan assigns it, at its planned range, before the first declared key that sorts after it. A
810
+ manifest with no `devDependencies` map gets one: `overwrite` creates it as one top-level key after
811
+ `dependencies`, or last in the manifest object when `dependencies` is absent too, in the indentation
812
+ of the manifest's first key. The declaration lands with the repair, before the catalog step, so a
813
+ partial run keeps it. The JSON result names each declaration in `additions`, and the human report
814
+ prints one `Declared "<name>": "<range>" in devDependencies. Run npm install to install it.` line
815
+ per declaration, because the lockfile does not carry the package until the next install. A
816
+ `devDependencies` value that is not an object, or an entry in that map whose value is not a version
817
+ string, leaves `overwrite` no map to declare in. `overwrite` refuses that manifest before any write,
818
+ whatever `--groups` selects, and names the section or each malformed entry. A malformed section
819
+ refuses `repair` as well.
808
820
 
809
821
  `audit` reports a further non-blocking question, on the `setup` field.
810
822
 
@@ -844,6 +856,17 @@ write: a writing verb reports it in the terminal audit it prints, because refusi
844
856
  gap no write can close would block every write. Run across a fleet, the question is the list of
845
857
  packages carrying a filled, exporting setup module that no proof covers.
846
858
 
859
+ `audit` reports a non-blocking question on the `tests` field for each planned sheet test the target
860
+ holds that imports by a root-relative specifier. A sheet test is birth-owned, so a target born
861
+ before the template imported the built sheet by a relative path keeps the
862
+ `'/dist/src/<name>/index.css?raw'` import, and the content-owned `.oxlintrc.json` refuses it under
863
+ `--deny-warnings` through `import/no-absolute-path`. The message names the file and each
864
+ root-relative specifier beside the relative one to write: one `../` per directory between the file
865
+ and the workspace root, so `tests/src/styles/index.test.ts` writes
866
+ `'../../../dist/src/styles/index.css?raw'`. Scaffold never rewrites the file. The question belongs to
867
+ the `tests` group, and `repair` and `overwrite` report it in their terminal audit without
868
+ refusing a write.
869
+
847
870
  `audit` reads the instruction canon as findings rather than as a question. Each `CANON_PATHS` member
848
871
  the target holds enters the comparison, by file where the member is a directory, and a path the plan
849
872
  does not claim there reports `foreign`. Ownership and drift states that population, and Vendored data
@@ -886,12 +909,14 @@ standard error, so a piped value is never polluted.
886
909
  | `audit` | `Audit` — `findings` and `questions` — plus `releases` and `provenance`; findings carry `ownership` |
887
910
  | `repair` | `MaterializeResult` plus `audit`, the terminal audit taken after the write, `releases`, and `provenance` |
888
911
  | `catalog` | `MaterializeResult` plus `mirrors`, `provenance`, optional `membership` with `entries`, `dropped`, and `releases`, and an explanatory `note` on a partial run |
889
- | `overwrite` | The `catalog` value plus `audit` and top-level `releases` from its version read; `note` explains a partial run |
912
+ | `overwrite` | The `catalog` value plus `audit`, top-level `releases` from its version read, and `additions`; `note` explains a partial run |
890
913
 
891
914
  The `membership` entity is present only when the catalog read completes. Its `entries` holds the
892
915
  package table, `dropped` names packages the preceding table carried that the registry no longer
893
916
  lists, and `releases` measures declared fleet ranges against the catalog read. The `overwrite`
894
- result also retains top-level `releases` from its separate version read, including foreign tools.
917
+ result also retains top-level `releases` from its separate version read, including foreign tools,
918
+ and `additions`, the `DependencyPinSet` of planned dependencies it declared, empty when the
919
+ manifest lacked none.
895
920
  An absent `membership` identifies an incomplete catalog read; `note` explains the cause.
896
921
 
897
922
  The following JSON excerpt shows the membership evidence in a completed catalog result:
@@ -4,7 +4,7 @@
4
4
  "storage": "AGENTS.md",
5
5
  "destination": "AGENTS.md",
6
6
  "executable": false,
7
- "digest": "54707c4e20210dc0a925274d76bb6b9a5d65d4b9b8d8535252dbd53a1a72fd38"
7
+ "digest": "357cfbe336a026543ec68f3eb999f1094c3139d33331b0ba5f153352cef75fde"
8
8
  },
9
9
  {
10
10
  "storage": "LICENSE",
@@ -352,7 +352,7 @@
352
352
  "storage": "agents/skills/orkestrel-publish/SKILL.md",
353
353
  "destination": ".agents/skills/orkestrel-publish/SKILL.md",
354
354
  "executable": false,
355
- "digest": "b79e81c49a5b89f8418fc751eaf6d02c68c5dd9f26a4a0678f9025b1915ebda2"
355
+ "digest": "f4bcc24e910bbd40de268ba6a9d4ba57b3e5f9ce69075dea395479a45ec0f9d4"
356
356
  },
357
357
  {
358
358
  "storage": "agents/skills/orkestrel-publish/agents/openai.yaml",
@@ -370,7 +370,7 @@
370
370
  "storage": "agents/skills/orkestrel-publish/references/wave.md",
371
371
  "destination": ".agents/skills/orkestrel-publish/references/wave.md",
372
372
  "executable": false,
373
- "digest": "d86fc6b60f12eb09a54442cc6c6e6c3e9b901b310af0bdb19b7d3084b010b059"
373
+ "digest": "2d5479e0d3ed0529015f5f1f21345fb389f8119333b6c5530d2320be432fe1e1"
374
374
  },
375
375
  {
376
376
  "storage": "agents/skills/orkestrel-publish/references/window.md",
@@ -394,7 +394,7 @@
394
394
  "storage": "agents/skills/orkestrel-publish/scripts/wave.ts",
395
395
  "destination": ".agents/skills/orkestrel-publish/scripts/wave.ts",
396
396
  "executable": false,
397
- "digest": "53a85584203346325df82b2b2b20a77a86b64dbbf017afc1fdfc58d782e4f85c"
397
+ "digest": "e7a492aee9f3c63fcb22e3ab6a90f7a1a292a408d1824d75e3de26ea07a3f3e9"
398
398
  },
399
399
  {
400
400
  "storage": "agents/skills/orkestrel-publish/scripts/window.ts",
@@ -496,7 +496,7 @@
496
496
  "storage": "claude/agents/orkestrel.md",
497
497
  "destination": ".claude/agents/orkestrel.md",
498
498
  "executable": false,
499
- "digest": "4b3dd60e59e6c0737d526083d70bcea0b010efbeaaf068f391ab1b76cac99280"
499
+ "digest": "6b3af62e323ff6725680f6a13c3505904154e1d2c0afcb8ba7151a00ff5875c2"
500
500
  },
501
501
  {
502
502
  "storage": "claude/agents/planner.md",
@@ -538,7 +538,7 @@
538
538
  "storage": "claude/rules/architecture.md",
539
539
  "destination": ".claude/rules/architecture.md",
540
540
  "executable": false,
541
- "digest": "c3695c2deb808b04e55dbea6b06e66af65b46794d08cc7be67f5661e634c4ad0"
541
+ "digest": "e30f034c8de38ce7c86506dfaa51844201d745181caed45ef427d0325e80afd3"
542
542
  },
543
543
  {
544
544
  "storage": "claude/rules/browser.md",
@@ -580,7 +580,7 @@
580
580
  "storage": "claude/rules/styles.md",
581
581
  "destination": ".claude/rules/styles.md",
582
582
  "executable": false,
583
- "digest": "f0f07cba616023b0c1d8f0a4cb23bd70129af1f4d818f41bfc82ff3f5c0f8e84"
583
+ "digest": "869f88c392b4795e47af881d668f4dc591e0c419229a843eb4105e38dfb6f831"
584
584
  },
585
585
  {
586
586
  "storage": "claude/rules/tests.md",
@@ -598,7 +598,7 @@
598
598
  "storage": "claude/rules/workspace.md",
599
599
  "destination": ".claude/rules/workspace.md",
600
600
  "executable": false,
601
- "digest": "a3c0e392dfe6c1aceac54a00cd4f735600c0ddd49bc2d997b9a977015c81a89e"
601
+ "digest": "3c9e269acd88935ee6c4f34dd07784f09f45bb6bfd1e27c6a183da3420da4fcc"
602
602
  },
603
603
  {
604
604
  "storage": "claude/rules/writing.md",
@@ -832,7 +832,7 @@
832
832
  "storage": "dotfiles/prettierignore",
833
833
  "destination": ".prettierignore",
834
834
  "executable": false,
835
- "digest": "fa3c916de6f5d2a4c07ee65281da26d21aca8db9974ec6199536fc7c36070e52"
835
+ "digest": "abd137ce9568994c74d4473765a70ba04554548b840e4a3275ab749e397ab69b"
836
836
  },
837
837
  {
838
838
  "storage": "guides/README.md",
@@ -1042,7 +1042,7 @@
1042
1042
  "storage": "guides/scaffold.md",
1043
1043
  "destination": "guides/scaffold.md",
1044
1044
  "executable": false,
1045
- "digest": "116e87dd3c9b5c23451bb53a2365f7df8982e6782f60a3466070c8058c099700"
1045
+ "digest": "9d6ea9f86610e3237d6ddbe120aabdec137f022092fc5311ca18e87d4b4e6212"
1046
1046
  },
1047
1047
  {
1048
1048
  "storage": "guides/sea.md",
@@ -1180,7 +1180,7 @@
1180
1180
  "storage": "tests/setupPolicy.ts",
1181
1181
  "destination": "tests/setupPolicy.ts",
1182
1182
  "executable": false,
1183
- "digest": "a6155fc5a2653d847014c4e7ec3fcf936bcbd7ab02e05827e6cf1f71beedcced"
1183
+ "digest": "2571274f92e75e6caffa86b2703ef9a38828db4ec8f51a7b38510f8bc48c6aee"
1184
1184
  }
1185
1185
  ],
1186
1186
  "roots": [
@@ -2144,5 +2144,5 @@
2144
2144
  ]
2145
2145
  }
2146
2146
  ],
2147
- "digest": "410ca4bbfc4639a33cea13a791dffd130f507b8efb3aa2d54b84f5143babab4b"
2147
+ "digest": "ccbd92a2462e0a17fb972b7582bf05e6deec0c1f11cf4d2d17e6ebec5cc431bf"
2148
2148
  }
@@ -281,10 +281,13 @@ export const POLICY_TEST_GLOB = 'tests/{app,src}/**/*.test.ts'
281
281
  // Compose suppression tokens so the instrument does not report its own definitions or controls.
282
282
  export const POLICY_SUPPRESSION_DIRECTIVE = ['oxlint', '-disable'].join('')
283
283
 
284
- /** Matches the source, test, config, and script files inspected for lint suppression directives. */
284
+ /** Names the formatter directive the text sweep refuses, composed so the instrument does not report itself. */
285
+ export const POLICY_FORMATTER_DIRECTIVE = ['prettier', '-ignore'].join('')
286
+
287
+ /** Matches the source, test, config, script, and sheet files inspected for lint and formatter suppression directives. */
285
288
  export const POLICY_SUPPRESSION_GLOB: readonly string[] = Object.freeze([
286
- '{src,app,tests,configs,scripts}/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx,vue}',
287
- '*.{cjs,cts,js,jsx,mjs,mts,ts,tsx,vue}',
289
+ '{src,app,tests,configs,scripts}/**/*.{cjs,css,cts,js,jsx,mjs,mts,scss,ts,tsx,vue}',
290
+ '*.{cjs,css,cts,js,jsx,mjs,mts,scss,ts,tsx,vue}',
288
291
  ])
289
292
 
290
293
  /** Lists the rules whose workspace-wide lint wiring must not be weakened by configuration. */
@@ -305,9 +308,11 @@ export const POLICY_WIRING_ROOTS: readonly string[] = Object.freeze([
305
308
  'configs',
306
309
  ])
307
310
 
308
- /** Matches either lint suppression token the text sweep refuses. */
311
+ /** Matches either lint suppression token or the formatter directive the text sweep refuses. */
309
312
  export const POLICY_SUPPRESSION_PATTERN = new RegExp(
310
- [['eslint', '-disable'].join(''), POLICY_SUPPRESSION_DIRECTIVE].join('|'),
313
+ [['eslint', '-disable'].join(''), POLICY_SUPPRESSION_DIRECTIVE, POLICY_FORMATTER_DIRECTIVE].join(
314
+ '|',
315
+ ),
311
316
  'u',
312
317
  )
313
318
 
@@ -577,7 +582,7 @@ export function inspectPolicySetup(root: string): readonly PolicyViolation[] {
577
582
  }
578
583
 
579
584
  /**
580
- * Inspects code-shaped workspace files for lint suppression directives.
585
+ * Inspects code-shaped and sheet workspace files for lint and formatter suppression directives.
581
586
  *
582
587
  * @param root - The workspace root to inspect.
583
588
  * @returns Every suppression occurrence in path and line order.
@@ -594,7 +599,7 @@ export function inspectPolicySuppressions(root: string): readonly PolicyViolatio
594
599
  createPolicyViolation(
595
600
  'suppression',
596
601
  path,
597
- 'file carries a lint suppression directive',
602
+ 'file carries a lint or formatter suppression directive',
598
603
  index + 1,
599
604
  ),
600
605
  )
@@ -2687,6 +2692,17 @@ export const POLICY_CONTROLS: readonly PolicyControl[] = Object.freeze([
2687
2692
  },
2688
2693
  ],
2689
2694
  },
2695
+ {
2696
+ label: 'rejects a formatter directive in a sheet partial',
2697
+ membership: 'sheet files in the suppression population',
2698
+ rule: 'suppression',
2699
+ files: [
2700
+ {
2701
+ path: 'src/styles/_control.scss',
2702
+ content: `// ${POLICY_FORMATTER_DIRECTIVE}\n.control {\n\tcolor: red;\n}\n`,
2703
+ },
2704
+ ],
2705
+ },
2690
2706
  {
2691
2707
  label: 'rejects an unmirrored module test',
2692
2708
  membership: 'module tests below tests/src or tests/app except integration.test.ts',