@orkestrel/scaffold 0.0.63 → 0.0.65

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 (57) hide show
  1. package/README.md +29 -104
  2. package/dist/bin/main.js +95 -27
  3. package/dist/bin/main.js.map +1 -1
  4. package/dist/host/AGENTS.md +2 -2
  5. package/dist/host/CLAUDE.md +6 -0
  6. package/dist/host/agents/orchestration.md +23 -15
  7. package/dist/host/agents/skills/enterprise-bootstrap/SKILL.md +184 -177
  8. package/dist/host/agents/skills/enterprise-bootstrap/references/bootstrap-reference.md +314 -91
  9. package/dist/host/agents/skills/enterprise-bootstrap/references/color-modes.md +241 -0
  10. package/dist/host/agents/skills/enterprise-bootstrap/references/components.md +83 -36
  11. package/dist/host/agents/skills/enterprise-bootstrap/references/frontend-design.md +297 -98
  12. package/dist/host/agents/skills/enterprise-bootstrap/references/inputs.md +25 -14
  13. package/dist/host/agents/skills/enterprise-bootstrap/references/inspection.md +216 -128
  14. package/dist/host/agents/skills/enterprise-bootstrap/references/responsive-layout.md +187 -0
  15. package/dist/host/agents/skills/enterprise-bootstrap/references/utilities.md +109 -20
  16. package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +5 -5
  17. package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +1 -1
  18. package/dist/host/agents/skills/orkestrel-publish/SKILL.md +15 -15
  19. package/dist/host/agents/skills/orkestrel-publish/references/wave.md +43 -17
  20. package/dist/host/agents/skills/orkestrel-publish/references/window.md +41 -16
  21. package/dist/host/claude/agents/orkestrel.md +56 -56
  22. package/dist/host/claude/agents/reviewer.md +13 -0
  23. package/dist/host/claude/rules/architecture.md +51 -45
  24. package/dist/host/claude/rules/documentation.md +18 -1
  25. package/dist/host/claude/rules/portability.md +2 -0
  26. package/dist/host/claude/rules/quality.md +1 -1
  27. package/dist/host/claude/rules/tests.md +12 -11
  28. package/dist/host/claude/rules/typescript.md +5 -0
  29. package/dist/host/claude/rules/workspace.md +25 -20
  30. package/dist/host/claude/rules/writing.md +4 -0
  31. package/dist/host/claude/settings.json +1 -1
  32. package/dist/host/claude/skills/enterprise-bootstrap/SKILL.md +10 -9
  33. package/dist/host/codex/agents/orkestrel.toml +3 -3
  34. package/dist/host/codex/agents/reviewer.toml +4 -2
  35. package/dist/host/configs/helpers.ts +311 -2
  36. package/dist/host/configs/policy.ts +1100 -51
  37. package/dist/host/dotfiles/oxlintrc.json +72 -1
  38. package/dist/host/guides/guide.md +749 -222
  39. package/dist/host/guides/scaffold.md +529 -394
  40. package/dist/host/manifest.json +53 -40
  41. package/dist/host/scripts/ollama.sh +322 -13
  42. package/dist/host/tests/config.test.ts +1200 -16
  43. package/dist/host/tests/policy.test.ts +157 -173
  44. package/dist/host/tests/setupPolicy.ts +522 -1007
  45. package/dist/src/core/index.cjs +402 -287
  46. package/dist/src/core/index.cjs.map +1 -1
  47. package/dist/src/core/index.d.cts +160 -128
  48. package/dist/src/core/index.d.ts +160 -128
  49. package/dist/src/core/index.js +400 -286
  50. package/dist/src/core/index.js.map +1 -1
  51. package/dist/src/server/index.cjs +28 -21
  52. package/dist/src/server/index.cjs.map +1 -1
  53. package/dist/src/server/index.d.cts +38 -33
  54. package/dist/src/server/index.d.ts +38 -33
  55. package/dist/src/server/index.js +28 -21
  56. package/dist/src/server/index.js.map +1 -1
  57. package/package.json +18 -19
@@ -24,7 +24,7 @@
24
24
  .bg-opacity-10, .bg-opacity-25, .bg-opacity-50, .bg-opacity-75, .bg-opacity-100
25
25
  ```
26
26
 
27
- Prefer `bg-body-*` and `*-subtle` over `bg-white`/`bg-light` — they track `data-bs-theme` so dark mode works without extra rules.
27
+ Prefer `bg-body`, `bg-body-secondary`, `bg-body-tertiary`, and `bg-*-subtle` for quiet surfaces; inherit text without an added foreground class. Treat original contextual `bg-*`, including `bg-light` and `bg-dark`, as non-adaptive in stock 5.3. Take ownership and exceptions from [color-modes.md](color-modes.md).
28
28
 
29
29
  ### Borders
30
30
 
@@ -39,7 +39,11 @@ Prefer `bg-body-*` and `*-subtle` over `bg-white`/`bg-light` — they track `dat
39
39
  .rounded-0, .rounded-1, .rounded-2, .rounded-3, .rounded-4, .rounded-5
40
40
  ```
41
41
 
42
- For borders that must stay visible in both color modes, prefer the `border-*-subtle` classes (theme-adaptive) over raw color borders.
42
+ Use adaptive border roles for quiet separation. Measure boundaries needed to identify a control or state; `border-*-subtle` adapts but does not automatically meet the contrast bar.
43
+
44
+ - **`border-{1..5}` sets `border-width` on every side.** On a component that already has a border (`.card`, `.alert`) it thickens the whole box. For a one-side accent, zero first, restore one side, then widen — `card border-0 border-top border-4 border-primary` — utility source order (`border` → `border-{side}` → `border-width`) makes it hold, and the cleared sides have no border style so their width never paints.
45
+ - Strengthen a rule that reads too faint with `border-2` on its soft color, not with a darker color; heavier width keeps the softness.
46
+ - `border-{color}` is the fixed brand color in both modes; check an accent against a dark `bg-*-subtle` before shipping it.
43
47
 
44
48
  ### Colors (Text)
45
49
 
@@ -48,10 +52,13 @@ For borders that must stay visible in both color modes, prefer the `border-*-sub
48
52
  .text-body, .text-body-secondary, .text-body-tertiary, .text-body-emphasis
