@orkestrel/scaffold 0.0.37 → 0.0.39

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 (33) hide show
  1. package/dist/host/CLAUDE.md +2 -0
  2. package/dist/host/agents/orchestration.md +68 -27
  3. package/dist/host/agents/skills/orkestrel-debrief/SKILL.md +7 -2
  4. package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +3 -0
  5. package/dist/host/agents/skills/orkestrel-debrief/references/instruction-audit.md +6 -0
  6. package/dist/host/claude/agents/analyst.md +6 -3
  7. package/dist/host/claude/agents/checker.md +1 -1
  8. package/dist/host/claude/agents/codex.md +13 -9
  9. package/dist/host/claude/agents/grok.md +3 -2
  10. package/dist/host/claude/agents/orkestrel.md +43 -36
  11. package/dist/host/claude/agents/planner.md +2 -1
  12. package/dist/host/claude/agents/reviewer.md +15 -14
  13. package/dist/host/claude/agents/sol.md +4 -2
  14. package/dist/host/claude/rules/quality.md +2 -1
  15. package/dist/host/claude/rules/tests.md +1 -0
  16. package/dist/host/codex/agents/analyst.toml +5 -5
  17. package/dist/host/codex/agents/claude.toml +34 -0
  18. package/dist/host/codex/agents/grok.toml +3 -3
  19. package/dist/host/codex/agents/opus.toml +16 -20
  20. package/dist/host/codex/agents/planner.toml +12 -17
  21. package/dist/host/codex/agents/reviewer.toml +13 -18
  22. package/dist/host/guides/guide.md +193 -98
  23. package/dist/host/manifest.json +6 -1
  24. package/dist/host/tests/config.test.ts +10 -7
  25. package/dist/src/core/index.cjs +9 -9
  26. package/dist/src/core/index.cjs.map +1 -1
  27. package/dist/src/core/index.js +9 -9
  28. package/dist/src/core/index.js.map +1 -1
  29. package/dist/src/server/index.cjs +32 -10
  30. package/dist/src/server/index.cjs.map +1 -1
  31. package/dist/src/server/index.js +32 -10
  32. package/dist/src/server/index.js.map +1 -1
  33. package/package.json +8 -8
@@ -1,22 +1,25 @@
1
1
  # Guide
2
2
 
3
3
  > A pure, I/O-free guides-parity toolkit: `Guide` extracts a markdown guide's documented
4
- > surface, method groups, links, and test links; `Source` reflects direct declarations and
5
- > conventional barrel reachability from a consumer-supplied file inventory via pure text
4
+ > surface, method groups, links, test links, and fenced code blocks; `Source` reflects direct
5
+ > declarations and conventional barrel reachability from a consumer-supplied file inventory via pure text
6
6
  > scanners (no filesystem or TypeScript compiler API); runtime dependencies provide markdown
7
7
  > and contract primitives, while comparison helpers (`missingSymbols`, `findMissing`,
8
8
  > `resolveLink`, …) reduce every guides-parity check to `expect([]).toEqual([])`
9
- > (AGENTS §22). Source: [`src/core`](../../src/core). Published through `@orkestrel/guide`.
9
+ > (AGENTS §22). Source: [`src/core`](../src/core). Published through `@orkestrel/guide`.
10
10
 
11
11
  The doctrine: a guide is a contract, not prose. `createGuide(markdown)` parses a guide's
12
12
  source once (via `@orkestrel/markdown`) into a `GuideInterface` — its `## Surface` identifiers
13
- (kind-tagged), its `## Methods` interface/method groups, every link, and its `## Tests`
14
- links, each cached at construction. `createSource({ files, module })` builds a
13
+ (kind-tagged), its `## Methods` interface/method groups, every link, its `## Tests`
14
+ links, and every fenced code block, each cached at construction.
15
+ `createSource({ files, module })` builds a
15
16
  `SourceInterface` that reflects intentional direct declarations, conventional barrel-reachable
16
17
  declarations, and interface/class methods by scanning a consumer-gathered file inventory with
17
- plain-text line scanners, never touching disk itself. A guides-parity test asserts direct
18
- declarations equal the barrel surface and the barrel surface equals the documented surface,
19
- in both directions. `parseManifest` reads a `guides/README.md`'s
18
+ plain-text line scanners, never touching disk itself. `createSourceManager({ files, modules })`
19
+ resolves the consumer's own import specifiers onto those views, one shared `Source` per module, so
20
+ a check that meets an import decides from the specifier alone which face of the package it names.
21
+ A guides-parity test asserts direct declarations equal the barrel surface and the barrel surface
22
+ equals the documented surface, in both directions. `parseManifest` reads a `guides/README.md`'s
20
23
  `## By concept` table into the list of `{ concept, spec, source, tests }` entries a suite
21
24
  iterates to run this check once per documented concept.
22
25
 
@@ -24,25 +27,28 @@ iterates to run this check once per documented concept.
24
27
 
25
28
  ### Types
26
29
 
