@orkestrel/scaffold 0.0.39 → 0.0.41

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 (37) hide show
  1. package/dist/bin/main.js +8 -1
  2. package/dist/bin/main.js.map +1 -1
  3. package/dist/host/AGENTS.md +2 -2
  4. package/dist/host/agents/orchestration.md +33 -2
  5. package/dist/host/agents/skills/enterprise-bootstrap/references/bootstrap-reference.md +16 -16
  6. package/dist/host/agents/skills/enterprise-bootstrap/references/components.md +3 -3
  7. package/dist/host/agents/skills/orkestrel-align-packages/references/integration.md +1 -1
  8. package/dist/host/agents/skills/orkestrel-build-application/SKILL.md +7 -2
  9. package/dist/host/claude/agents/builder.md +2 -2
  10. package/dist/host/claude/agents/orkestrel.md +48 -48
  11. package/dist/host/claude/rules/application.md +6 -4
  12. package/dist/host/claude/rules/architecture.md +2 -0
  13. package/dist/host/claude/rules/documentation.md +6 -0
  14. package/dist/host/claude/rules/tests.md +11 -1
  15. package/dist/host/claude/rules/typescript.md +15 -1
  16. package/dist/host/claude/rules/workspace.md +43 -13
  17. package/dist/host/claude/rules/writing.md +125 -0
  18. package/dist/host/claude/skills/enterprise-bootstrap/SKILL.md +10 -1
  19. package/dist/host/claude/skills/orkestrel-align-packages/SKILL.md +1 -1
  20. package/dist/host/claude/skills/orkestrel-build-application/SKILL.md +1 -1
  21. package/dist/host/claude/skills/orkestrel-harden-package/SKILL.md +1 -1
  22. package/dist/host/configs/policy.ts +185 -0
  23. package/dist/host/dotfiles/oxlintrc.json +13 -1
  24. package/dist/host/dotfiles/prettierignore +3 -0
  25. package/dist/host/guides/scaffold.md +13 -7
  26. package/dist/host/manifest.json +11 -7
  27. package/dist/host/tests/config.test.ts +195 -4
  28. package/dist/host/tests/policy.test.ts +82 -0
  29. package/dist/host/tests/setupPolicy.ts +863 -21
  30. package/dist/src/core/index.cjs +68 -6
  31. package/dist/src/core/index.cjs.map +1 -1
  32. package/dist/src/core/index.d.cts +11 -9
  33. package/dist/src/core/index.d.ts +11 -9
  34. package/dist/src/core/index.js +68 -6
  35. package/dist/src/core/index.js.map +1 -1
  36. package/package.json +2 -2
  37. package/dist/host/agents/skills/orkestrel-build-application/references/application.md +0 -129
@@ -2,7 +2,7 @@
2
2
 
3
3
  > Part of the `enterprise-bootstrap` package. Bootstrap **5.3.x**.
4
4
  > Component markup lookups: [components.md](components.md). Utility classes: [utilities.md](utilities.md).
5
- > This file holds what those don't: setup, color modes, theming/tokens, forms in
5
+ > This file holds what those do not: setup, color modes, theming/tokens, forms in
6
6
  > production, the JS lifecycle, accessibility depth, and enterprise app patterns.
7
7
 
8
8
  ## Contents
@@ -99,7 +99,7 @@ The 5.3 color-mode system replaces the old per-component dark variants.
99
99
 
100
100
  ### Theme toggle
101
101
 
102
- Bootstrap ships **no** mode picker — you build the toggle. The essentials: read the stored preference, fall back to `prefers-color-scheme`, set `data-bs-theme` on `document.documentElement`, and do it in a script early in `<head>` so the first paint doesn't flash the wrong mode.
102
+ Bootstrap ships **no** mode picker — you build the toggle. The essentials: read the stored preference, fall back to `prefers-color-scheme`, set `data-bs-theme` on `document.documentElement`, and do it in a script early in `<head>` so the first paint does not flash the wrong mode.
103
103
 
104
104
  ```js
105
105
  const stored = localStorage.getItem('theme')
@@ -211,7 +211,7 @@ Remove with `map-remove($utilities, "width")` or set the key to `null`. This is
211
211
 
212
212
  - **Top-aligned labels by default** — the evidence (eye-tracking form research) shows fastest completion and the cleanest single-column scan, and they survive narrow screens without reflow. Reserve left-aligned labels for dense read-back forms where vertical compression matters more than speed.
213
213
  - Visible label or `.form-floating` — never placeholder-only (disappears on input, fails accessibility).
214
- - **Don't say the same thing twice.** When the host already names the request — a card heading, a dialog title, a section header stating the question — the form associates with that name via `aria-labelledby` instead of repeating the prompt in its own label. Repetition reads as two different questions to a screen-reader user and as clutter to everyone else.
214
+ - **Do not say the same thing twice.** When the host already names the request — a card heading, a dialog title, a section header stating the question — the form associates with that name via `aria-labelledby` instead of repeating the prompt in its own label. Repetition reads as two different questions to a screen-reader user and as clutter to everyone else.
215
215
  - One column beats multi-column for completion; use the form grid (`row g-3` + `col-md-*`) only for genuinely paired fields (city/state/zip).
216
216
 
217
217
  ```html
@@ -235,9 +235,9 @@ Remove with `map-remove($utilities, "width")` or set the key to `null`. This is
235
235
 
236
236
  - Validate a field **on blur** — after the user leaves it — never on every keystroke, and never before the user has reached the field. Exception: live feedback that _helps_ while typing (password strength, username availability, character counts).
237
237
  - Once a field is in an error state, re-validate as the user types so they see the fix land.
238
- - Always re-check everything on submit. Keep the submit button **enabled** — a disabled submit hides _what's_ wrong; a validating submit shows it.
238
+ - Always re-check everything on submit. Keep the submit button **enabled** — a disabled submit hides _what is_ wrong; a validating submit shows it.
239
239
  - On failed submit of a long form, render an **error summary** at the top (focus it; link each item to its field) _and_ inline messages at each field — never summary-only, never inline-only.
240
- - Error style = color + icon + text, stating what's wrong and how to fix it. Wire message to field with `aria-describedby`, mark the field `aria-invalid="true"`. Never report errors via tooltip-on-hover.
240
+ - Error style = color + icon + text, stating what is wrong and how to fix it. Wire message to field with `aria-describedby`, mark the field `aria-invalid="true"`. Never report errors via tooltip-on-hover.
241
241
 
242
242
  ### Bootstrap validation mechanics
243
243
 