49
53
  .text-primary-emphasis, .text-secondary-emphasis, .text-success-emphasis, .text-danger-emphasis, .text-warning-emphasis, .text-info-emphasis, .text-light-emphasis, .text-dark-emphasis
50
54
  .text-black, .text-white, .text-black-50, .text-white-50
51
- .text-muted /* DEPRECATED in 5.3 — use .text-body-secondary; removed in v6 */
55
+ .text-muted /* Deprecated in 5.3; use a deliberate .text-body-secondary role instead. */
56
+ .text-reset /* Restore inherited color; not the same as .text-body. */
52
57
  .text-opacity-25, .text-opacity-50, .text-opacity-75, .text-opacity-100
53
58
  ```
54
59
 
60
+ Default ordinary text to inheritance. Original contextual `text-*` colors do not adapt in stock 5.3; body-role and `text-*-emphasis` colors do. `text-body-secondary` (body color at .75 alpha) clears 4.5:1 on every stock body surface in both modes; `text-body-tertiary` (.5 alpha) measures 3.0–4.1:1 and is decoration or disabled only. Neither, nor `text-white-50`, is a quiet tier on a colored fill — take the same-hue token from [color-modes.md](color-modes.md) → Text tiers. Do not add emphasis text automatically to subtle fills, and do not replace a component's native foreground without inspecting its state contract.
61
+
55
62
  ### Display
56
63
 
57
64
  ```css
@@ -120,6 +127,8 @@ For borders that must stay visible in both color modes, prefer the `border-*-sub
120
127
  .link-offset-1, .link-offset-2, .link-offset-3
121
128
  ```
122
129
 
130
+ Keep normal links on Bootstrap's link rules inside prose. Where most things are links — navigation, lists, tables — use `link-body-emphasis link-underline-opacity-0 link-underline-opacity-100-hover link-offset-2`: body tone, underline on hover and focus, and the one colored-link helper that adapts to dark mode; add `fw-semibold` where the link is the row's identity. For a brand underline that completes on hover: `link-underline-primary link-underline-opacity-50 link-underline-opacity-100-hover link-offset-2`. Do not replace hover/focus rules with a text-color utility.
131
+
123
132
  ### Object Fit
124
133
 
125
134
  ```css
@@ -133,6 +142,8 @@ For borders that must stay visible in both color modes, prefer the `border-*-sub
133
142
  .opacity-0, .opacity-25, .opacity-50, .opacity-75, .opacity-100
134
143
  ```
135
144
 
145
+ Opacity is not a text tier: it reads as disabled and lets the surface show through the glyphs. Do not use whole-element opacity to quiet a region containing readable text. Color-opacity utilities affect only rules that consume their variable; stock subtle backgrounds do not consume `--bs-bg-opacity`. Measure the composited result rather than assuming a tint.
146
+
136
147
  ### Overflow
137
148
 
138
149
  ```css
@@ -160,9 +171,11 @@ For borders that must stay visible in both color modes, prefer the `border-*-sub
160
171
  .shadow-none, .shadow-sm, .shadow, .shadow-lg
161
172
  ```
162
173
 
174
+ Three elevation steps: `shadow-sm` (`0 .125rem .25rem` at .075) for slightly raised cards and controls, `shadow` (`0 .5rem 1rem` at .15) for floating menus and a dragged item, `shadow-lg` (`0 1rem 3rem` at .175) for dialogs. Stock dropdowns, popovers, toasts, and modals all sit on `--bs-box-shadow`; lift a modal to the top step through `--bs-modal-box-shadow` ([bootstrap-reference.md](bootstrap-reference.md) → Elevation and depth). No shadow is a valid role.
175
+
163
176
  ### Sizing
164
177
 
165
- Bootstrap ships exactly these — nothing else (no `.vw-25`, `.vh-50`, `.mw-auto`, `.min-vh-75`, etc.; add missing steps via the utilities API if a project truly needs them — see [bootstrap-reference.md](bootstrap-reference.md)):
178
+ Bootstrap ships exactly these — nothing else (no `.vw-25`, `.vh-50`, `.mw-auto`, or `.min-vh-75`; add missing steps through the utilities API if a project truly needs them — see [bootstrap-reference.md](bootstrap-reference.md)):
166
179
 
167
180
  ```css
168
181
  /* Width / height (percent of parent) */
@@ -222,14 +235,16 @@ Notes: `s`/`e` are logical start/end — they flip automatically under RTL; neve
222
235
  /* Size */
223
236
  .fs-1, .fs-2, .fs-3, .fs-4, .fs-5, .fs-6
224
237
 
225
- /* Truncate — needs display block/inline-block or a flex child with min-width 0 */
238
+ /* Truncate — needs a constrained inline width and block/inline-block layout */
226
239
  .text-truncate
227
240
  ```
228
241
 
242
+ `fs-6…1` = 1 · 1.25 · 1.5 · 1.75 · 2 · 2.5 rem (16–40 px at the default root); `display-6…1` = 2.5–5 rem. Values above 1.25 rem scale down fluidly below a 1200 px viewport under RFS; `fs-5`, `fs-6`, `.lead`, and controls do not. There is no `rem` step below 1 rem: `.small` and `<small>` are `.875em`, so use one level only — `.small` inside `.small` is 12.25 px, off every scale — and take a 14 px or 12 px role from the generated `fs-sm`/`fs-xs` ([bootstrap-reference.md](bootstrap-reference.md) → Layout and type extensions). Stock 5.3 ships no letter-spacing utility; generate `ls-tight`/`ls-wide` there for display text and `text-uppercase` labels. `fw-light`/`fw-lighter` (300) belong only at display size; `.lead` and `display-*` ship at 300 by design.
243
+
229
244
  The composition traps in this group:
230
245
 