27
- The manifest/extraction shapes every check is built from, from [`types.ts`](../../src/core/types.ts).
28
-
29
- | Name | Kind | Shape |
30
- | ----------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
31
- | `ExportKind` | type | `'type' \| 'interface' \| 'const' \| 'function' \| 'class'` — the five-kind reflection population. Comment/template payload and enums are outside it; general package policy does not forbid enums. |
32
- | `SurfaceSymbol` | interface | `{ name, kind }` — one documented / exported symbol. |
33
- | `GuideModule` | type | `string \| readonly string[]` — one source directory, or several; `'.'` is the canonical workspace root. |
34
- | `SourceLine` | interface | `{ source, code, jsdoc }` — one terminator-free physical source line with exact raw text, equal-length projections, and every genuine JSDoc span at its physical column or `undefined`. |
35
- | `ManifestEntry` | interface | `{ concept, spec, source, tests }` — one `## By concept` manifest row, paths normalized to workspace root. |
36
- | `MethodGroup` | interface | `{ interface, methods }` — one `#### \`Interface\`` block's documented method names, in table order. |
37
- | `GuideInterface` | interface | `{ sections, surface, methods, links, tests, patterns }` the structured, pure view over one parsed guide. See [`## Methods`](#methods). |
38
- | `SourceInterface` | interface | `{ exports, surface, methods, exists, hidden, examples }` — direct declarations, conventional barrel reachability, members, paths, discipline, and examples. See [`## Methods`](#methods). |
39
- | `SourceOptions` | interface | `{ files, module }` — exact canonical-segment opaque workspace-relative inventory keys plus the canonicalized module scope to reflect. |
40
- | `DeclarationHead` | interface | `{ text, end }` — a declaration head joined into one line (across an oxfmt-wrapped signature) plus the index of the line ending in `{`. |
30
+ The manifest/extraction shapes every check is built from, from [`types.ts`](../src/core/types.ts).
31
+
32
+ | Name | Kind | Shape |
33
+ | ------------------------ | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
34
+ | `ExportKind` | type | `'type' \| 'interface' \| 'const' \| 'function' \| 'class'` — the five-kind reflection population. Comment/template payload and enums are outside it; general package policy does not forbid enums. |
35
+ | `SurfaceSymbol` | interface | `{ name, kind }` — one documented / exported symbol. |
36
+ | `GuideModule` | type | `string \| readonly string[]` — one source directory, or several; `'.'` is the canonical workspace root. |
37
+ | `SourceLine` | interface | `{ source, code, jsdoc }` — one terminator-free physical source line with exact raw text, equal-length projections, and every genuine JSDoc span at its physical column or `undefined`. |
38
+ | `ManifestEntry` | interface | `{ concept, spec, source, tests }` — one `## By concept` manifest row, paths normalized to workspace root. |
39
+ | `MethodGroup` | interface | `{ interface, methods }` — one `#### \`Interface\`` block's documented method names, in table order. |
40
+ | `GuideFence` | interface | `{ language, code }` one fenced code block; `language` is its info-string tag, or `undefined` when the fence is untagged. |
41
+ | `GuideInterface` | interface | `{ sections, surface, methods, links, tests, fences }` — the structured, pure view over one parsed guide. See [`## Methods`](#methods). |
42
+ | `SourceInterface` | interface | `{ exports, surface, methods, exists, hidden, examples }` — direct declarations, conventional barrel reachability, members, paths, discipline, and examples. See [`## Methods`](#methods). |
43
+ | `SourceManagerInterface` | interface | `{ source }` — resolves one import specifier to the shared source view of the module it names. See [`## Methods`](#methods). |
44
+ | `SourceOptions` | interface | `{ files, module }` — exact canonical-segment opaque workspace-relative inventory keys plus the canonicalized module scope to reflect. |
45
+ | `SourceManagerOptions` | interface | `{ files, modules }` — one shared inventory plus the consumer's own specifier-to-module policy. |
46
+ | `DeclarationHead` | interface | `{ text, end }` — a declaration head joined into one line (across an oxfmt-wrapped signature) plus the index of the line ending in `{`. |
41
47
 
42
48
  ### Constants
43
49
 
44
50
  The section-heading keys and external-link schemes every extractor and link check is keyed
45
- on, from [`constants.ts`](../../src/core/constants.ts).
51
+ on, from [`constants.ts`](../src/core/constants.ts).
46
52
 
47
53
  | Name | Kind | Behavior |
48
54
  | ------------------ | ----- | ------------------------------------------------------------------------------------------------------------------------------- |
@@ -54,55 +60,59 @@ on, from [`constants.ts`](../../src/core/constants.ts).
54
60
 
55
61
  ### Helpers
56
62
 
57
- Pure, total leaves from [`helpers.ts`](../../src/core/helpers.ts) — the building blocks
58
- `parsers.ts`'s extractors and a consumer's parity test both reach for directly.
59
-
60
- | Name | Kind | Signature | Behavior |
61
- | ---------------------- | -------- | --------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
62
- | `normalizeDirectories` | function | `(module: GuideModule) => readonly string[]` | Canonicalizes through `resolvePath`, uses `'.'` for root, and removes duplicates in first-seen order. |
63
- | `selectModuleKeys` | function | `(files: Readonly<Record<string, string>>, module: GuideModule) => readonly string[]` | Selects exact canonical-segment opaque `.ts` keys under any scope and excludes every selected exact `index.ts` plus `*.test.ts`; sorted. |
64
- | `hasCanonicalSegments` | function | `(key: string) => boolean` | Rejects empty, `.` and `..` slash-separated segments without normalization while retaining ordinary dotfiles. |
65
- | `symbolKey` | function | `(symbol: SurfaceSymbol) => string` | The bijection key for a surface symbol`${kind} ${name}` so a symbol comparison diffs (name, kind) pairs, not names alone. |
66
- | `findMissing` | function | `(names: readonly string[], source: readonly string[]) => readonly string[]` | The names present in `names` but absent from `source` the set-difference behind a both-directions bijection assertion. |
67
- | `missingSymbols` | function | `(symbols: readonly SurfaceSymbol[], source: readonly SurfaceSymbol[]) => readonly string[]` | The `symbolKey` set-difference between two symbol lists. |
68
- | `extractSourceLines` | function | `(source: string) => readonly SourceLine[]` | Equal-length physical source, code, and JSDoc projection with bounded literal Unicode identifier slash state. |
69
- | `isExternalLink` | function | `(href: string) => boolean` | Whether a link `href` should be skipped by guides-parity link checks an external scheme (`EXTERNAL_SCHEMES`) or a bare `#` anchor. |
70
- | `resolveLink` | function | `(file: string, target: string) => string` | Derives a declaring file's directory, including workspace-root files, then delegates to `resolvePath`. |
71
- | `resolvePath` | function | `(directory: string, target: string) => string` | Sole dot-segment reducer; returns `'.'` when no segment remains and preserves every excess leading parent. |
72
- | `firstCode` | function | `(nodes: readonly InlineNode[]) => string \| undefined` | The first code-span value found by descending an inline node list, following into `emphasis` / `link` children. |
73
- | `identifierOf` | function | `(code: string) => string` | The identifier prefix of a code-span text everything before its first `<`, trimmed (strips generic-parameter annotation). |
74
- | `kindIndex` | function | `(table: TableNode) => number \| undefined` | The index of a table's `Kind` column, found by its header text so it survives column reordering. |
75
- | `cellLinks` | function | `(cell: readonly InlineNode[]) => readonly string[]` | The link hrefs found within one table cell's inline content, in walk order. |
76
- | `findUnexampled` | function | `(names: readonly string[], fences: readonly string[], examples: readonly string[]) => readonly string[]` | The names with no fence mention (word boundary) and no `@example` membership the EX check's core comparison. |
77
- | `fenceImports` | function | `(fence: string) => readonly { specifier: string, names: readonly string[] }[]` | Parses a fence's `import` statements into per-specifier imported identifier names the FI check's core comparison. |
63
+ Pure, total leaves from [`helpers.ts`](../src/core/helpers.ts) — the source-line projection,
64
+ the declaration, member, JSDoc, and guide-document grammars built on it, and the comparison and
65
+ path primitives `Guide`, `Source`, `parsers.ts`, and a consumer's parity test all reach for
66
+ directly.
67
+
68
+ | Name | Kind | Signature | Behavior |
69
+ | ---------------------- | -------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
70
+ | `normalizeDirectories` | function | `(module: GuideModule) => readonly string[]` | Canonicalizes through `resolvePath`, uses `'.'` for root, and removes duplicates in first-seen order. |
71
+ | `moduleKey` | function | `(module: GuideModule) => string` | The stable cache key for a module scopeits normalized directories joined by NUL, so two spellings of one module share a key and no directory boundary can collide with directory text. |
72
+ | `selectModuleKeys` | function | `(files: Readonly<Record<string, string>>, module: GuideModule) => readonly string[]` | Selects exact canonical-segment opaque `.ts` keys under any scope and excludes every selected exact `index.ts` plus `*.test.ts`; sorted. |
73
+ | `hasCanonicalSegments` | function | `(key: string) => boolean` | Rejects empty, `.` and `..` slash-separated segments without normalization while retaining ordinary dotfiles. |
74
+ | `symbolKey` | function | `(symbol: SurfaceSymbol) => string` | The bijection key for a surface symbol `${kind} ${name}` so a symbol comparison diffs (name, kind) pairs, not names alone. |
75
+ | `findMissing` | function | `(names: readonly string[], source: readonly string[]) => readonly string[]` | The names present in `names` but absent from `source`the set-difference behind a both-directions bijection assertion. |
76
+ | `missingSymbols` | function | `(symbols: readonly SurfaceSymbol[], source: readonly SurfaceSymbol[]) => readonly string[]` | The `symbolKey` set-difference between two symbol lists. |
77
+ | `extractSourceLines` | function | `(source: string) => readonly SourceLine[]` | Equal-length physical source, code, and JSDoc projection with bounded literal Unicode identifier slash state. |
78
+ | `exportsFrom` | function | `(source: string) => readonly SurfaceSymbol[]` | Direct five-kind exports over projected lines with an uninterrupted column-zero head; projection never widens membership. |
79
+ | `hiddenFrom` | function | `(source: string) => readonly SurfaceSymbol[]` | The non-exported mirror with the same projected, uninterrupted column-zero head and five-kind population. |
80
+ | `joinHead` | function | `(lines: readonly string[], start: number) => DeclarationHead \| undefined` | Joins a declaration head starting at `start` into one space-separated line, consuming lines until the first ending in `{`. |
81
+ | `declarationBody` | function | `(source: string, keyword: 'class' \| 'interface', name: string) => readonly string[]` | Selects a real head/close from projected lines and returns the aligned raw body for JSDoc evidence. |
82
+ | `memberMethods` | function | `(lines: readonly string[]) => readonly string[]` | Matches callable members on one projection of the body; commented candidates, getters, setters, `static`, and `#` privates never count. |
83
+ | `extractExampleLines` | function | `(lines: readonly SourceLine[]) => readonly SourceLine[]` | The next physical candidate after the authoritative exact `@example` span in a leading chain. |
84
+ | `examplesFrom` | function | `(source: string) => readonly string[]` | Matches exported functions against shared eligible genuine JSDoc adjacency and aligned code. |
85
+ | `exampleMethods` | function | `(lines: readonly string[]) => readonly string[]` | Matches callable members against the same shared eligible genuine JSDoc adjacency and aligned code. |
86
+ | `sectionBlocks` | function | `(document: MarkdownDocument, heading: string) => readonly BlockNode[]` | The block nodes under a named `##` heading, up to the next `##`-or-higher heading (or the document's end). |
87
+ | `extractSurface` | function | `(document: MarkdownDocument) => readonly SurfaceSymbol[]` | Every `## Surface` identifier: each table's rows union every backticked H3 entity heading, deduped by `symbolKey`. |
88
+ | `extractMethods` | function | `(document: MarkdownDocument) => readonly MethodGroup[]` | One `MethodGroup` per documented behavioral interface in `## Methods` — an H4 code span sets the interface, the following table lists its methods. |
89
+ | `extractLinks` | function | `(document: MarkdownDocument) => readonly string[]` | Every link href in the guide document, including table cells — a full, depth-first AST walk. |
90
+ | `extractTests` | function | `(document: MarkdownDocument) => readonly string[]` | The relative test links declared under `## Tests`. |
91
+ | `extractFences` | function | `(document: MarkdownDocument) => readonly GuideFence[]` | Every fenced code block anywhere in the guide document, tagged or not — a full AST walk with no language filter. |
92
+ | `isExternalLink` | function | `(href: string) => boolean` | Whether a link `href` should be skipped by guides-parity link checks — an external scheme (`EXTERNAL_SCHEMES`) or a bare `#` anchor. |
93
+ | `resolveLink` | function | `(file: string, target: string) => string` | Derives a declaring file's directory, including workspace-root files, then delegates to `resolvePath`. |
94
+ | `resolvePath` | function | `(directory: string, target: string) => string` | Sole dot-segment reducer; returns `'.'` when no segment remains and preserves every excess leading parent. |
95
+ | `firstCode` | function | `(nodes: readonly InlineNode[]) => string \| undefined` | The first code-span value found by descending an inline node list, following into `emphasis`, `link`, and `image` children. |
96
+ | `identifierOf` | function | `(code: string) => string` | The identifier prefix of a code-span text — everything before its first `<`, trimmed (strips generic-parameter annotation). |
97
+ | `kindIndex` | function | `(table: TableNode) => number \| undefined` | The index of a table's `Kind` column, found by its header text so it survives column reordering. |
98
+ | `cellLinks` | function | `(cell: readonly InlineNode[]) => readonly string[]` | The link hrefs found within one table cell's inline content, in walk order. |
99
+ | `findUnexampled` | function | `(names: readonly string[], fences: readonly string[], examples: readonly string[]) => readonly string[]` | The names with no fence mention (word boundary) and no `@example` membership — the EX check's core comparison. |
100
+ | `findUnlisted` | function | `(fences: readonly GuideFence[], languages: readonly string[]) => readonly GuideFence[]` | The fences whose language the caller did not list, plus every untagged fence — an untagged fence has no language to list. |
101
+ | `fenceImports` | function | `(fence: string) => readonly { specifier: string, names: readonly string[] }[]` | Parses a fence's brace `import` statements into per-specifier imported identifier names — the FI check's core comparison. Brace bindings only: a default, namespace, side-effect, or mixed `import Default, { named }` statement is not surfaced. |
78
102
 
79
103
  ### Parsers
80
104
 
81
- The guide/manifest extraction pipeline, from [`parsers.ts`](../../src/core/parsers.ts) —
82
- the orchestration `Guide` composes out of `helpers.ts`'s leaves.
83
-
84
- | Name | Kind | Signature | Behavior |
85
- | --------------------- | -------- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
86
- | `exportsFrom` | function | `(source: string) => readonly SurfaceSymbol[]` | Direct five-kind exports over projected lines with an uninterrupted column-zero head; projection never widens membership. |
87
- | `hiddenFrom` | function | `(source: string) => readonly SurfaceSymbol[]` | The non-exported mirror with the same projected, uninterrupted column-zero head and five-kind population. |
88
- | `joinHead` | function | `(lines: readonly string[], start: number) => DeclarationHead \| undefined` | Joins a declaration head starting at `start` into one space-separated line, consuming lines until the first ending in `{`. |
89
- | `declarationBody` | function | `(source: string, keyword: 'class' \| 'interface', name: string) => readonly string[]` | Selects a real head/close from projected lines and returns the aligned raw body for JSDoc evidence. |
90
- | `memberMethods` | function | `(lines: readonly string[]) => readonly string[]` | Matches callable members on one projection of the body; commented candidates, getters, setters, `static`, and `#` privates never count. |
91
- | `sectionBlocks` | function | `(document: MarkdownDocument, heading: string) => readonly BlockNode[]` | The block nodes under a named `##` heading, up to the next `##`-or-higher heading (or the document's end). |
92
- | `extractSurface` | function | `(document: MarkdownDocument) => readonly SurfaceSymbol[]` | Every `## Surface` identifier: each table's rows union every backticked H3 entity heading, deduped by `symbolKey`. |
93
- | `extractMethods` | function | `(document: MarkdownDocument) => readonly MethodGroup[]` | One `MethodGroup` per documented behavioral interface in `## Methods` — an H4 code span sets the interface, the following table lists its methods. |
94
- | `extractLinks` | function | `(document: MarkdownDocument) => readonly string[]` | Every link href in the guide document, including table cells — a full, depth-first AST walk. |
95
- | `extractTests` | function | `(document: MarkdownDocument) => readonly string[]` | The relative test links declared under `## Tests`. |
96
- | `extractExampleLines` | function | `(lines: readonly SourceLine[]) => readonly SourceLine[]` | The next physical candidate after the authoritative exact `@example` span in a leading chain. |
97
- | `examplesFrom` | function | `(source: string) => readonly string[]` | Matches exported functions against shared eligible genuine JSDoc adjacency and aligned code. |
98
- | `exampleMethods` | function | `(lines: readonly string[]) => readonly string[]` | Matches callable members against the same shared eligible genuine JSDoc adjacency and aligned code. |
99
- | `extractPatterns` | function | `(document: MarkdownDocument) => readonly string[]` | Every fenced `ts` code block's body text anywhere in the guide document — a full AST walk. |
100
- | `parseManifest` | function | `(markdown: string, directory: string) => readonly ManifestEntry[]` | Resolves manifest links and canonicalizes Source values through `normalizeDirectories`, preserving one-versus-many shape. |
105
+ The manifest coercer, from [`parsers.ts`](../src/core/parsers.ts) — the one scanner that turns
106
+ markdown text into typed values, composed out of `helpers.ts`'s leaves.
107
+
108
+ | Name | Kind | Signature | Behavior |
109
+ | --------------- | -------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
110
+ | `parseManifest` | function | `(markdown: string, directory: string) => readonly ManifestEntry[]` | Resolves manifest links and canonicalizes Source values through `normalizeDirectories`, preserving one-versus-many shape. |
101
111
 
102
112
  ### Shapers
103
113
 
104
114
  Declarative `ContractShape` values (from `@orkestrel/contract`) from
105
- [`shapers.ts`](../../src/core/shapers.ts) — every documented data type here is
115
+ [`shapers.ts`](../src/core/shapers.ts) — every documented data type here is
106
116
  non-recursive, so each shapes directly.
107
117
 
108
118
  | Name | Kind | Builds |
@@ -114,7 +124,7 @@ non-recursive, so each shapes directly.
114
124
  ### Validators
115
125
 
116
126
  Total from-unknown guards composed from `@orkestrel/contract` combinators, from
117
- [`validators.ts`](../../src/core/validators.ts).
127
+ [`validators.ts`](../src/core/validators.ts).
118
128
 
119
129
  | Name | Kind | Narrows to / Tests | Behavior |
120
130
  | ----------------- | ----- | ------------------ | ------------------------------------------------------------------------ |
@@ -125,19 +135,20 @@ Total from-unknown guards composed from `@orkestrel/contract` combinators, from
125
135
 
126
136
  ### Factories
127
137
 
128
- From [`factories.ts`](../../src/core/factories.ts).
138
+ From [`factories.ts`](../src/core/factories.ts).
129
139
 
130
- | Name | Kind | Signature | Behavior |
131
- | ----------------------------- | -------- | --------------------------------------------- | -------------------------------------------------------------------------------- |
132
- | `createGuide` | function | `(source: string) => GuideInterface` | Creates a structured `GuideInterface` view over one guide's markdown source. |
133
- | `createSource` | function | `(options: SourceOptions) => SourceInterface` | Creates a pure `SourceInterface` over a consumer-supplied file inventory. |
134
- | `createSurfaceSymbolContract` | function | `() => ContractInterface<SurfaceSymbol>` | Compiles `surfaceSymbolShape` into a guard / parser / schema / generator bundle. |
135
- | `createMethodGroupContract` | function | `() => ContractInterface<MethodGroup>` | Compiles `methodGroupShape` into a guard / parser / schema / generator bundle. |
136
- | `createManifestEntryContract` | function | `() => ContractInterface<ManifestEntry>` | Compiles `manifestEntryShape` into a guard / parser / schema / generator bundle. |
140
+ | Name | Kind | Signature | Behavior |
141
+ | ----------------------------- | -------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
142
+ | `createGuide` | function | `(source: string) => GuideInterface` | Creates a structured `GuideInterface` view over one guide's markdown source. |
143
+ | `createSource` | function | `(options: SourceOptions) => SourceInterface` | Creates a pure `SourceInterface` over a consumer-supplied file inventory. |
144
+ | `createSourceManager` | function | `(options: SourceManagerOptions) => SourceManagerInterface` | Creates a `SourceManagerInterface` over a specifier-to-module policy, sharing one `Source` per module. |
145
+ | `createSurfaceSymbolContract` | function | `() => ContractInterface<SurfaceSymbol>` | Compiles `surfaceSymbolShape` into a guard / parser / schema / generator bundle. |
146
+ | `createMethodGroupContract` | function | `() => ContractInterface<MethodGroup>` | Compiles `methodGroupShape` into a guard / parser / schema / generator bundle. |
147
+ | `createManifestEntryContract` | function | `() => ContractInterface<ManifestEntry>` | Compiles `manifestEntryShape` into a guard / parser / schema / generator bundle. |
137
148
 
138
149
  ### `Guide`
139
150
 
140
- The implementing class of `GuideInterface`, from [`Guide.ts`](../../src/core/Guide.ts). A
151
+ The implementing class of `GuideInterface`, from [`Guide.ts`](../src/core/Guide.ts). A
141
152
  stateful, structured view over one parsed guide: parses `source` once via
142
153
  `@orkestrel/markdown` and never touches the filesystem — `Guide` has no notion of "where"
143
154
  the guide came from, only its markdown text. Every accessor returns the same cached,
@@ -146,7 +157,7 @@ surface.
146
157
 
147
158
  ### `Source`
148
159
 
149
- The implementing class of `SourceInterface`, from [`Source.ts`](../../src/core/Source.ts). A
160
+ The implementing class of `SourceInterface`, from [`Source.ts`](../src/core/sources/Source.ts). A
150
161
  pure reflection over a consumer-supplied file inventory (root-relative path → file text) plus a
151
162
  module scope. `exports()` inventories direct `type`, `interface`, `const`, `function`, and
152
163
  `class` declarations in the selected canonical directories' exact opaque module keys over
@@ -159,6 +170,20 @@ JSDoc evidence. `Source` never uses the
159
170
  TypeScript compiler API or filesystem; the consumer gathers `files` however its environment
160
171
  allows. See [`## Methods`](#methods) for the public call-signature surface.
161
172
 
173
+ ### `SourceManager`
174
+
175
+ The implementing class of `SourceManagerInterface`, from
176
+ [`SourceManager.ts`](../src/core/sources/SourceManager.ts). It answers one question a bare `Source`
177
+ cannot: a guide fence may import from a face of the package this `Source` does not cover, and the
178
+ check needs the right `Source` for whichever specifier the fence names. `modules` is the consumer's
179
+ own policy — it maps each import specifier the package publishes to the source module behind it —
180
+ and `SourceManager` never infers or normalizes that map. `source(specifier)` returns `undefined` for
181
+ an unmapped specifier, which is how a fence-import check skips a foreign import; a separate list of
182
+ self-specifiers is therefore no longer needed, because a mapped specifier is local and absence is
183
+ the skip signal. One `Source` is cached per module, so two specifiers naming one module share one
184
+ entity and the inventory is scanned once. See [`## Methods`](#methods) for its public
185
+ call-signature surface.
186
+
162
187
  ## Methods
163
188
 
164
189
  The public methods of each behavioral interface — one table per type, keyed by its
@@ -173,24 +198,54 @@ backticked name (AGENTS §22).
173
198
  | `methods` | `readonly MethodGroup[]` | One `MethodGroup` per documented behavioral interface in `## Methods`. |
174
199
  | `links` | `readonly string[]` | Every link href in the guide, including table cells. |
175
200
  | `tests` | `readonly string[]` | The relative test links declared under `## Tests`. |
176
- | `patterns` | `readonly string[]` | Every fenced `ts` code block's body text, whole document. |
201
+ | `fences` | `readonly GuideFence[]` | Every fenced code block in the whole document, tagged or not — no language filter. |
177
202
 
178
203
  #### `SourceInterface`
179
204
 
180
205
  | Method | Returns | Behavior |
181
206
  | ---------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
182
- | `exports` | `readonly SurfaceSymbol[]` | Direct `type`, `interface`, `const`, `function`, and `class` declarations in the selected module keys. |
183
- | `surface` | `readonly SurfaceSymbol[]` | Every declaration reachable through the selected directories' conventional root `index.ts` barrels. |
207
+ | `exports` | `readonly SurfaceSymbol[]` | What the package **declares** — direct `type`, `interface`, `const`, `function`, and `class` declarations in the selected module keys. |
208
+ | `surface` | `readonly SurfaceSymbol[]` | What a consumer can **import** — every declaration reachable through the selected directories' conventional root `index.ts` barrels. |
184
209
  | `methods` | `readonly string[]` | The call-signature members of the `class` / `interface` named `name`. |
185
210
  | `exists` | `boolean` | Whether a workspace-root-relative path exists in the inventory. |
186
211
  | `hidden` | `readonly SurfaceSymbol[]` | Every module-scope declaration LACKING `export` (AGENTS §5). |
187
212
  | `examples` | `readonly string[]` | The exported functions (or, given `name`, members) whose eligible leading JSDoc chain ends in an exact block-position `@example` span. |
188
213
 
214
+ #### Which projector a check uses
215
+
216
+ `exports()` and `surface()` answer different questions, and picking the wrong one is the single
217
+ mistake this package sees most often.
218
+
219
+ **`surface()` is what a guide is checked against.** A guide documents what a consumer can import,
220
+ and `surface()` is the barrel-reachable set. Use it for the documented-surface bijection (SB) and
221
+ for the fence-import comparison (FI).
222
+
223
+ **`exports()` answers a different question.** It is every direct declaration under the selected
224
+ modules, and it includes a class that carries `export` only because the placement sweep requires
225
+ every implementation class to be exported. Those classes are deliberately absent from the barrel,
226
+ so they are not part of the package's public surface. Use `exports()` where the question really is
227
+ what the package declares — the direct-versus-barrel legs of SB, which catch a declaration the
228
+ barrel never re-exports.
229
+
230
+ A package reaching for a denylist over `exports()`, or for a second projector built on the
231
+ TypeScript compiler, should first check whether `surface()` already answers its question. It
232
+ usually does: `surface()` excludes exactly the internal implementation classes such a denylist
233
+ enumerates by hand. Measured across the 41 published packages — 552 `ts` fences, 718 import rows —
234
+ zero imports would newly fail if every package switched its fence-import check from `exports()` to
235
+ `surface()`. The switch costs nothing, so a package carrying machinery to work around `exports()`
236
+ is carrying it for a question it never had.
237
+
238
+ #### `SourceManagerInterface`
239
+
240
+ | Method | Returns | Behavior |
241
+ | -------- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
242
+ | `source` | `SourceInterface \| undefined` | The shared source view of the module `specifier` names, or `undefined` when the policy does not map it — a foreign import. |
243
+
189
244
  ## The extraction model
190
245
 
191
246
  `Guide` parses a guide's markdown once (via `@orkestrel/markdown`'s `createMarkdown`) and
192
247
  caches six projections at construction — `sections`, `surface`, `methods`, `links`, `tests`,
193
- `patterns` — so every accessor is a cheap array return, not a re-parse. `extractSurface` scopes to the
248
+ `fences` — so every accessor is a cheap array return, not a re-parse. `extractSurface` scopes to the
194
249
  `## Surface` section (`sectionBlocks`) and unions two sources of identifiers: every table's
195
250
  column-0 code span (kind read from the column whose header text is `Kind`, located
196
251
  positionally so it survives reordering) and every backticked H3 entity heading (a class
@@ -200,8 +255,15 @@ table becomes that interface's `MethodGroup`. Both extractors normalize every id
200
255
  through `identifierOf`, stripping a generic-parameter annotation (`` `WidgetInterface<T>` ``
201
256
  → `WidgetInterface`) so the bijection key is always the bare name. `extractLinks` walks the
202
257
  whole AST for every `link` node (table cells included); `extractTests` does the same walk
203
- scoped to the `## Tests` section only; and `extractPatterns` walks the whole AST for every
204
- fenced `ts` code block.
258
+ scoped to the `## Tests` section only; and `extractFences` walks the whole AST for every
259
+ fenced code block, tagged or not, keeping each fence's info-string language and verbatim body.
260
+
261
+ The fence projection is total on purpose. `Guide` reports what the document contains, and each
262
+ consumer decides which languages its own checks read: `findUnlisted` states the list a package
263
+ allows, and the example and import checks filter to the language they parse. A package documenting
264
+ `sql` or `sh` examples can therefore feed those fences to its own checks. A `ts` filter inside
265
+ `Guide` would have discarded them before the consumer ever saw them, and an untagged fence would
266
+ have vanished with no language to report.
205
267
 
206
268
  `parseManifest(markdown, directory)` resolves every Spec, Source, and Tests link through
207
269
  `resolvePath(directory, target)`, so root, nested, and dotted directory names are ordinary path
@@ -303,16 +365,20 @@ guard so a renamed heading fails loudly instead of passing on an empty extractio
303
365
  missing.
304
366
  - **NV — Non-vacuousness.** `parseManifest` yields at least one entry; each guide's
305
367
  `surface()` and every `MethodGroup` is non-empty — the guard behind every other check.
368
+ - **FL — Fence-language listing.** `findUnlisted(guide.fences(), LANGUAGES)` keeps every fence
369
+ whose language the package did not list, plus every untagged fence. The list is the package's
370
+ own — `Guide` never decides it — so a package documenting `sh` or `sql` examples lists those
371
+ languages and keeps its remaining checks scoped to the one they parse.
306
372
  - **EX — Examples presence.** A documented symbol "has an example" when its bare name
307
- appears (word boundary) in any of `guide.patterns()`'s fence bodies, OR its source has
308
- an immediately preceding eligible leading JSDoc chain whose final authoritative span carries
309
- an exact block-position `@example` tag, with optional title text,
373
+ appears (word boundary) in any fence body from `guide.fences()` filtered to the example
374
+ language, OR its source has an immediately preceding eligible leading JSDoc chain whose final
375
+ authoritative span carries an exact block-position `@example` tag, with optional title text,
310
376
  (`source.examples()` / `source.examples(name)`).
311
377
  Applies to every `function`-kind `Surface` symbol and every `MethodGroup` member.
312
378
  Presence-only — fence and JSDoc CONTENT are never checked. `findUnexampled` is the
313
379
  comparison. Guard: the SB/MB extractions this check reuses already prove non-vacuous.
314
- - **FI — Fence-import reality.** Every `import { ... } from 'specifier'` in a
315
- `guide.patterns()` fence, for a SELF specifier (this repo's own package name / path
380
+ - **FI — Fence-import reality.** Every `import { ... } from 'specifier'` in a `guide.fences()`
381
+ fence of the checked language, for a SELF specifier (this repo's own package name / path
316
382
  alias), imports only names that exist in `source.surface()`. `fenceImports` parses the
317
383
  statement; `findMissing` diffs the imported names against the public/barrel surface's names.
318
384
 
@@ -346,6 +412,17 @@ guide.surface() // [{ name: 'X', kind: 'class' }]
346
412
  guide.sections() // ['Surface']
347
413
  ```
348
414
 
415
+ ### List the fence languages a package allows
416
+
417
+ ````ts
418
+ import { createGuide, findUnlisted } from '@orkestrel/guide'
419
+
420
+ const guide = createGuide('```ts\nconst a = 1\n```\n\n```sh\nnpm test\n```\n')
421
+ guide.fences() // [{ language: 'ts', code: 'const a = 1' }, { language: 'sh', code: 'npm test' }]
422
+ findUnlisted(guide.fences(), ['ts']) // [{ language: 'sh', code: 'npm test' }]
423
+ findUnlisted(guide.fences(), ['ts', 'sh']) // []
424
+ ````
425
+
349
426
  ### Construct a `Source` from an inline files record
350
427
 
351
428
  ```ts
@@ -365,6 +442,24 @@ source.methods('GuideInterface') // ['sections']
365
442
  source.exists('src/core/Guide.ts') // true
366
443
  ```
367
444
 
445
+ ### Resolve a fence's import specifier to the right `Source`
446
+
447
+ ```ts
448
+ import { createSourceManager } from '@orkestrel/guide'
449
+
450
+ const sources = createSourceManager({
451
+ files: {
452
+ 'src/core/index.ts': "export * from './Guide.js'\n",
453
+ 'src/core/Guide.ts': 'export class Guide {}\n',
454
+ },
455
+ modules: { '@scope/package': 'src/core', '@scope/package/core': 'src/core' },
456
+ })
457
+
458
+ sources.source('@scope/package')?.surface() // [{ name: 'Guide', kind: 'class' }]
459
+ sources.source('node:fs') // undefined — a foreign import, which a fence check skips
460
+ sources.source('@scope/package') === sources.source('@scope/package/core') // true
461
+ ```
462
+
368
463
  ### The bijection assertion shape
369
464
 
370
465
  ```ts
@@ -406,18 +501,18 @@ resolveLink('index.ts', './root.ts') // 'root.ts'
406
501
 
407
502
  ## Tests
408
503
 
409
- - [`tests/src/core/helpers.test.ts`](../../tests/src/core/helpers.test.ts) — direct `SourceLine`, lexical, JSDoc-alignment, canonical-key, runtime-name, `resolvePath`, and `resolveLink` invariants; all remaining helper leaves.
410
- - [`tests/src/core/parsers.test.ts`](../../tests/src/core/parsers.test.ts) — projected five-kind direct/hidden reflection, genuine JSDoc example adjacency, faux JSDoc exclusion, nested-directory `parseManifest`, and every guide/manifest extractor.
411
- - [`tests/src/core/validators.test.ts`](../../tests/src/core/validators.test.ts) — `isExportKind` / `isSurfaceSymbol` / `isMethodGroup` / `isManifestEntry`.
412
- - [`tests/src/core/shapers.test.ts`](../../tests/src/core/shapers.test.ts) — per-shape guard exactness, JSON Schema essentials, seeded generate round-trips, parse rebuilds.
413
- - [`tests/src/core/factories.test.ts`](../../tests/src/core/factories.test.ts) — `createGuide` / `createSource` + the compiled symbol/group/manifest contracts.
414
- - [`tests/src/core/Guide.test.ts`](../../tests/src/core/Guide.test.ts) — `Guide`'s six cached projections and production barrel/Guide phantom and kind-drift controls.
415
- - [`tests/src/core/Source.test.ts`](../../tests/src/core/Source.test.ts) — direct/barrel projections, lexical and JSDoc regressions, canonical-key populations, root and nested indexes, exact row grammar, graph invariants, and correlated population controls.
416
- - [`tests/fixtures/broken/stranded-export`](../../tests/fixtures/broken/stranded-export) — permanent negative control: its guide and direct declarations agree while its conventional barrel omits `strandedExport`.
417
- - [`tests/guides.test.ts`](../../tests/guides.test.ts) — the drop-in guides-parity suite, run against THIS repo's own `guides/README.md` manifest the self-dogfooding acceptance criterion.
504
+ - [`tests/src/core/helpers.test.ts`](../tests/src/core/helpers.test.ts) — direct `SourceLine`, lexical, and JSDoc-alignment invariants; projected five-kind direct/hidden reflection; genuine JSDoc example adjacency and faux JSDoc exclusion; every guide-document extractor; canonical-key, runtime-name, `resolvePath`, and `resolveLink` invariants; all remaining helper leaves.
505
+ - [`tests/src/core/parsers.test.ts`](../tests/src/core/parsers.test.ts) — `parseManifest` row parsing, malformed-row skipping, one-versus-many Source canonicalization, and nested manifest directories.
506
+ - [`tests/src/core/validators.test.ts`](../tests/src/core/validators.test.ts) — `isExportKind` / `isSurfaceSymbol` / `isMethodGroup` / `isManifestEntry`.
507
+ - [`tests/src/core/shapers.test.ts`](../tests/src/core/shapers.test.ts) — per-shape guard exactness, JSON Schema essentials, seeded generate round-trips, parse rebuilds.
508
+ - [`tests/src/core/factories.test.ts`](../tests/src/core/factories.test.ts) — `createGuide` / `createSource` + the compiled symbol/group/manifest contracts.
509
+ - [`tests/src/core/Guide.test.ts`](../tests/src/core/Guide.test.ts) — `Guide`'s six cached projections and production barrel/Guide phantom and kind-drift controls.
510
+ - [`tests/src/core/sources/Source.test.ts`](../tests/src/core/sources/Source.test.ts) — direct/barrel projections, lexical and JSDoc regressions, canonical-key populations, root and nested indexes, exact row grammar, graph invariants, and correlated population controls.
511
+ - [`tests/src/core/sources/SourceManager.test.ts`](../tests/src/core/sources/SourceManager.test.ts) — `moduleKey` boundary collision, specifier resolution, the `undefined` skip for an unmapped specifier, array-valued module scopes, and per-module entity sharing with a differently-scoped identity control.
512
+ - [`tests/fixtures/broken/stranded-export`](../tests/fixtures/broken/stranded-export) — permanent negative control: its guide and direct declarations agree while its conventional barrel omits `strandedExport`.
513
+ - [`tests/guides.test.ts`](../tests/guides.test.ts) — the drop-in guides-parity suite, run against THIS repo's own `guides/README.md` manifest — the self-dogfooding acceptance criterion.
418
514
 
419
515
  ## See also
420
516
 
421
517
  - `AGENTS.md` (workspace root) — the rules; §22 documentation-as-contracts.
422
- - [`README.md`](../README.md) — the guides index.
423
- - [`markdown.md`](markdown.md) — the dependency mirror for `@orkestrel/markdown`, the AST/parse layer `Guide` is built on.
518
+ - [`README.md`](README.md) — the guides index.
@@ -380,6 +380,11 @@
380
380
  "destination": ".codex/agents/checker.toml",
381
381
  "executable": false
382
382
  },
383
+ {
384
+ "storage": "codex/agents/claude.toml",
385
+ "destination": ".codex/agents/claude.toml",
386
+ "executable": false
387
+ },
383
388
  {
384
389
  "storage": "codex/agents/grok.toml",
385
390
  "destination": ".codex/agents/grok.toml",
@@ -571,5 +576,5 @@
571
576
  ".codex/agents",
572
577
  ".cursor/rules"
573
578
  ],
574
- "digest": "d2b056bb83ece5f6e49cb3acd27fd917241a51a3e287791eaf8d807cd56fb4c7"
579
+ "digest": "f01b63a454ea972776d7385db831f69dfcc1c888c5df2822870d2fe207c76aac"
575
580
  }
@@ -11,10 +11,10 @@ import {
11
11
  rmSync,
12
12
  writeFileSync,
13
13
  } from 'node:fs'
14
- import { tmpdir } from 'node:os'
15
14
  import { dirname, join, resolve } from 'node:path'
16
15
  import { fileURLToPath, pathToFileURL } from 'node:url'
17
16
  import { build, loadConfigFromFile } from 'vite'
17
+ import { createScratch } from '@orkestrel/test/server'
18
18
  import * as configHelpers from '../configs/helpers.js'
19
19
  import configuration, { resolveWorkspacePath } from '../vite.config.js'
20
20
  import tsconfig from '../tsconfig.json' with { type: 'json' }
@@ -490,8 +490,9 @@ describe('configuration helpers', () => {
490
490
  })
491
491
 
492
492
  it('resolves contained workspace paths and refuses a real outside sibling', () => {
493
- const outside = mkdtempSync(join(tmpdir(), 'orkestrel-config-outside-'))
493
+ const scratch = createScratch({ prefix: 'orkestrel-config-outside-' })
494
494
  try {
495
+ const outside = scratch.path
495
496
  const importer = resolve(root, 'tests/config.test.ts')
496
497
  expect(configHelpers.WORKSPACE_ROOT).toBe(realpathSync.native(root))
497
498
  expect(configHelpers.fileSystemPath(`/@fs/${root}`)).toBe(root)
@@ -507,13 +508,14 @@ describe('configuration helpers', () => {
507
508
  expect(configHelpers.isOutsideWorkspacePath(outside)).toBe(true)
508
509
  expect(configHelpers.isOutsideWorkspacePath('src/core/index.ts')).toBe(false)
509
510
  } finally {
510
- rmSync(outside, { recursive: true, force: true })
511
+ scratch.destroy()
511
512
  }
512
513
  })
513
514
 
514
515
  it('reads bounded files and resolves package roots from real manifests', () => {
515
- const workspace = mkdtempSync(join(tmpdir(), 'orkestrel-config-package-'))
516
+ const scratch = createScratch({ prefix: 'orkestrel-config-package-' })
516
517
  try {
518
+ const workspace = scratch.path
517
519
  const packageRoot = resolve(workspace, 'node_modules/@sample/package')
518
520
  const source = resolve(packageRoot, 'src/index.ts')
519
521
  mkdirSync(dirname(source), { recursive: true })
@@ -544,13 +546,14 @@ describe('configuration helpers', () => {
544
546
  configHelpers.trustedPackageRootFor(source, new Set([realpathSync.native(packageRoot)])),
545
547
  ).toBe(realpathSync.native(packageRoot))
546
548
  } finally {
547
- rmSync(workspace, { recursive: true, force: true })
549
+ scratch.destroy()
548
550
  }
549
551
  })
550
552
 
551
553
  it('classifies module boundaries and extracts static asset sources', async () => {
552
- const workspace = mkdtempSync(join(tmpdir(), 'orkestrel-config-assets-'))
554
+ const scratch = createScratch({ prefix: 'orkestrel-config-assets-' })
553
555
  try {
556
+ const workspace = scratch.path
554
557
  const source = resolve(workspace, 'entry.ts')
555
558
  const code =
556
559
  "const module = import('./module.js')\nconst asset = new URL('./asset%20name.png', import.meta.url)\nvoid module\nvoid asset\n"
@@ -573,7 +576,7 @@ describe('configuration helpers', () => {
573
576
  configHelpers.environmentAssetSources(readFileSync(source, 'utf8'), source, true),
574
577
  ).resolves.toStrictEqual([])
575
578
  } finally {
576
- rmSync(workspace, { recursive: true, force: true })
579
+ scratch.destroy()
577
580
  }
578
581
  })
579
582