@@ -329,7 +329,7 @@ el.addEventListener('hidden.bs.modal', () => {
329
329
 
330
330
  ### Popper
331
331
 
332
- Dropdowns, tooltips, and popovers require Popper — load `bootstrap.bundle.min.js` (includes it) or `@popperjs/core` before `bootstrap.min.js`. Modal, collapse, offcanvas, toast, tab, alert don't need it.
332
+ Dropdowns, tooltips, and popovers require Popper — load `bootstrap.bundle.min.js` (includes it) or `@popperjs/core` before `bootstrap.min.js`. Modal, collapse, offcanvas, toast, tab, alert do not need it.
333
333
 
334
334
  ## Accessibility
335
335
 
@@ -364,15 +364,15 @@ Wire the reader into the suite once it has settled a question.
364
364
  - **Focus not obscured (2.4.11, AA).** Sticky headers/footers/action bars and toast overlays must not bury the focused element. Reserve space with `scroll-margin-top` on focusables (or `scroll-padding-top` on the scroll container) equal to the sticky chrome height.
365
365
  - **Dragging alternatives (2.5.7, AA).** Any drag (row reorder, kanban, slider, resize) needs a non-drag single-pointer path: move up/down buttons, numeric input, click-to-place.
366
366
  - **Accessible authentication (3.3.8, AA).** Never block paste in password/OTP fields; support password managers; no puzzle as the only way in.
367
- - **Redundant entry (3.3.7, A).** Don't ask for the same information twice in one flow — auto-fill or offer "same as above". Governs wizards directly.
367
+ - **Redundant entry (3.3.7, A).** Do not ask for the same information twice in one flow — auto-fill or offer "same as above". Governs wizards directly.
368
368
  - **Consistent help (3.2.6, A).** If a help affordance repeats across pages, keep it in the same relative place everywhere.
369
369
  - Housekeeping: SC 4.1.1 Parsing was removed in 2.2 — duplicate-ID lint is no longer a WCAG failure by itself (still fix it).
370
370
 
371
371
  ### Pattern contracts (APG, compact)
372
372
 
373
- - **Dialog/modal:** `role="dialog"` (`alertdialog` for destructive confirms) + `aria-modal="true"` + `aria-labelledby`. Focus moves in on open, Tab is trapped, Esc closes, focus returns to the invoker. Bootstrap's modal does this — verify you didn't break focus-return by removing the trigger.
373
+ - **Dialog/modal:** `role="dialog"` (`alertdialog` for destructive confirms) + `aria-modal="true"` + `aria-labelledby`. Focus moves in on open, Tab is trapped, Esc closes, focus returns to the invoker. Bootstrap's modal does this — verify you did not break focus-return by removing the trigger.
374
374
  - **Tabs:** `tablist` > `tab` (+`aria-selected`, `aria-controls`) with panels `tabpanel`. Roving tabindex: Left/Right between tabs, Home/End to ends; only the selected tab is `tabindex="0"`.
375
- - **Disclosure:** a `<button>` with `aria-expanded` + `aria-controls`. Enter/Space toggles. That's the whole contract — Bootstrap collapse matches it.
375
+ - **Disclosure:** a `<button>` with `aria-expanded` + `aria-controls`. Enter/Space toggles. That is the whole contract — Bootstrap collapse matches it.
376
376
  - **Radio group:** prefer native `<input type="radio" name>` — one tab stop and arrow-selection come free.
377
377
  - **Combobox:** input `role="combobox"` + `aria-expanded` + `aria-controls` + `aria-activedescendant` tracking the active option; Down opens/advances, Enter accepts, Esc closes. This is the hardest contract on the list — see [When not to hand-roll](#when-not-to-hand-roll).
378
378
  - **Toolbar:** `role="toolbar"` + `aria-label`; one tab stop, arrows move between controls (roving tabindex). Use it to collapse a dense button cluster's tab-stops.
@@ -389,7 +389,7 @@ Browsers handle focus on full page loads; in an SPA **you** do:
389
389
 
390
390
  ### Reduced motion
391
391
 
392
- Bootstrap wraps its transitions and animations (`.fade`, `.collapsing`, carousel slide, spinner speed) in `prefers-reduced-motion: reduce` handling — transitions are disabled or slowed automatically when `$enable-reduced-motion` is on (default). Your obligations: wrap **custom** animation in `@media (prefers-reduced-motion: no-preference)`, don't auto-play movement for reduced-motion users, and keep any purely decorative motion cuttable.
392
+ Bootstrap wraps its transitions and animations (`.fade`, `.collapsing`, carousel slide, spinner speed) in `prefers-reduced-motion: reduce` handling — transitions are disabled or slowed automatically when `$enable-reduced-motion` is on (default). Your obligations: wrap **custom** animation in `@media (prefers-reduced-motion: no-preference)`, do not auto-play movement for reduced-motion users, and keep any purely decorative motion cuttable.
393
393
 
394
394
  ## Enterprise Patterns
395
395
 
@@ -457,7 +457,7 @@ The Bootstrap implementation — a responsive offcanvas that renders inline abov
457
457
 
458
458
  **Navigation rules:**
459
459
 
460
- - Breadcrumbs only for genuinely hierarchical models (org → account → contact); in flat or tabbed apps they're noise. Current item: `aria-current="page"`, not a link.
460
+ - Breadcrumbs only for genuinely hierarchical models (org → account → contact); in flat or tabbed apps they are noise. Current item: `aria-current="page"`, not a link.
461
461
  - A command palette (Ctrl/Cmd-K) is an accelerator **on top of** visible nav, never a replacement — everything it exposes needs a discoverable UI route too.
462
462
  - Keyboard shortcuts: use conventional bindings (Ctrl/Cmd-K palette, `/` focuses search, `?` opens the shortcut cheat-sheet), surface them in tooltips, and never let single-key shortcuts fire while an input has focus.
463
463
  - Dashboard composition follows the same craft as any screen: state the screen's single job, lead with the numbers that answer it, and keep every widget to one job — a dashboard is not a place to exhibit every chart type.
@@ -469,7 +469,7 @@ The Bootstrap implementation — a responsive offcanvas that renders inline abov
469
469
  **Craft rules:**
470
470
 
471
471
  - **Align by type:** numbers, currency, dates right-aligned (`text-end`, header too); text left. Use tabular figures so digits stack into comparable columns: `font-variant-numeric: tabular-nums` on numeric cells (one small custom rule that earns its place).
472
- - **Density:** `table-sm` for compact; offer density as a user toggle (comfortable/compact) driven by one token or wrapper class, not per-cell tweaks. Don't shrink font below readability to fake density.
472
+ - **Density:** `table-sm` for compact; offer density as a user toggle (comfortable/compact) driven by one token or wrapper class, not per-cell tweaks. Do not shrink font below readability to fake density.
473
473
  - **Sticky header** once the table meaningfully scrolls (roughly a viewport / ~15+ rows). Not built into Bootstrap — the pattern:
474
474
 
475
475
  ```html
@@ -508,7 +508,7 @@ Give header cells an **opaque background** (`bg-body-secondary` or a table varia
508
508
  - One toolbar above the table: search input first (`role="search"` on the form), then the 2–4 highest-value filters as `form-select`/segmented controls, overflow filters behind a "Filters" button (offcanvas on mobile, dropdown/collapse on desktop).
509
509
  - **Active filters must be visible and dismissible** — chips/badges with an ✕ and a "Clear all" — users must see _why_ the list is short. A filtered-empty state repeats the escape hatch.
510
510
  - Debounce live search; show result counts ("128 results") so feedback is immediate; filter state belongs in the URL when views are shareable.
511
- - Toolbars that overflow: `flex-nowrap overflow-auto` beats wrapping into a two-row toolbar mid-task — don't crush icon targets below 24px.
511
+ - Toolbars that overflow: `flex-nowrap overflow-auto` beats wrapping into a two-row toolbar mid-task — do not crush icon targets below 24px.
512
512
 
513
513
  ### Wizards & multi-step forms
514
514
 
@@ -520,10 +520,10 @@ Give header cells an **opaque background** (`bg-body-secondary` or a table varia
520
520
 
521
521
  ### The five states
522
522
 
523
- Design **all five** for every data surface: ideal (populated), empty, loading, partial, error. A component isn't done until all five exist.
523
+ Design **all five** for every data surface: ideal (populated), empty, loading, partial, error. A component is not done until all five exist.
524
524
 
525
525
  - **Skeleton vs spinner:** skeleton (`placeholder` + `placeholder-glow`) when you know the content's shape and it fills a region — tables, cards, detail panes — because it holds layout and shortens perceived wait. Spinner for short, indeterminate, or in-control waits (inside a button, a small inline fetch).
526
- - **Thresholds (guidance):** under ~1s show nothing — a flashed loader is worse than none; ~1–10s show a spinner or skeleton; beyond ~10s show determinate progress (percent or step) so it doesn't feel hung.
526
+ - **Thresholds (guidance):** under ~1s show nothing — a flashed loader is worse than none; ~1–10s show a spinner or skeleton; beyond ~10s show determinate progress (percent or step) so it does not feel hung.
527
527
  - **Optimistic vs pessimistic:** apply UI immediately and reconcile (rolling back loudly on failure) for reversible high-frequency actions — toggles, stars, reorders. Await confirmation for money, audited records, and anything a rollback would confuse.
528
528
  - **Empty states** invite the next action (button + one line of why). Never-had-data and filtered-empty are different states with different escapes — never ship one generic "nothing here".
529
529
  - **Every error state states what failed and how to fix it**, carries a keyboard-reachable retry in place, and preserves surrounding context — a body fetch failure must not blow away the toolbar and filters.
@@ -547,7 +547,7 @@ Match friction to reversibility × blast radius:
547
547
  2. **Confirm dialog** for irreversible-but-scoped operations. Restate the specific consequence ("This permanently deletes 3 invoices"), verb-labeled buttons ("Delete invoices" / "Cancel" — never Yes/No), destructive action visually separated from safe; `alertdialog` semantics; focus lands on the safe action.
548
548
  3. **Type-to-confirm** (type the entity name) only for high-blast-radius irreversible operations — delete an org, drop a dataset.
549
549
 
550
- Don't type-gate a single-row delete; don't one-tap a tenant wipe. Confirm only where this ladder calls for it — a confirmation on every action gets clicked through.
550
+ Do not type-gate a single-row delete; do not one-tap a tenant wipe. Confirm only where this ladder calls for it — a confirmation on every action gets clicked through.
551
551
 
552
552
  **Neutralize a disabled destructive control.** `btn-danger` at full saturation reads as armed whatever the `disabled` attribute says, and the contrast exemption for disabled controls does not excuse it. While the action is unavailable, drop to the neutral or outline variant (or let the disabled state mute the fill) so the color stops promising an action, and say _why_ it is unavailable in text the assistive layer reaches: `aria-describedby` pointing at the reason, with `title` only as the pointer-user convenience on top. Never use `title` alone — it never reaches a keyboard or screen-reader user, and it disappears on touch.
553
553
 
@@ -400,7 +400,7 @@ Multiple targets: give each panel `.multi-collapse` and point separate triggers
400
400
  </ul>
401
401
  ```
402
402
 
403
- (The `list-group-checkable` / `list-group-item-check` classes seen in Bootstrap's _examples gallery_ are custom CSS, not core — don't ship them without their styles.)
403
+ (The `list-group-checkable` / `list-group-item-check` classes seen in Bootstrap's _examples gallery_ are custom CSS, not core — do not ship them without their styles.)
404
404
 
405
405
  ### Modal
406
406
 
@@ -625,7 +625,7 @@ Real switchable tab panels (JS-driven — buttons, not scroll anchors):
625
625
  </div>
626
626
  ```
627
627
 
628
- **Responsive offcanvas** — the canonical sidebar-that-becomes-a-drawer: replace `.offcanvas` with `.offcanvas-{sm|md|lg|xl|xxl}`. Content renders **inline above** that breakpoint and as an **offcanvas below** it. Close buttons inside a responsive offcanvas need an explicit `data-bs-target`. Always set `aria-labelledby` (it's conceptually a dialog; `role="dialog"` is added by JS). Width/height via `--bs-offcanvas-width` (400px) / `--bs-offcanvas-height` (30vh). Full app-shell pattern: [bootstrap-reference.md](bootstrap-reference.md) → App shell.
628
+ **Responsive offcanvas** — the canonical sidebar-that-becomes-a-drawer: replace `.offcanvas` with `.offcanvas-{sm|md|lg|xl|xxl}`. Content renders **inline above** that breakpoint and as an **offcanvas below** it. Close buttons inside a responsive offcanvas need an explicit `data-bs-target`. Always set `aria-labelledby` (it is conceptually a dialog; `role="dialog"` is added by JS). Width/height via `--bs-offcanvas-width` (400px) / `--bs-offcanvas-height` (30vh). Full app-shell pattern: [bootstrap-reference.md](bootstrap-reference.md) → App shell.
629
629
 
630
630
  ### Pagination
631
631
 
@@ -1012,7 +1012,7 @@ Exactly one item in a selection carries `aria-current` — the visual fill and t
1012
1012
  ### Navigation & overlays
1013
1013
 
1014
1014
  - Active nav items need `aria-current="page"` (or `aria-selected="true"` for tabs).
1015
- - Modals and offcanvas: set `aria-labelledby`; Bootstrap traps focus and restores it on close — don't fight it; `dispose()` instances when the host unmounts in SPAs.
1015
+ - Modals and offcanvas: set `aria-labelledby`; Bootstrap traps focus and restores it on close — do not fight it; `dispose()` instances when the host unmounts in SPAs.
1016
1016
  - Icon-only controls always need an accessible name (`aria-label` or visually-hidden text) and a ≥24px target.
1017
1017
 
1018
1018
  ### Theming
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Map ownership
4
4
 
5
- Dependency direction across environments is the root project model in `AGENTS.md`, detailed for placement in `.claude/rules/workspace.md` and for app composition in `.claude/rules/application.md`. Read those; this reference does not restate them. Apply the same law to a dependency's `@orkestrel/<package>/browser` and `/server` exports, whose bare export is its core API.
5
+ Dependency direction across environments is the root project model in `AGENTS.md`, detailed for placement in `.claude/rules/workspace.md` and for app composition in `.claude/rules/application.md`. Read those; this reference does not restate them. Apply the same law to a dependency's `@orkestrel/<package>/browser` and `/server` exports.
6
6
 
7
7
  Ownership across packages is what those rules leave open: framework packages own reusable mechanisms, and applications own workflows, policy, presentation, users, authorization decisions, and product-specific defaults.
8
8
 
@@ -69,11 +69,16 @@ includes app/core so the shared transport contracts have one host-independent ow
69
69
  template-literal specifiers, declaration placement, and the barrel law.
70
70
  - Scoped TypeScript projects — host-global isolation.
71
71
  - Vite's real builds and environment-boundary plugin — Vue, CSS, assets, workers,
72
- runtime resolution, and physical workspace containment.
72
+ runtime resolution, and physical workspace containment. Its parsed HTML asset
73
+ callbacks reject forced inlining before any direct asset read, and non-inlined
74
+ output assets are audited by physical source path.
73
75
 
74
76
  Then hold these four limits:
75
77
  - Add no standalone boundary script, and no second parser or source-language analyzer
76
- duplicating those layers.
78
+ duplicating those layers. Reach for the toolchain's own facilities instead: Vite's
79
+ Oxc/Rolldown AST for TypeScript and JavaScript asset references, the official Vue SFC
80
+ compiler for `.vue` blocks, Vite's HTML parser callbacks, its bundled Lightning CSS
81
+ dependency analyzer, and the TypeScript compiler API for the narrow coding-law pass.
77
82
  - Disable the browser application's public directory, so an unmanaged file copy cannot
78
83
  bypass the module graph.
79
84
  - Keep browser-only runtime tooling development-only.
@@ -29,10 +29,10 @@ You are an Executor: do the work yourself, spawn nothing.
29
29
  - No mocks, behavioral fakes, superfluous wrappers, or current-scope
30
30
  TODOs/skips/deferrals.
31
31
 
32
- ## Deviation protocol — stop, don't solve
32
+ ## Deviation protocol — stop, do not solve
33
33
 
34
34
  The moment reality diverges from the dispatch — an unexpected error, a file that
35
- isn't what the plan says, a failing assumption, a scope surprise — STOP that line of
35
+ is not what the plan says, a failing assumption, a scope surprise — STOP that line of
36
36
  work and return a **deviation report**:
37
37
 
38
38
  - **Expected** — what the dispatch said.
@@ -34,54 +34,54 @@ so network-controlled descriptions never enter agent instruction context.
34
34
 
35
35
  <!-- orkestrel:catalog -->
36
36
 
37
- | Package | Version | Layer | Runtime dependencies |
38
- | ----------------------- | -------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
39
- | `@orkestrel/abort` | `0.0.7` | L1 | `@orkestrel/contract` `^0.0.12` |
40
- | `@orkestrel/agent` | `0.0.15` | L5 | `@orkestrel/abort` `^0.0.6`, `@orkestrel/budget` `^0.0.6`, `@orkestrel/contract` `^0.0.11`, `@orkestrel/database` `^0.0.8`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/queue` `^0.0.8`, `@orkestrel/timeout` `^0.0.6`, `@orkestrel/tool` `^0.0.10`, `@orkestrel/workflow` `^0.0.11`, `@orkestrel/workspace` `^0.0.4` |
41
- | `@orkestrel/brief` | `0.0.2` | L4 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/interpret` `^0.0.9`, `@orkestrel/reason` `^0.0.6` |
42
- | `@orkestrel/browser` | `0.0.9` | L3 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/html` `^0.0.3`, `@orkestrel/websocket` `^0.0.8` |
43
- | `@orkestrel/budget` | `0.0.7` | L1 | `@orkestrel/contract` `^0.0.12` |
44
- | `@orkestrel/console` | `0.0.8` | L2 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7` |
45
- | `@orkestrel/contract` | `0.0.12` | L0 | |
46
- | `@orkestrel/csv` | `0.0.4` | L1 | `@orkestrel/contract` `^0.0.12` |
47
- | `@orkestrel/database` | `0.0.10` | L2 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7`, `@orkestrel/indexeddb` `^0.0.8`, `@orkestrel/sqlite` `^0.0.8` |
48
- | `@orkestrel/emitter` | `0.0.7` | L1 | `@orkestrel/contract` `^0.0.12` |
49
- | `@orkestrel/form` | `0.0.2` | L2 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7` |
50
- | `@orkestrel/guide` | `0.0.11` | L3 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/markdown` `^0.0.8` |
51
- | `@orkestrel/html` | `0.0.4` | L1 | `@orkestrel/contract` `^0.0.12` |
52
- | `@orkestrel/indexeddb` | `0.0.8` | L1 | `@orkestrel/contract` `^0.0.12` |
53
- | `@orkestrel/interpret` | `0.0.9` | L3 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/reason` `^0.0.6`, `@orkestrel/template` `^0.0.3` |
54
- | `@orkestrel/markdown` | `0.0.9` | L2 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/html` `^0.0.4` |
55
- | `@orkestrel/mcp` | `0.0.15` | L3 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/sse` `^0.0.5`, `@orkestrel/tool` `^0.0.10`, `@orkestrel/websocket` `^0.0.8` |
56
- | `@orkestrel/middleware` | `0.0.13` | L2 | `@orkestrel/abort` `^0.0.7`, `@orkestrel/budget` `^0.0.7`, `@orkestrel/contract` `^0.0.12`, `@orkestrel/timeout` `^0.0.7` |
57
- | `@orkestrel/msg` | `0.0.7` | L0 | |
58
- | `@orkestrel/ndjson` | `0.0.7` | L1 | `@orkestrel/contract` `^0.0.12` |
59
- | `@orkestrel/ollama` | `0.0.9` | L6 | `@orkestrel/agent` `^0.0.15`, `@orkestrel/budget` `^0.0.6`, `@orkestrel/contract` `^0.0.11`, `@orkestrel/ndjson` `^0.0.6`, `@orkestrel/timeout` `^0.0.6`, `@orkestrel/tool` `^0.0.10` |
60
- | `@orkestrel/pool` | `0.0.8` | L2 | `@orkestrel/emitter` `^0.0.7` |
61
- | `@orkestrel/program` | `0.0.8` | L4 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/qualifier` `^0.0.9`, `@orkestrel/rater` `^0.0.10`, `@orkestrel/reason` `^0.0.6` |
62
- | `@orkestrel/qualifier` | `0.0.9` | L3 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/reason` `^0.0.6` |
63
- | `@orkestrel/queue` | `0.0.8` | L3 | `@orkestrel/abort` `^0.0.6`, `@orkestrel/contract` `^0.0.11`, `@orkestrel/database` `^0.0.8`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/timeout` `^0.0.6` |
64
- | `@orkestrel/rater` | `0.0.10` | L3 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/reason` `^0.0.6` |
65
- | `@orkestrel/reason` | `0.0.7` | L2 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7` |
66
- | `@orkestrel/relation` | `0.0.8` | L3 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/database` `^0.0.8`, `@orkestrel/emitter` `^0.0.6` |
67
- | `@orkestrel/router` | `0.0.10` | L2 | `@orkestrel/abort` `^0.0.7`, `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7` |
68
- | `@orkestrel/scaffold` | `0.0.38` | L3 | `@orkestrel/console` `^0.0.6`, `@orkestrel/contract` `^0.0.11`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/markdown` `^0.0.8`, `@orkestrel/template` `^0.0.3` |
69
- | `@orkestrel/sea` | `0.0.7` | L2 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7` |
70
- | `@orkestrel/server` | `0.0.12` | L3 | `@orkestrel/abort` `^0.0.6`, `@orkestrel/contract` `^0.0.11`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/router` `^0.0.9`, `@orkestrel/timeout` `^0.0.6` |
71
- | `@orkestrel/sqlite` | `0.0.8` | L1 | `@orkestrel/contract` `^0.0.12` |
72
- | `@orkestrel/sse` | `0.0.5` | L0 | |
73
- | `@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` |
74
- | `@orkestrel/table` | `0.0.2` | L2 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7` |
75
- | `@orkestrel/template` | `0.0.4` | L2 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7` |
76
- | `@orkestrel/terminal` | `0.0.9` | L3 | `@orkestrel/console` `^0.0.7`, `@orkestrel/contract` `^0.0.11`, `@orkestrel/database` `^0.0.9`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/form` `^0.0.1`, `@orkestrel/sse` `^0.0.5` |
77
- | `@orkestrel/test` | `0.0.6` | L0 | |
78
- | `@orkestrel/timeout` | `0.0.7` | L1 | `@orkestrel/contract` `^0.0.12` |
79
- | `@orkestrel/tool` | `0.0.11` | L1 | `@orkestrel/contract` `^0.0.12` |
80
- | `@orkestrel/toolbox` | `0.0.6` | L6 | `@orkestrel/agent` `^0.0.15`, `@orkestrel/contract` `^0.0.11`, `@orkestrel/database` `^0.0.8`, `@orkestrel/relation` `^0.0.8`, `@orkestrel/server` `^0.0.12`, `@orkestrel/terminal` `^0.0.7`, `@orkestrel/tool` `^0.0.10`, `@orkestrel/workflow` `^0.0.11`, `@orkestrel/workspace` `^0.0.4` |
81
- | `@orkestrel/websocket` | `0.0.9` | L2 | `@orkestrel/emitter` `^0.0.7` |
82
- | `@orkestrel/worker` | `0.0.7` | L4 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/database` `^0.0.8`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/pool` `^0.0.7`, `@orkestrel/queue` `^0.0.8` |
83
- | `@orkestrel/workflow` | `0.0.12` | L4 | `@orkestrel/abort` `^0.0.6`, `@orkestrel/budget` `^0.0.6`, `@orkestrel/contract` `^0.0.11`, `@orkestrel/database` `^0.0.8`, `@orkestrel/emitter` `^0.0.6`, `@orkestrel/queue` `^0.0.8`, `@orkestrel/timeout` `^0.0.6` |
84
- | `@orkestrel/workspace` | `0.0.4` | L3 | `@orkestrel/contract` `^0.0.11`, `@orkestrel/database` `^0.0.8`, `@orkestrel/emitter` `^0.0.6` |
37
+ | Package | Version | Layer | Runtime dependencies |
38
+ | ----------------------- | -------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
39
+ | `@orkestrel/abort` | `0.0.7` | L1 | `@orkestrel/contract` `^0.0.12` |
40
+ | `@orkestrel/agent` | `0.0.16` | L5 | `@orkestrel/abort` `^0.0.7`, `@orkestrel/budget` `^0.0.7`, `@orkestrel/contract` `^0.0.12`, `@orkestrel/database` `^0.0.10`, `@orkestrel/emitter` `^0.0.7`, `@orkestrel/queue` `^0.0.9`, `@orkestrel/timeout` `^0.0.7`, `@orkestrel/tool` `^0.0.11`, `@orkestrel/workflow` `^0.0.13`, `@orkestrel/workspace` `^0.0.5` |
41
+ | `@orkestrel/brief` | `0.0.3` | L4 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7`, `@orkestrel/interpret` `^0.0.10`, `@orkestrel/reason` `^0.0.7` |
42
+ | `@orkestrel/browser` | `0.0.10` | L3 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7`, `@orkestrel/html` `^0.0.4`, `@orkestrel/websocket` `^0.0.9` |
43
+ | `@orkestrel/budget` | `0.0.7` | L1 | `@orkestrel/contract` `^0.0.12` |
44
+ | `@orkestrel/console` | `0.0.8` | L2 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7` |
45
+ | `@orkestrel/contract` | `0.0.12` | L0 | |
46
+ | `@orkestrel/csv` | `0.0.4` | L1 | `@orkestrel/contract` `^0.0.12` |
47
+ | `@orkestrel/database` | `0.0.10` | L2 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7`, `@orkestrel/indexeddb` `^0.0.8`, `@orkestrel/sqlite` `^0.0.8` |
48
+ | `@orkestrel/emitter` | `0.0.7` | L1 | `@orkestrel/contract` `^0.0.12` |
49
+ | `@orkestrel/form` | `0.0.2` | L2 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7` |
50
+ | `@orkestrel/guide` | `0.0.12` | L3 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/markdown` `^0.0.9` |
51
+ | `@orkestrel/html` | `0.0.4` | L1 | `@orkestrel/contract` `^0.0.12` |
52
+ | `@orkestrel/indexeddb` | `0.0.8` | L1 | `@orkestrel/contract` `^0.0.12` |
53
+ | `@orkestrel/interpret` | `0.0.10` | L3 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7`, `@orkestrel/reason` `^0.0.7`, `@orkestrel/template` `^0.0.4` |
54
+ | `@orkestrel/markdown` | `0.0.9` | L2 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/html` `^0.0.4` |
55
+ | `@orkestrel/mcp` | `0.0.17` | L3 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7`, `@orkestrel/sse` `^0.0.5`, `@orkestrel/tool` `^0.0.11`, `@orkestrel/websocket` `^0.0.9` |
56
+ | `@orkestrel/middleware` | `0.0.14` | L2 | `@orkestrel/abort` `^0.0.7`, `@orkestrel/budget` `^0.0.7`, `@orkestrel/contract` `^0.0.12`, `@orkestrel/timeout` `^0.0.7` |
57
+ | `@orkestrel/msg` | `0.0.7` | L0 | |
58
+ | `@orkestrel/ndjson` | `0.0.7` | L1 | `@orkestrel/contract` `^0.0.12` |
59
+ | `@orkestrel/ollama` | `0.0.10` | L6 | `@orkestrel/agent` `^0.0.16`, `@orkestrel/budget` `^0.0.7`, `@orkestrel/contract` `^0.0.12`, `@orkestrel/ndjson` `^0.0.7`, `@orkestrel/timeout` `^0.0.7`, `@orkestrel/tool` `^0.0.11` |
60
+ | `@orkestrel/pool` | `0.0.8` | L2 | `@orkestrel/emitter` `^0.0.7` |
61
+ | `@orkestrel/program` | `0.0.9` | L4 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7`, `@orkestrel/qualifier` `^0.0.10`, `@orkestrel/rater` `^0.0.11`, `@orkestrel/reason` `^0.0.7` |
62
+ | `@orkestrel/qualifier` | `0.0.10` | L3 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7`, `@orkestrel/reason` `^0.0.7` |
63
+ | `@orkestrel/queue` | `0.0.9` | L3 | `@orkestrel/abort` `^0.0.7`, `@orkestrel/contract` `^0.0.12`, `@orkestrel/database` `^0.0.10`, `@orkestrel/emitter` `^0.0.7`, `@orkestrel/timeout` `^0.0.7` |
64
+ | `@orkestrel/rater` | `0.0.11` | L3 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7`, `@orkestrel/reason` `^0.0.7` |
65
+ | `@orkestrel/reason` | `0.0.7` | L2 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7` |
66
+ | `@orkestrel/relation` | `0.0.9` | L3 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/database` `^0.0.10`, `@orkestrel/emitter` `^0.0.7` |
67
+ | `@orkestrel/router` | `0.0.10` | L2 | `@orkestrel/abort` `^0.0.7`, `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7` |
68
+ | `@orkestrel/scaffold` | `0.0.40` | L3 | `@orkestrel/console` `^0.0.8`, `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7`, `@orkestrel/markdown` `^0.0.9`, `@orkestrel/template` `^0.0.4` |
69
+ | `@orkestrel/sea` | `0.0.7` | L2 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7` |
70
+ | `@orkestrel/server` | `0.0.13` | L3 | `@orkestrel/abort` `^0.0.7`, `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7`, `@orkestrel/router` `^0.0.10`, `@orkestrel/timeout` `^0.0.7` |
71
+ | `@orkestrel/sqlite` | `0.0.8` | L1 | `@orkestrel/contract` `^0.0.12` |
72
+ | `@orkestrel/sse` | `0.0.5` | L0 | |
73
+ | `@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` |
74
+ | `@orkestrel/table` | `0.0.2` | L2 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7` |
75
+ | `@orkestrel/template` | `0.0.4` | L2 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/emitter` `^0.0.7` |
76
+ | `@orkestrel/terminal` | `0.0.10` | L3 | `@orkestrel/console` `^0.0.8`, `@orkestrel/contract` `^0.0.12`, `@orkestrel/database` `^0.0.10`, `@orkestrel/emitter` `^0.0.7`, `@orkestrel/form` `^0.0.2`, `@orkestrel/sse` `^0.0.5` |
77
+ | `@orkestrel/test` | `0.0.6` | L0 | |
78
+ | `@orkestrel/timeout` | `0.0.7` | L1 | `@orkestrel/contract` `^0.0.12` |
79
+ | `@orkestrel/tool` | `0.0.11` | L1 | `@orkestrel/contract` `^0.0.12` |
80
+ | `@orkestrel/toolbox` | `0.0.7` | L6 | `@orkestrel/agent` `^0.0.16`, `@orkestrel/contract` `^0.0.12`, `@orkestrel/database` `^0.0.10`, `@orkestrel/form` `^0.0.2`, `@orkestrel/relation` `^0.0.9`, `@orkestrel/server` `^0.0.13`, `@orkestrel/terminal` `^0.0.10`, `@orkestrel/tool` `^0.0.11`, `@orkestrel/workflow` `^0.0.13`, `@orkestrel/workspace` `^0.0.5` |
81
+ | `@orkestrel/websocket` | `0.0.9` | L2 | `@orkestrel/emitter` `^0.0.7` |
82
+ | `@orkestrel/worker` | `0.0.8` | L4 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/database` `^0.0.10`, `@orkestrel/emitter` `^0.0.7`, `@orkestrel/pool` `^0.0.8`, `@orkestrel/queue` `^0.0.9` |
83
+ | `@orkestrel/workflow` | `0.0.13` | L4 | `@orkestrel/abort` `^0.0.7`, `@orkestrel/budget` `^0.0.7`, `@orkestrel/contract` `^0.0.12`, `@orkestrel/database` `^0.0.10`, `@orkestrel/emitter` `^0.0.7`, `@orkestrel/queue` `^0.0.9`, `@orkestrel/timeout` `^0.0.7` |
84
+ | `@orkestrel/workspace` | `0.0.5` | L3 | `@orkestrel/contract` `^0.0.12`, `@orkestrel/database` `^0.0.10`, `@orkestrel/emitter` `^0.0.7` |
85
85
 
86
86
  <!-- /orkestrel:catalog -->
87
87
 
@@ -41,10 +41,12 @@ paths:
41
41
  - Published `src` environments never import private `app` modules. Src core is
42
42
  host-independent; src browser/server may import src core but never one
43
43
  another's implementation. Apply the same environment law to
44
- `@orkestrel/<package>/browser` and `/server` exports.
45
- - App-only manifests are unscoped and `private: true`, with no package export
46
- map or publish configuration. Mixed manifests publish only `dist/src`, and
47
- Vue remains development-only because app output is never published.
44
+ `@orkestrel/<package>/browser` and `/server` exports; a package's bare export
45
+ is its core API.
46
+ - App-only manifests are unscoped and `private: true`, with no `main`,
47
+ `module`, `types`, export map, or publish configuration. Mixed manifests
48
+ publish only `dist/src`, and Vue remains development-only because app output
49
+ is never published.
48
50
  - Give app/server process signals to a tested, explicitly stoppable,
49
51
  generation-safe runner whose stale failures cannot release a newer run.
50
52
  - Return the runner from convenience startup, so normal cleanup cannot be hidden.
@@ -104,6 +104,8 @@ kind. It reads declaration syntax and file name, never meaning.
104
104
  data-kind file, that every centralized declaration is exported, that a class sits in its matching
105
105
  implementation or errors file, and that `constants.ts` declares only UPPER_SNAKE_CASE consts with
106
106
  no bare collection literal.
107
+ - It proves that no source, test, config, or script file carries an `eslint-disable` or
108
+ `oxlint-disable` directive.
107
109
  - It does not prove a collection is frozen. It reads the declaration, never the value a call
108
110
  returns, so `Object.freeze([…])` and any other call initializer are one syntax to it. The freeze
109
111
  obligation in the kind-purity rules above binds regardless; only the bare literal is mechanical.
@@ -63,10 +63,16 @@ Never use in-repository `@src/*` aliases in public guide examples; reserve them
63
63
 
64
64
  - Skills prescribe reusable process; they do not copy naming, placement, syntax, lifecycle, or test laws from `AGENTS.md` and rules.
65
65
  - Keep `SKILL.md` concise and route conditional detail to one-level `references/`.
66
+ - Name every Markdown file in a skill's `references/` from its `SKILL.md`, and delete a reference nothing names.
66
67
  - Frontmatter contains only `name` and a trigger-focused `description`.
68
+ - Set `name` to the skill's own directory name.
69
+ - Write `description` as a single-line scalar or a folded `>-` block, and no other shape. Include in it a sentence beginning `Use ` that names when to invoke the skill.
67
70
  - Do not put model routing or package version catalogs in a skill.
68
71
  - Validate every referenced resource; do not leave template TODOs or auxiliary README/changelog files.
69
72
  - Write `agents/openai.yaml` as one root `interface:` mapping over exactly `display_name`, `short_description`, and `default_prompt`, in that order, each on its own two-space-indented line.
70
73
  - Give every one of those keys a non-empty single-quoted scalar, and write an apostrophe inside it as `''`.
71
74
  - Name the skill's own `$<directory>` token in `default_prompt`.
72
75
  - Keep provider bridges minimal: they load one canonical workflow and add no competing instructions.
76
+ - Give a provider bridge its canonical skill's `name` and `description` verbatim, name the `.agents/skills/<name>/SKILL.md` path it loads, and give it no references of its own.
77
+ - Give every canonical skill exactly one provider bridge directory of the same name, and give every bridge directory a canonical twin.
78
+ - Review a bridge body's remaining instructions yourself: the policy sweep proves `name` and `description` parity, the named canonical path, and the absence of bridge-owned references, and nothing else about the body.
@@ -18,7 +18,9 @@ paths:
18
18
  not add `tests/configs/`.
19
19
  - Resolve a mirrored module through `.ts`, `.tsx`, `.mts`, `.cts`, `.vue`, `.scss`, or `.css`.
20
20
  - Resolve a Sass or CSS partial through the module's leading underscore.
21
- - Resolve a `setup*` module test against its sibling `setup*.ts` module inside `tests/`.
21
+ - Resolve each root `tests/setup*.test.ts` proof against its sibling `tests/setup*.ts` module. A
22
+ root `tests/setup.test.ts` file can prove several setup modules when their helpers serve
23
+ several projects.
22
24
  - Prefer test filenames matching entrypoints: `index.test.ts` for `index.ts`, `main.test.ts` for `main.ts`.
23
25
  - Tests are deterministic: identical inputs produce identical results.
24
26
  - Keep default suites fast: timers normally use 10–50 ms and tests make no network calls.
@@ -54,8 +56,12 @@ its own:
54
56
  | `tests/conformance.test.ts` | Where this package drifts from the official tooling it tracks |
55
57
  | `tests/distribution.test.ts` | The packed package installs and resolves through its public exports |
56
58
  | `tests/integration.test.ts` | The package's features work together end to end across environments |
59
+ | `tests/setup*.test.ts` | Reusable behavior exported from sibling `tests/setup*.ts` modules works as the workspace's suites require |
57
60
  | `tests/service/**/*.test.ts` | The live external services this package drives, driven for real |
58
61
 
62
+ - Put each root `tests/setup*.test.ts` proof in the `setup` project. Keep its assertions on
63
+ exported test-infrastructure behavior: do not duplicate production behavior there, and do not
64
+ move setup-helper assertions into another cross-cutting proof.
59
65
  - `.claude/rules/workspace.md` names the Vitest project each location belongs to.
60
66
  - The `guides` project runs in Node with the browser disabled. Its subject is what the guide
61
67
  claims: that every documented name resolves, and that every fence asserting a value returns
@@ -146,6 +152,10 @@ Test helpers are shared infrastructure, not local test-file clutter.
146
152
 
147
153
  `@orkestrel/test` owns the helpers every workspace repeats: the call recorder, the real delay, the JSON and async collectors, and the owned scratch directory. Import them from `@orkestrel/test`, and its Node-only helpers from `@orkestrel/test/server`. Write a helper of your own only where the package exports none for the job. The shapes below are the contract a workspace codes against, not source to copy.
148
154
 
155
+ - For the vendored test set (`tests/setupPolicy.ts`, `tests/policy.test.ts`, and
156
+ `tests/config.test.ts`), keep shared helpers within that set instead of importing them from
157
+ `@orkestrel/test`; follow the vendored-file import law in `.claude/rules/workspace.md`.
158
+
149
159
  - Extract a fixture, recorder, event factory, async wait, renderer, scenario/data builder, protocol fixture, or DOM builder as soon as it could serve another test.
150
160
  - Any duplicate or near-duplicate helper is a defect; consolidate it into one general form.
151
161
  - Export every reusable helper, fixture type, factory, constant, and guard from setup files.
@@ -17,12 +17,19 @@ The non-negotiables and design laws in `AGENTS.md` apply without exception and a
17
17
  - Place `import type` declarations before value imports.
18
18
  - Do not place blank lines between consecutive imports of the same kind.
19
19
  - Narrow an accepted `unknown` with a total guard rather than a conditional access.
20
+ - A parameter property is a constructor parameter carrying any accessibility or `readonly` modifier.
21
+ The ban reaches every form, `readonly` alone included; declare the `#` field and assign it in the
22
+ constructor body.
20
23
 
21
24
  ## Types
22
25
 
23
26
  - Put every reusable or public interface/type alias in the nearest authoritative `*/types.ts`.
24
27
  - Public collection properties and return types use `readonly T[]`, `ReadonlyMap<K, V>`, or `ReadonlySet<T>`.
25
28
  - Optional state is `T | undefined`; an optional lookup failure returns `undefined`.
29
+ - `as const` annotates a literal with its own type and never overrides the checker, so the assertion
30
+ ban does not reach it. Use it to derive a literal union from a value and to fix a tuple's arity and
31
+ element types. Do not write it on a value whose contract is already declared; annotate the
32
+ declaration instead.
26
33
 
27
34
  ## Immutability
28
35
 
@@ -63,8 +70,15 @@ type Result<T, E = Error> = Success<T> | Failure<E>
63
70
 
64
71
  ## Comments and API documentation
65
72
 
66
- - Comments explain why, never restate what self-explanatory code does. The Writing rules in `AGENTS.md` govern their prose.
73
+ - Comments explain why, never restate what self-explanatory code does. The Writing rules in `AGENTS.md` and `.claude/rules/writing.md` govern their prose.
67
74
  - Every public export has complete TSDoc: description, `@param`, `@returns`, and `@example` where applicable.
75
+ - The first sentence states what the symbol does in the third person with an `-s` verb — `Creates`,
76
+ `Returns`, `Checks whether` — and never repeats the symbol's name.
77
+ - Describe a boolean parameter as "If `true`, …; if `false`, …", and a boolean return as
78
+ "True if …; false otherwise".
79
+ - Write a default as "Default: …" and a thrown error as "Thrown when …".
80
+ - State a prerequisite and the failure behavior wherever the symbol has either.
81
+ - `@deprecated` names the replacement first, then the reason.
68
82
  - Document an options object as one `@param`; describe its short fields under `@remarks`.
69
83
  - Private methods and overload-specific notes use single-line `//` comments, not public TSDoc.
70
84
  - Do not document speculative future product behavior unless requested.
@@ -60,13 +60,22 @@ Define aliases in `tsconfig.json` first. `vite.config.ts` derives from `compiler
60
60
  - `*/types.ts`: public API contracts.
61
61
  - `configs/src/` and `configs/app/`: thin per-target wrappers, including optional
62
62
  `configs/src/*bin*` files. Shared logic remains in root configs.
63
- - `configs/helpers.ts` and `configs/browsers.ts`: the only permitted leaves under `configs/`. Each
64
- imports nothing from the workspace, which is what keeps it a leaf. Each `configs/src/*.config.ts`
65
- imports the root config rather than a leaf, so shared build logic stays in one place.
63
+ - `configs/helpers.ts`, `configs/browsers.ts`, and `configs/policy.ts`: the only permitted leaves
64
+ under `configs/`. Each imports nothing from the workspace, which is what keeps it a leaf. Each
65
+ `configs/src/*.config.ts` imports the root config rather than a leaf, so shared build logic stays
66
+ in one place.
66
67
  - Keep `configs/helpers.ts` free of any dependency a core-only workspace does not declare. It is
67
68
  vendored byte-identical to every workspace, so an import there must resolve in all of them.
68
69
  `configs/browsers.ts` exists for that reason: it imports `playwright` and
69
70
  `@vitest/browser-playwright`, and only a workspace with a browser environment is given it.
71
+ - Keep `configs/policy.ts` free of imports entirely. It is the workspace's oxlint plugin, the lint
72
+ instrument of the policy law, and it is vendored byte-identical to every workspace including a
73
+ core-only one, so a module that imports nothing at all is the only form that resolves in all of
74
+ them. Because it may import nothing, keep its own types, data, and functions in that one file: the
75
+ centralized-kind placement in `.claude/rules/architecture.md` does not reach it.
76
+ - When a file is vendored byte-identical, import nothing that fails to resolve in any target. Import
77
+ no `@orkestrel/*` package from it: every such package is itself a target and cannot depend on
78
+ itself.
70
79
 
71
80
  Environment rules:
72
81
 
@@ -114,15 +123,20 @@ environment:
114
123
  The second axis is cross-cutting workspace proofs. Each one covers the whole workspace rather than
115
124
  one environment, so each is its own project:
116
125
 
117
- | Project | Files | Proves | Gate |
118
- | -------------- | ---------------------------- | ------------------------------------------------------------------- | ------------------------------------- |
119
- | `policy` | `tests/policy.test.ts` | Every source file obeys the syntactic coding and placement law | `test` |
120
- | `config` | `tests/config.test.ts` | Root configuration resolves its aliases, projects, and outputs | `test` |
121
- | `guides` | `tests/guides.test.ts` | Every documented API exists and every public API is documented | `test` |
122
- | `conformance` | `tests/conformance.test.ts` | Where this package drifts from the official tooling it tracks | `test` |
123
- | `distribution` | `tests/distribution.test.ts` | The packed package installs and resolves through its public exports | `prepublishOnly`; absent when private |
124
- | `integration` | `tests/integration.test.ts` | The package's features work together end to end across environments | `test` |
125
- | `service` | `tests/service/**/*.test.ts` | The live external services this package drives, driven for real | `prepublishOnly`; `test` when private |
126
+ | Project | Files | Proves | Gate |
127
+ | -------------- | ---------------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------- |
128
+ | `policy` | `tests/policy.test.ts` | Every source file obeys the syntactic coding and placement law | `test` |
129
+ | `config` | `tests/config.test.ts` | Root configuration resolves its aliases, projects, and outputs | `test` |
130
+ | `setup` | `tests/setup*.test.ts` | Reusable behavior exported from the root test setup modules works as the consuming suites require | `test` |
131
+ | `guides` | `tests/guides.test.ts` | Every documented API exists and every public API is documented | `test` |
132
+ | `conformance` | `tests/conformance.test.ts` | Where this package drifts from the official tooling it tracks | `test` |
133
+ | `distribution` | `tests/distribution.test.ts` | The packed package installs and resolves through its public exports | `prepublishOnly`; absent when private |
134
+ | `integration` | `tests/integration.test.ts` | The package's features work together end to end across environments | `test` |
135
+ | `service` | `tests/service/**/*.test.ts` | The live external services this package drives, driven for real | `prepublishOnly`; `test` when private |
136
+
137
+ - Define the `setup` project only when a root file matches `tests/setup*.test.ts`, exact-case.
138
+ Include every matching file. When registered, emit `test:setup` and run it from `test`. When no
139
+ file matches, emit neither the project nor the script.
126
140
 
127
141
  `conformance`, `integration`, `distribution`, and `service` are four subjects, not four names for
128
142
  one.
@@ -155,7 +169,7 @@ Setup assets:
155
169
  - Styles setup loads `setup.css` and the compiled cascade.
156
170
 
157
171
  Scope with `test:src`, `test:src:core`, `test:app`, `test:app:server`, and equivalent scripts. Each
158
- cross-cutting project has its own script too: `test:policy`, `test:config`, `test:guides`,
172
+ cross-cutting project has its own script too: `test:policy`, `test:config`, `test:setup`, `test:guides`,
159
173
  `test:conformance`, `test:distribution`, `test:integration`, `test:service`.
160
174
 
161
175
  ## Typechecking and environment isolation
@@ -225,6 +239,22 @@ Run `show` only **after** formatting. The committed `demo/showcase.html` is gene
225
239
  - Node build targets derive from the package's declared supported runtime. Keep `engines`, bundler targets, scoped configs, tests, and documentation aligned; never hard-code one Node version line-wide.
226
240
  - Browser framework: Vue 3 when present.
227
241
 
242
+ Policy instruments:
243
+
244
+ - Put every rule of the policy law in exactly one of two instruments. `configs/policy.ts` — the
245
+ oxlint plugin, namespace `policy` — takes the rules a single file's AST decides. The policy sweep
246
+ (`tests/setupPolicy.ts`) takes the rules that are path- or text-shaped, and every rule whose
247
+ subject is suppression itself.
248
+ - Choose between them by what can defeat the rule: an instrument must not be suppressible by the
249
+ thing it polices. A file-level `oxlint-disable` silently defeats every lint rule in its file,
250
+ plugin rules included, and nothing inside a file can suppress the sweep.
251
+ - Write each visitor in the plugin's visitor table as a one-line context-binding arrow delegating to
252
+ a named module-scope `report{Noun}` function. Never write rule logic inline in the table. That
253
+ arrow is the sanctioned exception to the in-body function-expression limits in
254
+ `.claude/rules/architecture.md` for exactly that table.
255
+ - Name no individual rule id here. This section fixes the two instruments and how work is assigned
256
+ between them; each rule's substance stays with the law it enforces.
257
+
228
258
  ## Text integrity
229
259
 
230
260
  - Store text as UTF-8.