231
246
  - **`fs-*` without `lh-1` grows the row.** A resized glyph or mark keeps the parent's line-height, so the line box stretches and the row sits taller than its neighbors. Pair `fs-*` with `lh-1` on anything that is a mark rather than a paragraph.
232
- - **`text-truncate` zeroes a flex item's automatic minimum size** (that's the `min-width: 0` it carries). Inside a flex _column_, that also removes the floor that kept a heading at its own height: a growing sibling then squeezes the title from the bottom until it clips. Floor the title with `flex-shrink-0` and let the growing sibling absorb the change.
247
+ - **`text-truncate` is not a minimum-size utility.** It sets hidden overflow, ellipsis, and no wrapping; it does not declare `min-width: 0`. Give the truncating element a constrained width and make its flex ancestry shrink where required. In a constrained column, protect a title from height loss with `flex-shrink-0`. Take any missing inline-size utility from the project's generated scale, never an invented `.min-w-0`.
233
248
 
234
249
  ### Vertical Align
235
250
 
@@ -261,6 +276,17 @@ The composition traps in this group:
261
276
  | `5` | $spacer \* 3 (3rem = 48px) |
262
277
  | `auto` | auto |
263
278
 
279
+ Use the installed scale as the first choice. Assign its steps to internal, group, panel, and section
280
+ gaps; keep inter-group gaps larger than internal gaps. Compare adjacent steps before adding one.
281
+ The displayed pixel equivalents assume the default root size; they are not fixed pixel constraints.
282
+
283
+ The shipped jumps are +100 / +100 / +50 / +100 %: coarse above 16 px, with no 12 px or 32 px
284
+ step and nothing above 48 px for section rhythm. A missing step is not permission to type `p-2.5`
285
+ or `p-6`; those resolve to no rule and fail silently. Add a named step through
286
+ [bootstrap-reference.md](bootstrap-reference.md) → Layout and type extensions and verify the
287
+ generated class in the shipped build. Take type, width, and spacing decisions from
288
+ [frontend-design.md](frontend-design.md).
289
+
264
290
  ## Z-index Scale (components)
265
291
 
266
292
  | Component | Z-index |
@@ -288,25 +314,88 @@ Helpers are single-purpose classes that sit alongside utilities.
288
314
 
289
315
  - **`.visually-hidden`** — hide visually, keep for screen readers (icon-button labels, table caption text, "Danger:" prefixes).
290
316
  - **`.visually-hidden-focusable`** — hidden until focused; the skip-link class. Never combine with `.visually-hidden`.
291
- - **`.stretched-link`** — makes a whole `position-relative` container (e.g. a card) the click target of one inner link, without wrapping everything in `<a>`.
317
+ - **`.stretched-link`** — makes a whole `position-relative` container (for example, a card) the click target of one inner link, without wrapping everything in `<a>`.
292
318
  - **`.ratio .ratio-16x9`** (also `1x1`, `4x3`, `21x9`, or `--bs-aspect-ratio`) — responsive embeds/iframes.
293
319
  - **`.vstack` / `.hstack gap-*`** — shorthand vertical/horizontal flex stacks for quick toolbars and side rails.
294
320
  - **`.vr`** — vertical rule divider inside an `.hstack` or flex row.
295
- - **`.focus-ring`** (+ `.focus-ring-primary` … per theme color) — opt-in focus ring for custom interactive elements; tune via `--bs-focus-ring-width` (.25rem), `--bs-focus-ring-opacity` (.25), `--bs-focus-ring-color`, `--bs-focus-ring-x/y/blur`. Use it instead of `outline: none` hacks so keyboard focus stays visible.
296
- - **`.icon-link`** (+ `.icon-link-hover`) — pairs a Bootstrap Icon SVG with a text link; icon auto-sizes to 1em; give decorative icons `aria-hidden="true"`. Hover shift via `--bs-icon-link-transform`.
321
+ - **`.focus-ring`** (+ `.focus-ring-primary` … per theme color) — opt-in focus ring for custom interactive elements; tune through `--bs-focus-ring-width` (.25rem), `--bs-focus-ring-opacity` (.25), `--bs-focus-ring-color`, `--bs-focus-ring-x/y/blur`. Use it instead of `outline: none` hacks so keyboard focus stays visible.
322
+ - **`.icon-link`** (+ `.icon-link-hover`) — pairs a Bootstrap Icon SVG with a text link; icon auto-sizes to 1em; give decorative icons `aria-hidden="true"`. Hover shift through `--bs-icon-link-transform`.
297
323
 
298
324
  ## Enterprise notes (utilities)
299
325
 
300
326
  ### Composition habits
301
327
 
