@orkestrel/scaffold 0.0.64 → 0.0.66

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.
@@ -0,0 +1,237 @@
1
+ # Responsive layout
2
+
3
+ > Part of the `enterprise-bootstrap` skill. Use before composing a screen, shell, toolbar,
4
+ > form, overlay, or data view. Operate layer: [SKILL.md](../SKILL.md).
5
+
6
+ ## Contents
7
+
8
+ - [Declare the contract](#declare-the-contract)
9
+ - [Build the base](#build-the-base)
10
+ - [Bootstrap's responsive surface](#bootstraps-responsive-surface)
11
+ - [Expand by available space](#expand-by-available-space)
12
+ - [Keep the task intact](#keep-the-task-intact)
13
+ - [Handle navigation and overlays](#handle-navigation-and-overlays)
14
+ - [Prove the result](#prove-the-result)
15
+
16
+ ## Declare the contract
17
+
18
+ Record one row per distinct region before writing its layout. Reuse a region's contract instead
19
+ of repeating it on every screen. Take the host's supported range; absent one, prove normal content
20
+ at 320 CSS px and compose first at a representative 390 CSS px. These are verification widths,
21
+ not new Bootstrap breakpoints or guarantees about physical devices.
22
+
23
+ | Region | Narrow behavior | Expansion condition | Information/actions retained | Overflow |
24
+ | ---------------- | ------------------------------------------------------------- | --------------------------------------- | -------------------------------------------------- | ---------------------------- |
25
+ | Navigation | Named trigger opens a drawer | Rail plus usable main content fit | Same destinations and current state | Drawer body only when needed |
26
+ | Search/actions | Search and actions stack; filters wrap or disclose | Labels and hit areas fit together | Search, active filters, clear path, primary action | None |
27
+ | Record list | Identity, decision fields, primary action; details disclosure | Comparison columns fit beside any rail | Same data, sorting, selection, actions | None for the list |
28
+ | Comparison table | Named, keyboard-operable local scroller | Columns fit without a scroller | All comparison columns and row identity | Table region only |
29
+ | Form/dialog | One reading column; natural height | Related fields or explanation have room | Labels, errors, consequences, cancel/commit | One vertical owner |
30
+ | Hero/preview | Copy, actions, then legible evidence | Both columns remain useful | Thesis, primary action, useful demonstration | Decorative layer only |
31
+
32
+ Name the actual thresholds used and why. Do not give every region the same breakpoint by habit.
33
+ Build and use the narrow primary flow before adding desktop chrome or finishing details.
34
+
35
+ ## Build the base
36
+
37
+ Use Bootstrap's mobile-first direction: unprefixed rules apply from the smallest width; `sm`,
38
+ `md`, `lg`, `xl`, and `xxl` add behavior at their minimum widths. `xs` has no class infix.
39
+ Take the installed breakpoint map, not device labels, from
40
+ [Breakpoints & layout](bootstrap-reference.md#breakpoints--layout).
41
+
42
+ Prefer shipped structure before custom media queries:
43
+
44
+ ```html
45
+ <!-- One field per row until the content supports a pair. -->
46
+ <div class="row g-3">
47
+ <div class="col-12 col-md-6"><!-- labelled field --></div>
48
+ <div class="col-12 col-md-6"><!-- related labelled field --></div>
49
+ </div>
50
+
51
+ <!-- Stretch actions at the base; use their natural widths from sm. -->
52
+ <div class="d-grid gap-2 d-sm-flex flex-sm-wrap">
53
+ <button type="submit" class="btn btn-primary">Save changes</button>
54
+ <button type="button" class="btn btn-outline-secondary">Cancel</button>
55
+ </div>
56
+
57
+ <!-- Give search its own narrow row rather than crushing its input. -->
58
+ <form class="row g-2 align-items-end" role="search" aria-label="Find invoices">
59
+ <div class="col-12 col-md">
60
+ <label class="form-label" for="invoice-search">Search invoices</label>
61
+ <input class="form-control" id="invoice-search" type="search" />
62
+ </div>
63
+ <div class="col-12 col-sm-6 col-md-auto">
64
+ <label class="form-label" for="invoice-status">Status</label>
65
+ <select class="form-select" id="invoice-status">
66
+ <option>All statuses</option>
67
+ </select>
68
+ </div>
69
+ </form>
70
+ ```
71
+
72
+ Keep DOM order meaningful before arranging columns. Do not use visual `order-*` to separate focus
73
+ order from reading order. Use `p-3 p-lg-4` and `g-3 g-lg-4` to grow outer space independently of
74
+ control size. Keep `.row` gutters inside a compatible container or padded parent; do not add
75
+ unbudgeted `gap-*` to percentage columns whose widths already total the row.
76
+
77
+ Check the generated CSS. Stock width/height, overflow, and general position utilities do not all
78
+ ship breakpoint variants. Do not invent `w-md-auto`, `overflow-lg-auto`, `position-lg-sticky`, or
79
+ `min-w-0`. Responsive sticky helpers are a separate shipped family. Take missing roles through
80
+ [Layout and type extensions](bootstrap-reference.md#layout-and-type-extensions).
81
+
82
+ ## Bootstrap's responsive surface
83
+
84
+ Stock 5.3.8 thresholds are `min-width` breakpoints: `sm` 576, `md` 768, `lg` 992, `xl` 1200,
85
+ `xxl` 1400 px; `xs` has no infix, so an unprefixed class is the base and `sm-*` applies from
86
+ 576 px up. `.container` caps at 540 / 720 / 960 / 1140 / 1320 px; `container-{bp}` stays fluid
87
+ until its breakpoint; `container-fluid` always. Gutter and container padding are 1.5 rem, so a
88
+ 320 px viewport leaves 296 px of content and a 390 px viewport 366 px. Offcanvas panels are
89
+ 400 px wide (`w-100` on narrow viewports); modals are 300 / 500 / 800 / 1140 px.
90
+
91
+ Families that ship breakpoint infixes, read from the utilities map: `d-*`, `flex-*`,
92
+ `justify-content-*`, `align-items-*`, `align-self-*`, `align-content-*`, `order-*`, `float-*`,
93
+ `gap-*`, `row-gap-*`, `column-gap-*`, every `m*`/`p*` spacing class, `text-{bp}-start/center/end`,
94
+ `object-fit-*`; plus the grid (`col-*`, `row-cols-*`, `g-*`, `offset-*`), `container-*`,
95
+ `sticky-{bp}-top/bottom`, and the component thresholds `navbar-expand-*`, `offcanvas-*`,
96
+ `table-responsive-*`, `modal-fullscreen-*-down`, `dropdown-menu-{bp}-end/start`,
97
+ `list-group-horizontal-*`.
98
+
99
+ Families with no infix: `w-*`, `h-*`, `mw-*`, `vh-*`, `position-*`, `top/bottom/start/end-*`,
100
+ `overflow-*`, `border-*`, `rounded-*`, `shadow-*`, `fs-*`, `fw-*`, `lh-*`, `text-nowrap`,
101
+ `text-truncate`, `text-uppercase`, `opacity-*`, `hstack`/`vstack`, `btn-group-vertical`. Write
102
+ `d-flex flex-column flex-md-row gap-3` where a stack must become a row, and generate `w-md-auto`
103
+ or `overflow-lg-visible` through the utilities API with `responsive: true` when a role needs it.
104
+ That key reaches a `$utilities` entry and nothing else, so it generates `w-*` and `overflow-*`
105
+ infixes but cannot reach a component threshold, a grid class, or a helper such as `hstack`; change
106
+ the component's own breakpoint class instead
107
+ ([bootstrap-reference.md](bootstrap-reference.md) → Utilities API).
108
+
109
+ Recipes:
110
+
111
+ - **Table scroller.** `<div class="table-responsive" role="region" aria-label="Invoices"
112
+ tabindex="0">` — the shipped class is `overflow-x: auto` only; the name and `tabindex` make it
113
+ keyboard-reachable. `table-responsive-{bp}` scrolls only below the breakpoint.
114
+ - **Menus in a scroller.** A scroller clips its `dropdown-menu`. Add
115
+ `data-bs-popper-config='{"strategy":"fixed"}'` to the toggle, or open row actions in a
116
+ root-mounted dialog.
117
+ - **Dual representations.** Render the narrow list and the wide table from one data array and one
118
+ selection set keyed by record id. Hide the inactive view with `d-none d-lg-block` /
119
+ `d-lg-none` so only one is in the accessibility tree; keep every `id` unique per view; re-sync
120
+ selection, sort, and filter state into whichever view is active. A view that resolves is not a
121
+ second store.
122
+ - **Toolbar base.** `d-grid gap-2 d-sm-flex flex-sm-wrap` stretches controls at the base and
123
+ releases them from `sm`; give search its own `col-12 col-md` row.
124
+ - **Touch.** `btn` is 38 px tall at the default size, `btn-sm` 31 px, `btn-lg` 48 px. Primary
125
+ mobile controls take `btn` or `btn-lg`, never a scaled-up icon inside `btn-sm`.
126
+ - **Pager.** Keep previous/next and the current page at every width; hide other numbers with
127
+ `d-none d-sm-block` on the `page-item` before shrinking targets.
128
+ - **Joined groups.** A `btn-group` bent over two rows loses its shared corners; below the width
129
+ where its labels fit, use a `form-select` or independent wrapping buttons.
130
+
131
+ ## Expand by available space
132
+
133
+ Measure the content container after rails, gutters, and panel padding. A wide viewport can contain
134
+ a narrow main region, split pane, or dialog. Delay columns, keep an intrinsic layout, or use an
135
+ authorized container-query extension when reuse requires it. A viewport breakpoint alone does not
136
+ prove the component fits.
137
+
138
+ Let flex/grid children shrink: use the project's zero-inline-minimum role and, for custom grids,
139
+ `minmax(0, 1fr)` where appropriate. Break long identifiers at safe opportunities; expose complete
140
+ values through a usable detail view when truncation is unavoidable. Never shrink amounts, labels,
141
+ or input text to rescue a desktop row. Let identity/amount headers wrap independently and bound
142
+ long action labels to their container; a wrapping parent does not constrain an oversized child.
143
+ Preserve native input scrolling for long editable values.
144
+
145
+ Use a content-led maximum width, not an unconditional fixed width or `vw-100` inside a padded
146
+ container. Let content set height. Prefer ordinary page scrolling to a phone-sized nested viewport;
147
+ reserve bounded table scrolling for a documented comparison task. Do not use `vh-100`, transforms,
148
+ CSS zoom, or root `overflow-x-hidden` to make an oversized layout appear to fit.
149
+
150
+ Keep large typography and presentation space responsive without shrinking body text or hit areas.
151
+ Bootstrap RFS scales supported type; it does not reflow navigation, dialogs, or preview content.
152
+ A readable line count beats mechanically preserving a desktop hero's proportions.
153
+
154
+ ## Keep the task intact
155
+
156
+ Choose a data strategy by the task, not a fixed ranking:
157
+
158
+ - **Record work:** use a compact list or labelled stack when the job is finding and acting on one
159
+ record. Keep identity, status, amount, due date, and the relevant action directly discoverable;
160
+ put secondary details behind a named disclosure. Generate variants from one data/state model.
161
+ - **Comparison work:** retain a semantic table when column comparison is essential. Local horizontal
162
+ scrolling is valid; name the region, provide a scroll cue, and verify keyboard reach to both ends.
163
+ Keep search and pagination outside it. Do not turn every comparison into cards.
164
+ - **Priority columns:** omit a column from the narrow presentation only when the same information is
165
+ available through an operable detail path. `d-none` alone is not a content strategy.
166
+
167
+ Do not render two independent forms or state stores for narrow/wide variants. Keep IDs unique and
168
+ only the active representation in the accessibility/focus tree. Preserve filters, values, selected
169
+ record IDs, sort, and open-detail context across a live resize. Restore focus to the equivalent
170
+ visible control if its representation disappears; do not steal unrelated focus. Track the control
171
+ before hiding it: the browser may move focus to the body before a media-query listener runs.
172
+ A dialog closed after reflow returns to the visible equivalent of its original trigger.
173
+
174
+ Keep search and action bars in normal flow. Stack or wrap ordinary controls before considering a
175
+ scroller; put additional filters behind a working disclosure with active-filter count and reset.
176
+ Use a wrapping group of independent buttons or a select, not a joined `.btn-group` bent over two
177
+ rows. Keep consequential button labels visible. Preserve the current page and previous/next when
178
+ reducing a pager's numbered links.
179
+
180
+ Keep record actions outside a clipped table wrapper when necessary. Popper placement alone does
181
+ not guarantee escape from an overflow ancestor. Prefer a root-mounted dialog or an existing
182
+ portal implementation over z-index escalation.
183
+
184
+ Make touch targets comfortable without making text larger; take every dimension from
185
+ [bootstrap-reference.md](bootstrap-reference.md) → WCAG 2.2 requirements for app UI, which owns the target floor and
186
+ the mobile preference. Keep action affordances visible without hover. Measure effective label hit
187
+ areas for native checkboxes and switches.
188
+
189
+ ## Handle navigation and overlays
190
+
191
+ Use `navbar-expand-*` or responsive `offcanvas-*` from the installed build. Match the trigger's
192
+ visibility threshold to the inline panel. Preserve one destination set, a named trigger, current
193
+ state, keyboard operation, Escape, and focus return. Exercise a drawer opened below a threshold,
194
+ resized above it, then returned below; no stale backdrop, body scroll lock, or invisible focus trap
195
+ may remain. Do not assume the host wrapper behaves exactly like the stock plugin.
196
+
197
+ Keep overlay width within the viewport and height content-led with an explicit vertical scroll
198
+ owner. Bootstrap's `modal-fullscreen-*-down` belongs to its modal structure, not a native `<dialog>`.
199
+ For native dialogs, declare a bounded logical size in the stylesheet when needed; keep consequences,
200
+ fields, safe dismissal, and commit reachable on short screens and with enlarged text. Check the
201
+ initial view as well as the scrolled footer. When action focus would hide the beginning, focus a
202
+ static top heading with `tabindex="-1"`; keep a safe dismissal in the tab sequence. Follow the
203
+ [dialog focus guidance](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/).
204
+
205
+ Let nonessential sticky chrome become static on narrow or short screens. Where fixed controls are
206
+ required, reserve their space, account for safe-area insets, and check focus visibility. Use a
207
+ supported dynamic viewport unit only for a genuine viewport-bound requirement; it does not prove
208
+ virtual-keyboard behavior. Test the soft keyboard and browser chrome on a real supported device,
209
+ or leave that coverage open.
210
+
211
+ Keep theme ownership intact during reflow. A dark hero or drawer on a light page establishes its
212
+ foreground/background at its boundary; ordinary descendants inherit. Take adaptive subtle fills
213
+ and exceptions from [color-modes.md](color-modes.md), not a separate mobile palette.
214
+
215
+ ## Prove the result
216
+
217
+ Run [Responsive task and reflow](inspection.md#responsive-task-and-reflow) and
218
+ [Responsive interaction continuity](inspection.md#responsive-interaction-continuity). Check 320 and
219
+ 390 CSS px, one wide view, and `b−1`, `b`, `b+1` for each used breakpoint; deduplicate overlaps.
220
+ Check narrow/short landscape, long unbroken identifiers, expanded copy, enlarged text, and the
221
+ states actually used. Cross every declared mode with the important narrow/wide states.
222
+
223
+ Require both geometry and task evidence. A page with no horizontal overflow can still hide its
224
+ primary action, clip a menu, or push decision fields into an undiscoverable scroller. Conversely,
225
+ a valid local two-dimensional scroller is not a page-level reflow failure.
226
+
227
+ Capture narrow output first and inspect it before desktop. Preserve before/after evidence for
228
+ regressions; run known-invalid controls through the same readers. State the Bootstrap build,
229
+ browser, assets, CSS viewport, state, and exclusions. Do not call a screenshot, class scan, reduced
230
+ viewport, device-pixel-ratio change, or emulated touch a real-device or complete accessibility pass.
231
+
232
+ Take upstream behavior from Bootstrap's [breakpoints](https://getbootstrap.com/docs/5.3/layout/breakpoints/),
233
+ [grid](https://getbootstrap.com/docs/5.3/layout/grid/), [flex](https://getbootstrap.com/docs/5.3/utilities/flex/),
234
+ [offcanvas](https://getbootstrap.com/docs/5.3/components/offcanvas/), and
235
+ [tables](https://getbootstrap.com/docs/5.3/content/tables/). Distinguish this skill's test matrix
236
+ from the requirements and two-dimensional-content exception in
237
+ [WCAG reflow](https://www.w3.org/WAI/WCAG22/Understanding/reflow.html).
@@ -1,6 +1,6 @@
1
1
  # Bootstrap 5 Utilities Reference
2
2
 
3
- > Part of the `enterprise-bootstrap` package. Bootstrap **5.3.x** class index +
3
+ > Part of the `enterprise-bootstrap` skill. Bootstrap **5.3.x** class index +
4
4
  > composition notes. Component markup: [components.md](components.md).
5
5
  > Theming, patterns, a11y: [bootstrap-reference.md](bootstrap-reference.md).
6
6
 
@@ -24,7 +24,9 @@
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
+ `bg-body`, `bg-body-secondary`, `bg-body-tertiary`, and `bg-*-subtle` adapt; `bg-{theme}`, `bg-light`, `bg-dark`, `bg-white`, `bg-black`, and `bg-gradient` are fixed. Inherited text on a fixed fill is never a pair — measure it in each declared mode (stock `bg-light` measures 1.2:1 in dark); a fixed fill takes `text-bg-*`, its component's foreground, or a `data-bs-theme` scope with `text-body` on the same element (the scope changes variables only; plain text inherits the outer mode's painted color).
28
+
29
+ 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
30
 
29
31
  ### Borders
30
32
 
@@ -39,7 +41,11 @@ Prefer `bg-body-*` and `*-subtle` over `bg-white`/`bg-light` — they track `dat
39
41
  .rounded-0, .rounded-1, .rounded-2, .rounded-3, .rounded-4, .rounded-5
40
42
  ```
41
43
 
42
- For borders that must stay visible in both color modes, prefer the `border-*-subtle` classes (theme-adaptive) over raw color borders.
44
+ 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.
45
+
46
+ - **`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.
47
+ - Strengthen a rule that reads too faint with `border-2` on its soft color, not with a darker color; heavier width keeps the softness.
48
+ - `border-{color}` is the fixed brand color in light and dark; check an accent against a dark `bg-*-subtle` before shipping it.
43
49
 
44
50
  ### Colors (Text)
45
51
 
@@ -48,10 +54,13 @@ For borders that must stay visible in both color modes, prefer the `border-*-sub
48
54
  .text-body, .text-body-secondary, .text-body-tertiary, .text-body-emphasis
49
55
  .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
56
  .text-black, .text-white, .text-black-50, .text-white-50
51
- .text-muted /* DEPRECATED in 5.3 — use .text-body-secondary; removed in v6 */
57
+ .text-muted /* Deprecated in 5.3; use a deliberate .text-body-secondary role instead. */
58
+ .text-reset /* Restore inherited color; not the same as .text-body. */
52
59
  .text-opacity-25, .text-opacity-50, .text-opacity-75, .text-opacity-100
53
60
  ```
54
61
 
62
+ Default ordinary text to inheritance. `text-{theme}`, `text-white`, `text-black`, `text-light`, `text-dark`, and their `-50` variants are fixed in light and dark; `text-body*`, `text-muted` (alias), and `text-*-emphasis` adapt — pair them with adaptive surfaces only ([color-modes.md](color-modes.md) → Fixed and adaptive classes). Classification and deprecation are separate axes: `text-muted` adapts and pairs correctly, and 5.3 deprecates it, so replace it with `text-body-secondary` on the deprecation rather than on a contrast reading. `text-body-secondary` (body color at .75 alpha) clears 4.5:1 on every stock body surface in light and dark; `text-body-tertiary` (.5 alpha) measures 3.0–4.1:1 there and is decoration or disabled only. Those readings bound stock 5.3.8 — re-measure under a declared theme or skin. 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.
63
+
55
64
  ### Display
56
65
 
57
66
  ```css
@@ -120,6 +129,8 @@ For borders that must stay visible in both color modes, prefer the `border-*-sub
120
129
  .link-offset-1, .link-offset-2, .link-offset-3
121
130
  ```
122
131
 
132
+ 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.
133
+
123
134
  ### Object Fit
124
135
 
125
136
  ```css
@@ -133,6 +144,8 @@ For borders that must stay visible in both color modes, prefer the `border-*-sub
133
144
  .opacity-0, .opacity-25, .opacity-50, .opacity-75, .opacity-100
134
145
  ```
135
146
 
147
+ 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.
148
+
136
149
  ### Overflow
137
150
 
138
151
  ```css
@@ -154,12 +167,16 @@ For borders that must stay visible in both color modes, prefer the `border-*-sub
154
167
  .translate-middle, .translate-middle-x, .translate-middle-y
155
168
  ```
156
169
 
170
+ Responsive infixes: `d`, `flex`, `justify-content`, `align-*`, `order`, `float`, `gap`, spacing, `text-{bp}-start/center/end`, and `object-fit` ship them; `w`, `h`, `position`, `overflow`, `border`, `rounded`, `shadow`, `fs`, `fw`, `lh`, `text-nowrap`, `text-truncate`, `hstack`/`vstack` do not ([responsive-layout.md](responsive-layout.md) → Bootstrap's responsive surface). Generate a missing infix only where a `$utilities` entry owns the property; `hstack` and `vstack` are helpers with no entry, so no key makes them responsive ([bootstrap-reference.md](bootstrap-reference.md) → Utilities API).
171
+
157
172
  ### Shadows
158
173
 
159
174
  ```css
160
175
  .shadow-none, .shadow-sm, .shadow, .shadow-lg
161
176
  ```
162
177
 
178
+ Assign the shipped elevation steps by layer role: `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.
179
+
163
180
  ### Sizing
164
181
 
165
182
  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)):
@@ -222,14 +239,16 @@ Notes: `s`/`e` are logical start/end — they flip automatically under RTL; neve
222
239
  /* Size */
223
240
  .fs-1, .fs-2, .fs-3, .fs-4, .fs-5, .fs-6
224
241
 
225
- /* Truncate — needs display block/inline-block or a flex child with min-width 0 */
242
+ /* Truncate — needs a constrained inline width and block/inline-block layout */
226
243
  .text-truncate
227
244
  ```
228
245
 
246
+ `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.
247
+
229
248
  The composition traps in this group:
230
249
 
231
250
  - **`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.
251
+ - **`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
252
 
234
253
  ### Vertical Align
235
254
 
@@ -261,6 +280,17 @@ The composition traps in this group:
261
280
  | `5` | $spacer \* 3 (3rem = 48px) |
262
281
  | `auto` | auto |
263
282
 
283
+ Use the installed scale as the first choice. Assign its steps to internal, group, panel, and section
284
+ gaps; keep inter-group gaps larger than internal gaps. Compare adjacent steps before adding one.
285
+ The displayed pixel equivalents assume the default root size; they are not fixed pixel constraints.
286
+
287
+ The shipped jumps are +100 / +100 / +50 / +100 %: coarse above 16 px, with no 12 px or 32 px
288
+ step and nothing above 48 px for section rhythm. A missing step is not permission to type `p-2.5`
289
+ or `p-6`; those resolve to no rule and fail silently. Add a named step through
290
+ [bootstrap-reference.md](bootstrap-reference.md) → Layout and type extensions and verify the
291
+ generated class in the shipped build. Take type, width, and spacing decisions from
292
+ [frontend-design.md](frontend-design.md).
293
+
264
294
  ## Z-index Scale (components)
265
295
 
266
296
  | Component | Z-index |
@@ -299,14 +329,78 @@ Helpers are single-purpose classes that sit alongside utilities.
299
329
 
300
330
  ### Composition habits
301
331
 
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.
332
+ - **Spacing:** let the parent own rhythm with `gap-*`; use margins where the relationship requires
333
+ them. Choose panel padding from the shared scale, not a separate value per card. Keep labels,
334
+ controls, help, and errors closer together than neighboring groups: `.form-label` ships `.5rem`
335
+ below, so field groups take `mb-3` or `row g-3`. Headings ship `margin-bottom: .5rem` and no top
336
+ margin, so a section heading after a paragraph attaches to the wrong block — add `mt-4`/`mt-5`
337
+ or let a `vstack gap-4` parent own the rhythm. In a row, `hstack gap-2` inside a group and
338
+ `gap-4` between groups. Start a gap one step too large and step down.
339
+ - **Hierarchy:** use `fw-normal` for reading and `fw-semibold`/`fw-bold` for emphasis; headings
340
+ ship at 500, barely a step above body on a system stack, so pair a smaller heading class with
341
+ `fw-semibold` (`<h2 class="h5 fw-semibold">`) rather than a large heading at 500. Never
342
+ `fw-light` on UI text. Start ordinary text and quiet status with inheritance; add
343
+ `text-body-secondary` as the one quiet tier. Do not make metadata tiny or translucent to quiet
344
+ it. Quiet a heavy icon beside a label with `text-body-secondary` on the icon, not a larger label.
345
+ Quiet an active nav item's competitors (inherited text, normal weight) before making the active
346
+ item louder.
347
+ - **Type:** `fs-*` changes size, not semantic heading level. Choose `lh-*` by the text's role;
348
+ `lh-1` suits a glyph or suitable display treatment, not every paragraph. Keep prose start-aligned
349
+ and bound its measure independently of wider content.
350
+ - **Baseline:** use `align-items-baseline` for mixed-size text on one row. Keep `align-items-center`
351
+ for controls or icon/text combinations where the boxes, rather than text baselines, need to align.
352
+ - **Body surfaces:** `bg-body`, `bg-body-secondary`, and `bg-body-tertiary` track `data-bs-theme`.
353
+ Keep ordinary text inherited on those surfaces; use an explicit pair only at an owned boundary.
354
+ Take exceptions from [color-modes.md](color-modes.md).
355
+ - **Opacity:** do not use `text-white-50`, `text-black-50`, or `text-opacity-*` as the default secondary
356
+ tier. On colored fills, inherit the tested foreground or use a scoped opaque token. Any opacity
357
+ changes the composited contrast, including opacity on an ancestor.
358
+ - **Width:** use `w-100` inside a content-led maximum width, not `w-50` merely to avoid a wide form.
359
+ Stock Bootstrap has no prose-measure, fixed-rail, or zero-inline-minimum utility. Generate needed
360
+ roles through [bootstrap-reference.md](bootstrap-reference.md) → Layout and type extensions.
361
+ - **Flex floors:** allow a flexible main region to shrink without clipping its essential content.
362
+ Protect titles or marks that must retain height with `flex-shrink-0`. Do not add `overflow-hidden`
363
+ to an ancestor to conceal a layout bug; it can clip menus and focus rings.
364
+ - **Toolbars:** start with `d-grid gap-2` or labelled `row g-2` controls; expand with `d-sm-flex
365
+ flex-sm-wrap` or `col-md-auto` only when the container fits. Do not default to a horizontal
366
+ scroller for search or primary actions. Keep matching control sizes and usable hit areas. Align equal-height
367
+ cards only where their content benefits, not to fill empty space.
368
+ - **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
369
+ its remaining icon is recognizable and its accessible name remains complete; unfamiliar or
370
+ consequential actions keep visible labels. Do not erase task information to save the brand's width.
371
+ - **Links:** use persistent underlines for inline prose links. In conventional navigation, quieter
372
+ color and weight can carry hierarchy; retain visible hover and focus. Never rely on hover alone
373
+ for discovery. An action remains a button even when styled with `btn-link`.
374
+ - **RTL:** use `ms-*`/`me-*`/`ps-*`/`pe-*`, `text-start`/`text-end`, and logical custom properties;
375
+ verify the matching RTL build and the content's writing direction.
376
+ - **Density:** drive compact/comfortable variants from shared tokens or a wrapper, not scattered
377
+ per-element tweaks. Dense data retains readable text and control targets at the floor in
378
+ [bootstrap-reference.md](bootstrap-reference.md) → WCAG 2.2 requirements for app UI.
379
+ - **Boundaries and depth:** separate with spacing first, then a surface change, then a shadow, then
380
+ a line: `bg-body-tertiary` panels instead of bordered ones; `card border-0 shadow-sm` on a page
381
+ surface that differs from the card; `list-group-flush`, `accordion-flush`, `table-borderless`,
382
+ `border-0` on a `card-header`; `gap-4` instead of an `<hr>`. Remove a border where a distinct
383
+ background already separates. Use `border-0` or `shadow-none` only when grouping and control
384
+ recognition survive. Assign `shadow-sm`, `shadow`, and `shadow-lg` by layer role; do not shadow
385
+ every panel or replace the focus indicator with depth. A `bg-body` panel on `bg-body-tertiary`
386
+ reads raised and `bg-body-secondary` inside `bg-body` reads inset — depth with no shadow, in
387
+ light and dark.
388
+ - **Accents and decoration:** one accent border per region (see [Borders](#borders)); the shipped
389
+ `nav-underline` is the active-item accent. Alternate `bg-body` and `bg-body-tertiary` sections
390
+ before decorating; a `bg-primary-subtle` band emphasizes one panel. `bg-gradient` is a
391
+ white-to-transparent fade over the current fill, not a hero gradient.
392
+ - **Overlap:** `position-relative translate-middle-y` for a card that straddles two surfaces;
393
+ `mt-n*` only after `$enable-negative-margins`. Ring an overlapping avatar in the body color
394
+ with `rounded-circle border border-3` plus the `ring-body` class from
395
+ [bootstrap-reference.md](bootstrap-reference.md) → Elevation and depth, never `border-white`.
396
+ - **Images:** combine a declared frame or ratio with `object-fit-cover` only when cropping is safe;
397
+ take `object-fit-contain` when the whole asset matters. Guard a user upload against bleeding into
398
+ a same-color surface with `border border-black border-opacity-10` — translucent, so it does not
399
+ clash with the photo. Reduce a photo's dynamics before placing text on it: a `bg-dark
400
+ bg-opacity-50` (or `-75`) overlay layer under `card-img-overlay`, measured at every crop. Keep a
401
+ small glyph near 16–24 px inside `rounded-circle bg-primary-subtle p-3` rather than scaling it up.
402
+ Keep useful image detail and icon optical size rather than stretching assets to fill a box.
403
+ - **Lists and quotes:** `list-unstyled` with a meaningful glyph per item (`d-flex gap-2
404
+ align-items-baseline`, glyph `aria-hidden="true"`); `.blockquote` with `.blockquote-footer`.
405
+ - **Print:** mark chrome `d-print-none` and keep results readable; do not hide data merely because
406
+ its interactive controls have no print role.
@@ -46,9 +46,9 @@ so network-controlled descriptions never enter agent instruction context.
46
46
  | Package | Version | Layer | Runtime dependencies | Peer dependencies |
47
47
  | ----------------------- | -------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
48
48
  | `@orkestrel/abort` | `0.0.10` | L1 | `@orkestrel/contract` `^0.0.17` | |
49
- | `@orkestrel/agent` | `0.0.20` | L5 | `@orkestrel/abort` `^0.0.9`, `@orkestrel/budget` `^0.0.9`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/database` `^0.0.13`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/queue` `^0.0.12`, `@orkestrel/timeout` `^0.0.9`, `@orkestrel/tool` `^0.0.13`, `@orkestrel/workflow` `^0.0.17`, `@orkestrel/workspace` `^0.0.7` | |
50
- | `@orkestrel/brief` | `0.0.7` | L4 | `@orkestrel/contract` `^0.0.16`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/interpret` `^0.0.12`, `@orkestrel/reason` `^0.0.9` | |
51
- | `@orkestrel/browser` | `0.0.15` | L3 | `@orkestrel/contract` `^0.0.16`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/html` `^0.0.8`, `@orkestrel/websocket` `^0.0.11` | |
49
+ | `@orkestrel/agent` | `0.0.21` | L5 | `@orkestrel/abort` `^0.0.10`, `@orkestrel/budget` `^0.0.10`, `@orkestrel/contract` `^0.0.17`, `@orkestrel/database` `^0.0.14`, `@orkestrel/emitter` `^0.0.10`, `@orkestrel/queue` `^0.0.13`, `@orkestrel/timeout` `^0.0.10`, `@orkestrel/tool` `^0.0.14`, `@orkestrel/workflow` `^0.0.18`, `@orkestrel/workspace` `^0.0.8` | |
50
+ | `@orkestrel/brief` | `0.0.8` | L4 | `@orkestrel/reason` `^0.0.10`, `@orkestrel/emitter` `^0.0.10`, `@orkestrel/contract` `^0.0.17`, `@orkestrel/interpret` `^0.0.13` | |
51
+ | `@orkestrel/browser` | `0.0.16` | L3 | `@orkestrel/html` `^0.0.9`, `@orkestrel/emitter` `^0.0.10`, `@orkestrel/contract` `^0.0.17`, `@orkestrel/websocket` `^0.0.12` | |
52
52
  | `@orkestrel/budget` | `0.0.10` | L1 | `@orkestrel/contract` `^0.0.17` | |
53
53
  | `@orkestrel/codec` | `0.0.3` | L0 | | |
54
54
  | `@orkestrel/console` | `0.0.13` | L2 | `@orkestrel/emitter` `^0.0.10`, `@orkestrel/contract` `^0.0.17` | |
@@ -57,44 +57,44 @@ so network-controlled descriptions never enter agent instruction context.
57
57
  | `@orkestrel/database` | `0.0.14` | L2 | `@orkestrel/contract` `^0.0.17`, `@orkestrel/emitter` `^0.0.10`, `@orkestrel/indexeddb` `^0.0.11`, `@orkestrel/sqlite` `^0.0.11` | |
58
58
  | `@orkestrel/emitter` | `0.0.10` | L1 | `@orkestrel/contract` `^0.0.17` | |
59
59
  | `@orkestrel/form` | `0.0.6` | L2 | `@orkestrel/contract` `^0.0.17`, `@orkestrel/emitter` `^0.0.10` | |
60
- | `@orkestrel/guide` | `0.0.17` | L3 | `@orkestrel/contract` `^0.0.16`, `@orkestrel/markdown` `^0.0.13` | |
60
+ | `@orkestrel/guide` | `0.0.18` | L3 | `@orkestrel/contract` `^0.0.17`, `@orkestrel/markdown` `^0.0.14` | |
61
61
  | `@orkestrel/html` | `0.0.9` | L1 | `@orkestrel/contract` `^0.0.17` | |
62
62
  | `@orkestrel/indexeddb` | `0.0.11` | L1 | `@orkestrel/contract` `^0.0.17` | |
63
- | `@orkestrel/interpret` | `0.0.12` | L3 | `@orkestrel/reason` `^0.0.9`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/template` `^0.0.6` | |
64
- | `@orkestrel/lsp` | `0.0.6` | L3 | `@orkestrel/emitter` `^0.0.9`, `@orkestrel/process` `^0.0.10`, `@orkestrel/contract` `^0.0.16` | |
63
+ | `@orkestrel/interpret` | `0.0.13` | L3 | `@orkestrel/reason` `^0.0.10`, `@orkestrel/emitter` `^0.0.10`, `@orkestrel/contract` `^0.0.17`, `@orkestrel/template` `^0.0.7` | |
64
+ | `@orkestrel/lsp` | `0.0.7` | L3 | `@orkestrel/emitter` `^0.0.10`, `@orkestrel/process` `^0.0.11`, `@orkestrel/contract` `^0.0.17` | |
65
65
  | `@orkestrel/markdown` | `0.0.14` | L2 | `@orkestrel/contract` `^0.0.17`, `@orkestrel/html` `^0.0.9` | |
66
- | `@orkestrel/mcp` | `0.0.28` | L4 | `@orkestrel/codec` `^0.0.2`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/process` `^0.0.10`, `@orkestrel/sse` `^0.0.6`, `@orkestrel/tool` `^0.0.13`, `@orkestrel/websocket` `^0.0.11` | `@orkestrel/router` `^0.0.13`, `@orkestrel/server` `^0.0.18` |
67
- | `@orkestrel/middleware` | `0.0.19` | L4 | `@orkestrel/abort` `^0.0.9`, `@orkestrel/budget` `^0.0.9`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/timeout` `^0.0.9` | `@orkestrel/database` `^0.0.13`, `@orkestrel/server` `^0.0.18` |
66
+ | `@orkestrel/mcp` | `0.0.29` | L4 | `@orkestrel/sse` `^0.0.7`, `@orkestrel/tool` `^0.0.14`, `@orkestrel/codec` `^0.0.3`, `@orkestrel/emitter` `^0.0.10`, `@orkestrel/process` `^0.0.11`, `@orkestrel/contract` `^0.0.17`, `@orkestrel/websocket` `^0.0.12` | `@orkestrel/router` `^0.0.14`, `@orkestrel/server` `^0.0.19` |
67
+ | `@orkestrel/middleware` | `0.0.20` | L4 | `@orkestrel/abort` `^0.0.10`, `@orkestrel/budget` `^0.0.10`, `@orkestrel/contract` `^0.0.17`, `@orkestrel/timeout` `^0.0.10` | `@orkestrel/database` `^0.0.14`, `@orkestrel/server` `^0.0.19` |
68
68
  | `@orkestrel/msg` | `0.0.10` | L0 | | |
69
69
  | `@orkestrel/ndjson` | `0.0.10` | L1 | `@orkestrel/contract` `^0.0.17` | |
70
- | `@orkestrel/ollama` | `0.0.14` | L6 | `@orkestrel/agent` `^0.0.20`, `@orkestrel/budget` `^0.0.9`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/ndjson` `^0.0.9`, `@orkestrel/timeout` `^0.0.9`, `@orkestrel/tool` `^0.0.13` | |
70
+ | `@orkestrel/ollama` | `0.0.15` | L6 | `@orkestrel/agent` `^0.0.21`, `@orkestrel/budget` `^0.0.10`, `@orkestrel/contract` `^0.0.17`, `@orkestrel/ndjson` `^0.0.10`, `@orkestrel/timeout` `^0.0.10`, `@orkestrel/tool` `^0.0.14` | |
71
71
  | `@orkestrel/pool` | `0.0.11` | L2 | `@orkestrel/emitter` `^0.0.10` | |
72
- | `@orkestrel/probe` | `0.0.12` | L5 | `@orkestrel/contract` `^0.0.16`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/lsp` `^0.0.6`, `@orkestrel/mcp` `^0.0.28`, `@orkestrel/queue` `^0.0.12`, `@orkestrel/timeout` `^0.0.9`, `@orkestrel/tool` `^0.0.13` | `oxlint` `^1.80.0`, `typescript` `^6.0.3`, `vitest` `^4.1.11` |
73
- | `@orkestrel/process` | `0.0.11` | L2 | `@orkestrel/contract` `^0.0.17`, `@orkestrel/emitter` `^0.0.10` | |
74
- | `@orkestrel/program` | `0.0.12` | L4 | `@orkestrel/contract` `^0.0.16`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/qualifier` `^0.0.13`, `@orkestrel/rater` `^0.0.13`, `@orkestrel/reason` `^0.0.9` | |
75
- | `@orkestrel/qualifier` | `0.0.13` | L3 | `@orkestrel/contract` `^0.0.16`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/reason` `^0.0.9` | |
76
- | `@orkestrel/queue` | `0.0.12` | L3 | `@orkestrel/abort` `^0.0.9`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/database` `^0.0.13`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/timeout` `^0.0.9` | |
77
- | `@orkestrel/rater` | `0.0.13` | L3 | `@orkestrel/reason` `^0.0.9`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/contract` `^0.0.16` | |
72
+ | `@orkestrel/probe` | `0.0.13` | L5 | `@orkestrel/contract` `^0.0.17`, `@orkestrel/emitter` `^0.0.10`, `@orkestrel/lsp` `^0.0.7`, `@orkestrel/mcp` `^0.0.29`, `@orkestrel/queue` `^0.0.13`, `@orkestrel/timeout` `^0.0.10`, `@orkestrel/tool` `^0.0.14` | `oxlint` `^1.82.0`, `typescript` `^6.0.3`, `vitest` `^4.1.11` |
73
+ | `@orkestrel/process` | `0.0.12` | L2 | `@orkestrel/contract` `^0.0.17`, `@orkestrel/emitter` `^0.0.10` | |
74
+ | `@orkestrel/program` | `0.0.13` | L4 | `@orkestrel/contract` `^0.0.17`, `@orkestrel/emitter` `^0.0.10`, `@orkestrel/qualifier` `^0.0.14`, `@orkestrel/rater` `^0.0.14`, `@orkestrel/reason` `^0.0.10` | |
75
+ | `@orkestrel/qualifier` | `0.0.14` | L3 | `@orkestrel/contract` `^0.0.17`, `@orkestrel/emitter` `^0.0.10`, `@orkestrel/reason` `^0.0.10` | |
76
+ | `@orkestrel/queue` | `0.0.13` | L3 | `@orkestrel/abort` `^0.0.10`, `@orkestrel/contract` `^0.0.17`, `@orkestrel/database` `^0.0.14`, `@orkestrel/emitter` `^0.0.10`, `@orkestrel/timeout` `^0.0.10` | |
77
+ | `@orkestrel/rater` | `0.0.14` | L3 | `@orkestrel/reason` `^0.0.10`, `@orkestrel/emitter` `^0.0.10`, `@orkestrel/contract` `^0.0.17` | |
78
78
  | `@orkestrel/reason` | `0.0.10` | L2 | `@orkestrel/contract` `^0.0.17`, `@orkestrel/emitter` `^0.0.10` | |
79
- | `@orkestrel/relation` | `0.0.11` | L3 | `@orkestrel/contract` `^0.0.16`, `@orkestrel/database` `^0.0.13`, `@orkestrel/emitter` `^0.0.9` | |
79
+ | `@orkestrel/relation` | `0.0.12` | L3 | `@orkestrel/contract` `^0.0.17`, `@orkestrel/database` `^0.0.14`, `@orkestrel/emitter` `^0.0.10` | |
80
80
  | `@orkestrel/router` | `0.0.14` | L2 | `@orkestrel/abort` `^0.0.10`, `@orkestrel/emitter` `^0.0.10`, `@orkestrel/contract` `^0.0.17` | |
81
- | `@orkestrel/scaffold` | `0.0.63` | L3 | `@orkestrel/console` `^0.0.12`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/markdown` `^0.0.13`, `@orkestrel/process` `^0.0.10`, `@orkestrel/template` `^0.0.6` | |
82
- | `@orkestrel/sea` | `0.0.14` | L3 | `@orkestrel/contract` `^0.0.16`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/process` `^0.0.10` | |
83
- | `@orkestrel/server` | `0.0.18` | L3 | `@orkestrel/abort` `^0.0.9`, `@orkestrel/codec` `^0.0.2`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/router` `^0.0.13`, `@orkestrel/timeout` `^0.0.9` | |
81
+ | `@orkestrel/scaffold` | `0.0.65` | L3 | `@orkestrel/console` `^0.0.13`, `@orkestrel/contract` `^0.0.17`, `@orkestrel/emitter` `^0.0.10`, `@orkestrel/markdown` `^0.0.14`, `@orkestrel/process` `^0.0.11`, `@orkestrel/template` `^0.0.7` | |
82
+ | `@orkestrel/sea` | `0.0.15` | L3 | `@orkestrel/contract` `^0.0.17`, `@orkestrel/emitter` `^0.0.10`, `@orkestrel/process` `^0.0.11` | |
83
+ | `@orkestrel/server` | `0.0.19` | L3 | `@orkestrel/abort` `^0.0.10`, `@orkestrel/codec` `^0.0.3`, `@orkestrel/router` `^0.0.14`, `@orkestrel/emitter` `^0.0.10`, `@orkestrel/timeout` `^0.0.10`, `@orkestrel/contract` `^0.0.17` | |
84
84
  | `@orkestrel/sqlite` | `0.0.11` | L1 | `@orkestrel/contract` `^0.0.17` | |
85
85
  | `@orkestrel/sse` | `0.0.7` | L0 | | |
86
86
  | `@orkestrel/supervisor` | `0.0.1` | L5 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/database` `^0.0.9`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/workflow` `^0.0.12` | |
87
87
  | `@orkestrel/table` | `0.0.5` | L2 | `@orkestrel/contract` `^0.0.17`, `@orkestrel/emitter` `^0.0.10` | |
88
88
  | `@orkestrel/template` | `0.0.7` | L2 | `@orkestrel/emitter` `^0.0.10`, `@orkestrel/contract` `^0.0.17` | |
89
- | `@orkestrel/terminal` | `0.0.14` | L3 | `@orkestrel/console` `^0.0.12`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/database` `^0.0.13`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/form` `^0.0.5`, `@orkestrel/sse` `^0.0.6` | |
89
+ | `@orkestrel/terminal` | `0.0.15` | L3 | `@orkestrel/console` `^0.0.13`, `@orkestrel/contract` `^0.0.17`, `@orkestrel/database` `^0.0.14`, `@orkestrel/emitter` `^0.0.10`, `@orkestrel/form` `^0.0.6`, `@orkestrel/sse` `^0.0.7` | |
90
90
  | `@orkestrel/test` | `0.0.14` | L0 | | `vitest` `^4.1.11` |
91
91
  | `@orkestrel/timeout` | `0.0.10` | L1 | `@orkestrel/contract` `^0.0.17` | |
92
92
  | `@orkestrel/tool` | `0.0.14` | L1 | `@orkestrel/contract` `^0.0.17` | |
93
- | `@orkestrel/toolbox` | `0.0.12` | L6 | `@orkestrel/agent` `^0.0.20`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/database` `^0.0.13`, `@orkestrel/form` `^0.0.5`, `@orkestrel/relation` `^0.0.11`, `@orkestrel/server` `^0.0.18`, `@orkestrel/terminal` `^0.0.14`, `@orkestrel/tool` `^0.0.13`, `@orkestrel/workflow` `^0.0.17`, `@orkestrel/workspace` `^0.0.7` | |
93
+ | `@orkestrel/toolbox` | `0.0.13` | L6 | `@orkestrel/form` `^0.0.6`, `@orkestrel/tool` `^0.0.14`, `@orkestrel/agent` `^0.0.21`, `@orkestrel/server` `^0.0.19`, `@orkestrel/contract` `^0.0.17`, `@orkestrel/database` `^0.0.14`, `@orkestrel/relation` `^0.0.12`, `@orkestrel/terminal` `^0.0.15`, `@orkestrel/workflow` `^0.0.18`, `@orkestrel/workspace` `^0.0.8` | |
94
94
  | `@orkestrel/websocket` | `0.0.12` | L2 | `@orkestrel/emitter` `^0.0.10` | |
95
- | `@orkestrel/worker` | `0.0.11` | L4 | `@orkestrel/contract` `^0.0.16`, `@orkestrel/database` `^0.0.13`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/pool` `^0.0.10`, `@orkestrel/queue` `^0.0.12` | |
96
- | `@orkestrel/workflow` | `0.0.17` | L4 | `@orkestrel/abort` `^0.0.9`, `@orkestrel/budget` `^0.0.9`, `@orkestrel/contract` `^0.0.16`, `@orkestrel/database` `^0.0.13`, `@orkestrel/emitter` `^0.0.9`, `@orkestrel/queue` `^0.0.12`, `@orkestrel/timeout` `^0.0.9` | |
97
- | `@orkestrel/workspace` | `0.0.7` | L3 | `@orkestrel/contract` `^0.0.16`, `@orkestrel/database` `^0.0.13`, `@orkestrel/emitter` `^0.0.9` | |
95
+ | `@orkestrel/worker` | `0.0.12` | L4 | `@orkestrel/contract` `^0.0.17`, `@orkestrel/database` `^0.0.14`, `@orkestrel/emitter` `^0.0.10`, `@orkestrel/pool` `^0.0.11`, `@orkestrel/queue` `^0.0.13` | |
96
+ | `@orkestrel/workflow` | `0.0.18` | L4 | `@orkestrel/abort` `^0.0.10`, `@orkestrel/budget` `^0.0.10`, `@orkestrel/contract` `^0.0.17`, `@orkestrel/database` `^0.0.14`, `@orkestrel/emitter` `^0.0.10`, `@orkestrel/queue` `^0.0.13`, `@orkestrel/timeout` `^0.0.10` | |
97
+ | `@orkestrel/workspace` | `0.0.8` | L3 | `@orkestrel/contract` `^0.0.17`, `@orkestrel/database` `^0.0.14`, `@orkestrel/emitter` `^0.0.10` | |
98
98
 
99
99
  <!-- /orkestrel:catalog -->
100
100
 
@@ -166,8 +166,8 @@ the `test:bench` script joins no chain, so no gate runs either mode; the project
166
166
  ignored by git; and `.claude/rules/tests.md` governs what may live there.
167
167
 
168
168
  - Define a cross-cutting project only for a proof the package actually has.
169
- - A live-service project is the `service` project in the preceding table, `scripts/service.sh`
170
- provisions what it drives, and `.claude/rules/tests.md` governs it. Name it `service` whatever it
169
+ - Prepare the external service before invoking the `service` project. Use `tests/setupService.ts`
170
+ to verify readiness, apply `.claude/rules/tests.md`, and name the project `service` whatever it
171
171
  drives.
172
172
  - In a publishing workspace, a project leaves the default run when it drives a live external
173
173
  service or is hermetic but slow — it spawns processes, packs, installs, or drives a real build.