302
- - **Spacing scale:** prefer `gap-*` on flex/grid parents over scattering `m-*` on every child — the parent owns rhythm, children stay reorderable. Use `p-3` / `p-4` for panel padding; reserve `p-5` for sparse marketing-like empty states.
303
- - **Body surfaces:** `bg-body`, `bg-body-secondary`, `bg-body-tertiary` track `data-bs-theme` — raw `bg-white` / `bg-light` freeze the surface in light mode.
304
- - **Text hierarchy:** `text-body` for content, `text-body-secondary` for meta, `text-*-emphasis` for any status a reader acts on. The plain `text-success` / `text-danger` / `text-warning` colors and `text-body-tertiary` are the decoration tier ([SKILL.md](../SKILL.md) → Surfaces, color, contrast).
305
- - **Opacity traps:** `text-white-50` / `text-black-50` often fail contrast — prefer `text-opacity-75` on a known solid, or `text-body-secondary`. Every one of these pairings is measured against the shipped cascade in both themes; a skin retunes the same token names.
306
- - **Flex floors:** a flex column gives its items an automatic minimum size, and `text-truncate` removes it. Titles and marks that must keep their height carry `flex-shrink-0`; only the growing sibling absorbs the slack.
307
- - **Flex toolbars:** `d-flex align-items-center gap-2 flex-wrap` (or `flex-nowrap overflow-auto` for dense bars). Equal-height siblings: `align-items-stretch` + `h-100` on cards.
308
- - **Responsive hide:** show the best layout per breakpoint (`d-none d-md-block` vs `d-md-none`) rather than cramming one layout everywhere. Below `sm`, hide button captions (`d-none d-sm-inline` on the label span, `aria-label` on the control so the accessible name stays) before you let the brand or page title truncate.
309
- - **RTL safety:** always `ms-*`/`me-*`/`ps-*`/`pe-*`, `text-start`/`text-end`, `float-start`/`float-end` — the logical model is what lets one build serve LTR and RTL.
310
- - **Density as a system:** when a screen offers compact/comfortable density, drive it from a token or wrapper class that swaps padding — not ad-hoc `-sm` sprinkling per element ([bootstrap-reference.md](bootstrap-reference.md) → Design tokens).
311
- - **Shadows:** `shadow-sm` for panels in product UI; `shadow-lg` rarely belongs in dense admin screens.
312
- - **Print:** mark chrome `d-print-none`; keep the data table/results printable.
328
+ - **Spacing:** let the parent own rhythm with `gap-*`; use margins where the relationship requires
329
+ them. Choose panel padding from the shared scale, not a separate value per card. Keep labels,
330
+ controls, help, and errors closer together than neighboring groups: `.form-label` ships `.5rem`
331
+ below, so field groups take `mb-3` or `row g-3`. Headings ship `margin-bottom: .5rem` and no top
332
+ margin, so a section heading after a paragraph attaches to the wrong block — add `mt-4`/`mt-5`
333
+ or let a `vstack gap-4` parent own the rhythm. In a row, `hstack gap-2` inside a group and
334
+ `gap-4` between groups. Start a gap one step too large and step down.
335
+ - **Hierarchy:** use `fw-normal` for reading and `fw-semibold`/`fw-bold` for emphasis; headings
336
+ ship at 500, barely a step above body on a system stack, so pair a smaller heading class with
337
+ `fw-semibold` (`<h2 class="h5 fw-semibold">`) rather than a large heading at 500. Never
338
+ `fw-light` on UI text. Start ordinary text and quiet status with inheritance; add
339
+ `text-body-secondary` as the one quiet tier. Do not make metadata tiny or translucent to quiet
340
+ it. Quiet a heavy icon beside a label with `text-body-secondary` on the icon, not a larger label.
341
+ Quiet an active nav item's competitors (inherited text, normal weight) before making the active
342
+ item louder.
343
+ - **Type:** `fs-*` changes size, not semantic heading level. Choose `lh-*` by the text's role;
344
+ `lh-1` suits a glyph or suitable display treatment, not every paragraph. Keep prose start-aligned
345
+ and bound its measure independently of wider content.
346
+ - **Baseline:** use `align-items-baseline` for mixed-size text on one row. Keep `align-items-center`
347
+ for controls or icon/text combinations where the boxes, rather than text baselines, need to align.
348
+ - **Body surfaces:** `bg-body`, `bg-body-secondary`, and `bg-body-tertiary` track `data-bs-theme`.
349
+ Keep ordinary text inherited on those surfaces; use an explicit pair only at an owned boundary.
350
+ Take exceptions from [color-modes.md](color-modes.md).
351
+ - **Opacity:** do not use `text-white-50`, `text-black-50`, or `text-opacity-*` as the default secondary
352
+ tier. On colored fills, inherit the tested foreground or use a scoped opaque token. Any opacity
353
+ changes the composited contrast, including opacity on an ancestor.
354
+ - **Width:** use `w-100` inside a content-led maximum width, not `w-50` merely to avoid a wide form.
355
+ Stock Bootstrap has no prose-measure, fixed-rail, or zero-inline-minimum utility. Generate needed
356
+ roles through [bootstrap-reference.md](bootstrap-reference.md) → Layout and type extensions.
357
+ - **Flex floors:** allow a flexible main region to shrink without clipping its essential content.
358
+ Protect titles or marks that must retain height with `flex-shrink-0`. Do not add `overflow-hidden`
359
+ to an ancestor to conceal a layout bug; it can clip menus and focus rings.
360
+ - **Toolbars:** start with `d-grid gap-2` or labelled `row g-2` controls; expand with `d-sm-flex
361
+ flex-sm-wrap` or `col-md-auto` only when the container fits. Do not default to a horizontal
362
+ scroller for search or primary actions. Keep matching control sizes and usable hit areas. Align equal-height
363
+ cards only where their content benefits, not to fill empty space.
364
+ - **Responsive content:** follow [responsive-layout.md](responsive-layout.md); unprefixed utilities define the complete narrow task. A class that resolves can still implement the wrong layout. Reflow and prioritize before truncating. Hide a button caption only when
365
+ its remaining icon is recognizable and its accessible name remains complete; unfamiliar or
366
+ consequential actions keep visible labels. Do not erase task information to save the brand's width.
367
+ - **Links:** use persistent underlines for inline prose links. In conventional navigation, quieter
368
+ color and weight can carry hierarchy; retain visible hover and focus. Never rely on hover alone
369
+ for discovery. An action remains a button even when styled with `btn-link`.
370
+ - **RTL:** use `ms-*`/`me-*`/`ps-*`/`pe-*`, `text-start`/`text-end`, and logical custom properties;
371
+ verify the matching RTL build and the content's writing direction.
372
+ - **Density:** drive compact/comfortable variants from shared tokens or a wrapper, not scattered
373
+ per-element tweaks. Dense data retains readable text and ≥24px control targets.
374
+ - **Boundaries and depth:** separate with spacing first, then a surface change, then a shadow, then
375
+ a line: `bg-body-tertiary` panels instead of bordered ones; `card border-0 shadow-sm` on a page
376
+ surface that differs from the card; `list-group-flush`, `accordion-flush`, `table-borderless`,
377
+ `border-0` on a `card-header`; `gap-4` instead of an `<hr>`. Remove a border where a distinct
378
+ background already separates. Use `border-0` or `shadow-none` only when grouping and control
379
+ recognition survive. Assign `shadow-sm`, `shadow`, and `shadow-lg` by layer role; do not shadow
380
+ every panel or replace the focus indicator with depth. A `bg-body` panel on `bg-body-tertiary`
381
+ reads raised and `bg-body-secondary` inside `bg-body` reads inset — depth with no shadow, in
382
+ both modes.
383
+ - **Accents and decoration:** one accent border per region (see [Borders](#borders)); the shipped
384
+ `nav-underline` is the active-item accent. Alternate `bg-body` and `bg-body-tertiary` sections
385
+ before decorating; a `bg-primary-subtle` band emphasizes one panel. `bg-gradient` is a
386
+ white-to-transparent fade over the current fill, not a hero gradient.
387
+ - **Overlap:** `position-relative translate-middle-y` for a card that straddles two surfaces;
388
+ `mt-n*` only after `$enable-negative-margins`. Ring an overlapping avatar in the body color
389
+ with `rounded-circle border border-3` plus the `ring-body` class from
390
+ [bootstrap-reference.md](bootstrap-reference.md) → Elevation and depth, never `border-white`.
391
+ - **Images:** combine a declared frame or ratio with `object-fit-cover` only when cropping is safe;
392
+ take `object-fit-contain` when the whole asset matters. Guard a user upload against bleeding into
393
+ a same-color surface with `border border-black border-opacity-10` — translucent, so it does not
394
+ clash with the photo. Reduce a photo's dynamics before placing text on it: a `bg-dark
395
+ bg-opacity-50` (or `-75`) overlay layer under `card-img-overlay`, measured at every crop. Keep a
396
+ small glyph near 16–24 px inside `rounded-circle bg-primary-subtle p-3` rather than scaling it up.
397
+ Keep useful image detail and icon optical size rather than stretching assets to fill a box.
398
+ - **Lists and quotes:** `list-unstyled` with a meaningful glyph per item (`d-flex gap-2
399
+ align-items-baseline`, glyph `aria-hidden="true"`); `.blockquote` with `.blockquote-footer`.
400
+ - **Print:** mark chrome `d-print-none` and keep results readable; do not hide data merely because
401
+ its interactive controls have no print role.
@@ -8,10 +8,10 @@ model consumes.
8
8
  Test from the top down, and do not stop at the tier that passes:
9
9
 
10
10
  1. **Frontier** (the harness's default model) — proves the surface works at all.
11
- 2. **Mid tier** (e.g. a codex mechanical model) — proves the surface survives a harness's
12
- schema abbreviation and a model that reads less carefully.
13
- 3. **Small harness-native** (e.g. Haiku, a codex high-volume model) — the acceptance
14
- tier: these must walk the surface unaided, or the surface is not done.
11
+ 2. **Mid tier** (for example, a codex mechanical model) — proves the surface survives a
12
+ harness's schema abbreviation and a model that reads less carefully.
13
+ 3. **Small harness-native** (for example, Haiku or a codex high-volume model) — the
14
+ acceptance tier: these must walk the surface unaided, or the surface is not done.
15
15
  4. **Local floor** (a quantized 2B-class model through a real tool-calling client) — not
16
16
  an acceptance gate; a stochastic probe that exposes teaching gaps nothing else hits.
17
17
  Its residual failures must be provably consumer-floor (malformed emission, attention
@@ -29,7 +29,7 @@ Test from the top down, and do not stop at the tier that passes:
29
29
  - **Caps and journals.** Every pass runs as a tracked background command under a hard
30
30
  time cap with its transcript journaled; the journal is the evidence of record.
31
31
 
32
- ## Capture the reasoning, not just the calls
32
+ ## Capture the reasoning, not the calls alone
33
33
 
34
34
  Where the runtime exposes thinking (local runtimes expose it directly; harness stream
35
35
  formats carry interstitial text), record it. The call log shows WHAT failed; the trace
@@ -120,7 +120,7 @@ leaves this round is the procedure.
120
120
 
121
121
  When a round certifies an instrument — a pin, an identity check, a generated sweep — the controls
122
122
  are usually drawn from whatever the instrument obviously covers, because that is where the examples
123
- are easiest to construct. That sampling proves discrimination _within_ the population and is
123
+ take the least construction. That sampling proves discrimination _within_ the population and is
124
124
  routinely reported as proof the instrument works.
125
125
 
126
126
  So before running controls, write down the instrument's **membership rule** in one sentence, then
@@ -10,8 +10,9 @@ description: Run an Orkestrel release from layer order to registry confirmation.
10
10
  Read the current files in this order:
11
11
 
12
12
  1. `AGENTS.md` and every applicable `.claude/rules/*.md` file.
13
- 2. `.agents/orchestration.md` § Publishing the fleet and § Long-running commands. Each named
14
- section binds every step here.
13
+ 2. `.agents/orchestration.md` § Publishing the fleet, § Long-running commands, § Orchestrator and
14
+ executor, § Writing concurrency, and § Dispatch anatomy. Each named section binds every step
15
+ here.
15
16
  3. The reference the moment needs: [wave.md](references/wave.md) before visiting a repository,
16
17
  ruling on a bump, or preparing a layer; [window.md](references/window.md) before running
17
18
  `npm login` or any upload.
@@ -42,21 +43,17 @@ following the skill.
42
43
  Derive each pin from that reading, never from a local manifest.
43
44
  3. **Visit each repository.** Run the visit in [wave.md](references/wave.md) in its stated order,
44
45
  in parallel slices of disjoint repositories, each slice serial inside itself.
45
- 4. **Rule on each package's bump.** Apply the triggers in [wave.md](references/wave.md). A package
46
- whose published surface did not move takes its re-pin, its gates, and a commit to `main`, and
47
- does not publish.
48
- 5. **Prepare the whole layer before authenticating.** Bump, re-pin, install, sweep the self-pins,
49
- run each package's own `prepublishOnly` to green, commit, and push. Every one of those steps
50
- happens outside the window.
46
+ 4. **Rule on each package's bump.** Apply the triggers in [wave.md](references/wave.md) § Rule on
47
+ the bump; the contract's § What a bump obliges owns the blast radius.
48
+ 5. **Prepare the whole layer before authenticating**, in the order [wave.md](references/wave.md)
49
+ § Prepare a layer fixes. Every one of those steps happens outside the window.
51
50
  6. **Reach the approval.** Follow [window.md](references/window.md), and launch the login chain
52
51
  only after the user signals they are at the keyboard.
53
52
  7. **Authorize and upload.** Follow [window.md](references/window.md). Take the account's one-time
54
- code where it has one, because that path opens no window. Where the account answers with no
55
- code, the browser authorization opens the five-minute window: open the layer with one package,
56
- confirm its upload from the registry, then chase the remaining uploads back-to-back.
57
- 8. **Close the layer from the registry, then prepare the next.** A dependent's new pin cannot
58
- install until the version it names exists, so preparation and publication interleave and cannot
59
- be batched ahead.
53
+ code where it has one; § Authorize the upload there fixes that code's life and the layer it
54
+ carries. Where the account answers with no code, follow § Spend the window there.
55
+ 8. **Close the layer from the registry, then prepare the next**, per [wave.md](references/wave.md)
56
+ § Prepare a layer.
60
57
 
61
58
  Run that sequence for every layer, from the registry reading to the registry close. Refresh the
62
59
  registry evidence between layers rather than carrying the previous round's reading forward.
@@ -71,7 +68,10 @@ Completion requires:
71
68
  that section requires;
72
69
  - every tarball swap is restored per § Fixing a dependency before it publishes, and no target
73
70
  repository is left holding an uncommitted bump or an unpushed commit;
74
- - every gate that proved a package ran outside the window and against the artifact that shipped.
71
+ - every gate that proved a package ran outside the window and against the artifact that shipped;
72
+ - every gate red at a package's baseline for a cause `ROADMAP.md` already carries is recorded as a
73
+ standing reading beside the package's row with its carrier, and the release report names it. A
74
+ standing reading is not a gate the release ran.
75
75
 
76
76
  Report the layers in publish order, each package with its registry-confirmed version, the bump
77
77
  rulings and their evidence, the approvals the user granted, and anything still unpublished. End
@@ -10,9 +10,11 @@ prepare the next.
10
10
  Run the visit in this order. A step that reads generated or installed state is invalid before the
11
11
  step that writes it.
12
12
 
13
- 1. Re-pin the target's `@orkestrel/scaffold` devDependency and install, so the overwrite runs the
14
- current vendored host.
15
- 2. Run `scaffold overwrite`. One run repairs the `AGENTS.md` and `CLAUDE.md` pointers and deletes
13
+ 1. Re-pin every `@orkestrel` range to the registry caret (peer ranges included) and install, so
14
+ the overwrite runs the current vendored host.
15
+ 2. Commit the manifest and the lockfile as the preparation commit. `scaffold overwrite` refuses a
16
+ tree carrying uncommitted changes, and the install left both dirty.
17
+ 3. Run `scaffold overwrite`. One run repairs the `AGENTS.md` and `CLAUDE.md` pointers and deletes
16
18
  every tracked copy the target still holds at an instruction-canon path. Prove the sweep with a
17
19
  second `scaffold audit` that exits `0`.
18
20
  - Where the target's `.claude/agents/orkestrel.md` carries a body outside the marker-bounded
@@ -30,12 +32,16 @@ step that writes it.
30
32
  rather than waiving past it.
31
33
  - A copy the target git-ignores stays a `foreign` finding, so that target never reaches exit `0`
32
34
  again. Keep a local MCP server registration outside the repository rather than at `.mcp.json`.
33
- 3. Force-verify every `@orkestrel` range against a registry sweep taken after the previous layer
35
+ 4. Force-verify every `@orkestrel` range against a registry sweep taken after the previous layer
34
36
  published.
35
- 4. Run the full install.
36
- 5. Run the mutating `format` script to converge generated writes.
37
- 6. Run the quality gates.
38
- 7. Compare the rebuilt `dist/` against the published tarball for material content.
37
+ 5. Run the full install. The overwrite re-declares the toolchain ranges, so the lockfile the first
38
+ install regenerated no longer matches the manifest.
39
+ 6. Sweep the self-pins, per § Sweep the self-pins: the re-pin moves the snapshot class.
40
+ 7. Run the mutating `format` script to converge generated writes.
41
+ 8. Run the quality gates.
42
+ 9. Fetch the published tarball, then compare the rebuilt `dist/` against it for material content.
43
+ An absent baseline is an unanswered comparison, never a moved dist: fetch and re-run rather than
44
+ ruling a bump owed.
39
45
 
40
46
  Restore any unpublished tarball the target is holding before the quality gates run, per
41
47
  `.agents/orchestration.md` § Fixing a dependency before it publishes. A distribution proof run
@@ -69,7 +75,15 @@ final runtime dependency set differs from the published packument.
69
75
  per package rather than assuming it: a package that imports its own `package.json` version into
70
76
  published code emits that version, so its pre-bump dist is stale the moment the version moves.
71
77
  Rebuild after the bump there and pack from the rebuilt tree. The `npm publish --ignore-scripts`
72
- command skips `prepack`, so that rebuild is the operator's step rather than the publish's.
78
+ command skips `prepack`, so that rebuild is the operator's step rather than the publish's. The
79
+ same holds for a package that writes its declared ranges into published output: its `dist/`
80
+ moves on a development re-pin, and `.agents/orchestration.md` § What a bump obliges rules that
81
+ re-pin a release.
82
+
83
+ One trigger orders rather than bumps. A package the fleet consumes as a development dependency,
84
+ whose consumers' gates read its unpublished tip, publishes on its own account ahead of the layer
85
+ order and again at its own slot after its runtime ranges move: each consumer's visit installs the
86
+ registry copy over any staged tip, so every consumer stays red until that tip is on the registry.
73
87
 
74
88
  ## Prepare a layer
75
89
 
@@ -77,16 +91,22 @@ An unpublished package's first version is `0.0.1`. Do not bump it before that fi
77
91
  registry has nothing to serve, so there is no version to move away from, and bumping produces a
78
92
  package whose history starts at a number nothing explains.
79
93
 
80
- Prepare a published package's layer in this order:
94
+ Prepare a published package's layer in this order, after the visit has ruled the package's bump:
81
95
 
82
96
  1. **Bump from what the registry serves, not from the local manifest.** A repository's `version`
83
97
  field can sit a release behind what was published from another checkout, and bumping that
84
98
  produces a version the registry already holds, which fails on upload after the whole gate chain
85
99
  has run. Read the registry first.
86
- 2. **Re-pin every `@orkestrel` range to what the registry serves, and install.**
87
- 3. **Sweep the self-pins**, per the following section.
100
+ 2. **Re-pin every `@orkestrel` range to what the registry serves, and install.** The visit's
101
+ preparation commit already precedes the overwrite; this install regenerates the lockfile for the
102
+ bumped manifest.
103
+ 3. **Sweep the self-pins**, per the following section: the bump moves the version class.
88
104
  4. **Run each package's own `prepublishOnly` script to green.**
89
- 5. **Commit and push before the window opens.**
105
+ 5. **Write the release commit and push before the window opens.** The preparation commit inside
106
+ the visit is a different commit at a different moment.
107
+
108
+ Where an inventory taken before the round already ruled every dist moved, the bump rides the
109
+ visit's first step and these steps fold into the visit, whose comparison then confirms the ruling.
90
110
 
91
111
  Prepare the next layer only after this one is on the registry. A dependent's new pin cannot
92
112
  install until the version it names exists, so preparation and publication interleave and cannot be
@@ -99,12 +119,17 @@ the flag is what stops the gate chain running a second time inside the five minu
99
119
  ## Sweep the self-pins
100
120
 
101
121
  A package's own version appears in its source and its tests as a literal, and a bump falsifies
102
- every one of them. Run this sweep after the re-pin install, not after the manifest edit.
122
+ every one of them. A snapshot of generated output carries the ranges the package writes rather than
123
+ its own version, and any re-pin, a development one included, falsifies it. Run this sweep after the
124
+ re-pin install, not after the manifest edit.
103
125
 
104
- - `grep` the prior version literal across `tests/` and `src/` in the publishing package, and rule
105
- on every hit. A canned packument in a fixture and a looked-up version in a CLI suite carry the
126
+ - Search `tests/` and `src/` in the publishing package for the prior version literal, and rule on
127
+ every hit. A canned packument in a fixture and a looked-up version in a CLI suite carry the
106
128
  version with no tripwire comment beside them, so they surface as a red gate after the bump
107
129
  rather than as a planned edit before it.
130
+ - Search `tests/` for the prior range of every re-pinned dependency, and move each snapshot the
131
+ search hits with the re-pin. A generated-manifest fixture never carries the package's own prior
132
+ version, so the version sweep cannot reach it.
108
133
  - Move a documented tripwire — a golden digest over generated output — in the same change as the
109
134
  version bump. That is what the tripwire is for.
110
135
  - Re-take a generated artifact's digest after the install, because the generated bytes can derive
@@ -120,4 +145,5 @@ every one of them. Run this sweep after the re-pin install, not after the manife
120
145
 
121
146
  Refresh the registry evidence between layers and derive each round's pins from it. A pin can only
122
147
  name a version the registry already serves, so a dependency shipping in the same window keeps the
123
- resolvable previous pin and takes its development-only re-pin after the window closes.
148
+ resolvable previous pin and takes its development-only re-pin after the window closes. That re-pin
149
+ takes the self-pin sweep too, because the snapshot class moves with no bump.
@@ -1,10 +1,10 @@
1
1
  # The approval and the upload window
2
2
 
3
3
  A release needs the user at the keyboard to authenticate the session, and again to authorize each
4
- upload. An approval URL dies unclicked in under a minute, so mint one only in a
5
- moment the user can click, and relay it byte for byte. Where the account answers with a one-time
6
- code, take that code for the upload — it needs no browser authorization and opens no window to
7
- lose. Where the account has no code, the browser authorization opens a five-minute window, and the
4
+ upload. An approval URL dies unclicked in under a minute, so mint one only in a moment the user can
5
+ click, and relay it byte for byte. Where the account answers with a one-time code, take that code
6
+ for the upload — it needs no browser authorization, and § Authorize the upload states the code's own
7
+ life. Where the account has no code, the browser authorization opens a five-minute window, and the
8
8
  rest of the layer either fits inside it or takes another approval.
9
9
 
10
10
  ## Arm the terminal
@@ -25,8 +25,11 @@ rest of the layer either fits inside it or takes another approval.
25
25
  zero.
26
26
  - Re-probe `whoami` immediately before the first upload. A stored credential expires mid-session
27
27
  and an overnight gap expires it, so a session-start answer does not hold.
28
- - Read a login log that shows the spinner and then a legacy `Username:` prompt as an expired
29
- attempt rather than as a prompt to answer. Kill it by process id and mint a fresh flow.
28
+ - Read a login log that shows the spinner and then a legacy `Username:` prompt as a dead attempt
29
+ rather than as a prompt to answer: expired, or refused on its first poll per § Read a `403` on the
30
+ poll. Kill it by the process id recorded at its launch, per `.agents/orchestration.md` § Confirm
31
+ dead before relaunching. Every publish here runs under the same `script -qfc` form, so a pattern
32
+ over the process list reaches a live upload as readily as the dead login.
30
33
  - On a Windows host, Git Bash ships no `script` binary, so the upload step is operator-driven:
31
34
  prepare the layer, prove the gates, surface the exact `npm publish` command, and the operator
32
35
  runs it in a real terminal. Everything before and after the upload — bumps, re-pins, gates,
@@ -49,13 +52,17 @@ rest of the layer either fits inside it or takes another approval.
49
52
  against `registry.npmjs.org` with `npm` 10.9.7 and `node` 22.22.2: npm polls `GET /-/v1/done`
50
53
  every few seconds and takes `202` while the session waits, and the registry answers `403` at
51
54
  about 45 seconds. Whether the registry fixes that abandon by elapsed time or by poll count is
52
- unmeasured, so plan against the duration.
55
+ unmeasured, so plan against the duration. A `403` within seconds of the mint is not that abandon;
56
+ § Read a `403` on the poll names it.
53
57
  - Recognise the abandon on each side. The `npm login` command reads the `403` as web login being
54
58
  unsupported and drops to its legacy `Username:` prompt. The `npm publish` command exits `E403`
55
59
  naming `GET /-/v1/done?authId=`.
56
60
  - Never keep a link alive by re-minting on a loop. Each mint invalidates the URL before it, so a
57
61
  supervisor that re-mints on expiry makes the link a moving target and every relayed URL is dead
58
- on arrival. Mint once per human moment, and mint again only when the user asks.
62
+ on arrival. Mint once per human moment, and mint again only when the user asks. That ban covers
63
+ a URL already relayed. While no URL has been relayed, mint attempts until one survives its own
64
+ first poll, relay that one alone, and kill the rest by process id; § Read a `403` on the poll
65
+ names the first-poll refusal this answers.
59
66
  - Name the login approval and the upload authorization to the user before either arrives, or the
60
67
  authorization link reads as the login having failed.
61
68
  - Surface each approval URL the moment it appears in the log, and take the **last** one in log
@@ -75,17 +82,23 @@ rest of the layer either fits inside it or takes another approval.
75
82
 
76
83
  - Take the account's one-time code where the account has one. The
77
84
  `npm publish --ignore-scripts --otp=<code>` command uploads with no browser authorization and no
78
- poll, so it carries neither a window nor a race. In the `@orkestrel/scaffold` 0.0.56 run on
79
- 2026-08-27 the browser authorization failed on the 45-second abandon and the one-time code
80
- uploaded the package with no retry.
81
- - Ask for the code at the moment of the upload, and run the upload inside that code's own life. A
82
- code read minutes earlier is already spent.
85
+ poll. The code has its own life: measured on 2026-09-04 against `registry.npmjs.org`, one code
86
+ carried every upload of a layer started within about a minute of its first use, console at
87
+ 20:25:02 through router at 20:25:58, and the upload started at 20:25:58 was refused `EOTP` at
88
+ 20:26:00; a code read before its layer was ready was refused at its first upload.
89
+ - Prepare the whole layer, then ask for one code at the moment the layer's first upload starts, and
90
+ chase the layer's uploads back-to-back inside that code's life with no gate between them. A code
91
+ read minutes earlier is already spent.
92
+ - Read `EOTP` on this path as the code's life ending, never as the contention § Spend the window
93
+ describes for the browser path: the chain stops at the refused package, and the layer resumes
94
+ from that package on a fresh code. Never retry the refused upload on the same code.
83
95
  - Ask for the code and nothing else. Never ask for a password, an access token, or an auth file.
84
96
  `.agents/orchestration.md` § Publishing the fleet owns that law.
85
97
  - Arm a one-time-code upload the way § Arm the terminal arms every other publish.
86
98
  - Fall back to the browser authorization where the account answers with no code. That path mints
87
99
  the `auth/cli/<id>` URL, needs the click inside the session's life, and opens the five-minute
88
- window.
100
+ window. In the `@orkestrel/scaffold` 0.0.56 run on 2026-08-27 that authorization failed on the
101
+ 45-second abandon and the one-time code uploaded the package with no retry.
89
102
  - Tell the user that approving an `auth/cli/<id>` URL opens a five-minute window covering the rest
90
103
  of the layer.
91
104
 
@@ -95,7 +108,8 @@ rest of the layer either fits inside it or takes another approval.
95
108
  window, so none of it binds that path.
96
109
  - The window opens when the user approves, not when the first publish starts.
97
110
  - Open each layer with one package: publish it alone, surface its approval URL the moment the
98
- journal shows it, and confirm the upload from the registry before starting the rest.
111
+ journal shows it, and read its acceptance line in the journal before starting the rest; the
112
+ registry read confirms it.
99
113
  - Then chase the remaining uploads back-to-back in one process with no gap. An upload started
100
114
  within seconds of an approval frequently rides that approval, and each one that does not mints
101
115
  its own URL.
@@ -127,14 +141,25 @@ the same status. Rule from the evidence, never from which cause reads likelier.
127
141
  opened, and the registry closed the session.
128
142
  - The user clicked a superseded URL and poisoned the live attempt. The poll fails mid-flight while
129
143
  the user is looking at a page that reports success.
144
+ - The registry refused the attempt's first poll, seconds after the mint and before anyone could
145
+ click, because the poll left from an egress address other than the one that minted the session.
146
+ npm reads that `403` as web login unsupported and drops to the legacy `Username:` prompt within
147
+ seconds rather than at 45. Recover by minting attempts on a kept-alive connection until one
148
+ survives its first poll and relaying that one alone, per § Reach the approval.
130
149
  - Tell those causes apart from the log and the user, never from the status alone. A single minted
131
150
  URL that nobody opened in time is the abandon. A log carrying a URL the running attempt
132
- superseded, with the user reporting a click, is the poisoned attempt.
151
+ superseded, with the user reporting a click, is the poisoned attempt. A drop within seconds of
152
+ the mint, before any relay, is the egress refusal.
133
153
  - Recover the same way whichever it was: read the registry for the version, confirm no publish
134
154
  process is live, then mint exactly one fresh attempt with the user at the keyboard.
135
155
 
136
156
  ## Read the verdict from the registry
137
157
 
158
+ - Read `+ @orkestrel/<name>@<version>` in the upload's own journal as the accepted verdict, and
159
+ advance the chain on it. The registry's read lags its processing by minutes, which the
160
+ `Your package is being processed and may take a few minutes to become available.` notice beside
161
+ that line announces, so a chain keyed on the registry stops on an accepted upload. Read the
162
+ registry to confirm and to record the layer's close, never to gate the next upload.
138
163
  - Read the result from the registry, not from an exit code. A piped `npm publish` reports the exit
139
164
  status of the pipeline, and a CDN read straight after a publish can still serve the previous
140
165
  version.