@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.
- package/dist/host/CLAUDE.md +2 -0
- package/dist/host/agents/orchestration.md +68 -27
- package/dist/host/agents/skills/orkestrel-debrief/SKILL.md +7 -2
- package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +3 -0
- package/dist/host/agents/skills/orkestrel-debrief/references/instruction-audit.md +6 -0
- package/dist/host/claude/agents/analyst.md +6 -3
- package/dist/host/claude/agents/checker.md +1 -1
- package/dist/host/claude/agents/codex.md +13 -9
- package/dist/host/claude/agents/grok.md +3 -2
- package/dist/host/claude/agents/orkestrel.md +43 -36
- package/dist/host/claude/agents/planner.md +2 -1
- package/dist/host/claude/agents/reviewer.md +15 -14
- package/dist/host/claude/agents/sol.md +4 -2
- package/dist/host/claude/rules/quality.md +2 -1
- package/dist/host/claude/rules/tests.md +1 -0
- package/dist/host/codex/agents/analyst.toml +5 -5
- package/dist/host/codex/agents/claude.toml +34 -0
- package/dist/host/codex/agents/grok.toml +3 -3
- package/dist/host/codex/agents/opus.toml +16 -20
- package/dist/host/codex/agents/planner.toml +12 -17
- package/dist/host/codex/agents/reviewer.toml +13 -18
- package/dist/host/guides/guide.md +193 -98
- package/dist/host/manifest.json +6 -1
- package/dist/host/tests/config.test.ts +10 -7
- package/dist/src/core/index.cjs +9 -9
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.js +9 -9
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +32 -10
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.js +32 -10
- package/dist/src/server/index.js.map +1 -1
- 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
|
|
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`](
|
|
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,
|
|
14
|
-
links,
|
|
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.
|
|
18
|
-
|
|
19
|
-
|
|
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`](
|
|
28
|
-
|
|
29
|
-
| Name
|
|
30
|
-
|
|
|
31
|
-
| `ExportKind`
|
|
32
|
-
| `SurfaceSymbol`
|
|
33
|
-
| `GuideModule`
|
|
34
|
-
| `SourceLine`
|
|
35
|
-
| `ManifestEntry`
|
|
36
|
-
| `MethodGroup`
|
|
37
|
-
| `
|
|
38
|
-
| `
|
|
39
|
-
| `
|
|
40
|
-
| `
|
|
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`](
|
|
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`](
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
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 scope — its 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
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
| Name
|
|
85
|
-
|
|
|
86
|
-
| `
|
|
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`](
|
|
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`](
|
|
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`](
|
|
138
|
+
From [`factories.ts`](../src/core/factories.ts).
|
|
129
139
|
|
|
130
|
-
| Name | Kind | Signature
|
|
131
|
-
| ----------------------------- | -------- |
|
|
132
|
-
| `createGuide` | function | `(source: string) => GuideInterface`
|
|
133
|
-
| `createSource` | function | `(options: SourceOptions) => SourceInterface`
|
|
134
|
-
| `
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
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`](
|
|
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`](
|
|
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
|
-
| `
|
|
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[]` |
|
|
183
|
-
| `surface` | `readonly SurfaceSymbol[]` |
|
|
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
|
-
`
|
|
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 `
|
|
204
|
-
fenced
|
|
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
|
|
308
|
-
an immediately preceding eligible leading JSDoc chain whose final
|
|
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
|
-
|
|
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`](
|
|
410
|
-
- [`tests/src/core/parsers.test.ts`](
|
|
411
|
-
- [`tests/src/core/validators.test.ts`](
|
|
412
|
-
- [`tests/src/core/shapers.test.ts`](
|
|
413
|
-
- [`tests/src/core/factories.test.ts`](
|
|
414
|
-
- [`tests/src/core/Guide.test.ts`](
|
|
415
|
-
- [`tests/src/core/Source.test.ts`](
|
|
416
|
-
- [`tests/
|
|
417
|
-
- [`tests/
|
|
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`](
|
|
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.
|
package/dist/host/manifest.json
CHANGED
|
@@ -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": "
|
|
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
|
|
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
|
-
|
|
511
|
+
scratch.destroy()
|
|
511
512
|
}
|
|
512
513
|
})
|
|
513
514
|
|
|
514
515
|
it('reads bounded files and resolves package roots from real manifests', () => {
|
|
515
|
-
const
|
|
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
|
-
|
|
549
|
+
scratch.destroy()
|
|
548
550
|
}
|
|
549
551
|
})
|
|
550
552
|
|
|
551
553
|
it('classifies module boundaries and extracts static asset sources', async () => {
|
|
552
|
-
const
|
|
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
|
-
|
|
579
|
+
scratch.destroy()
|
|
577
580
|
}
|
|
578
581
|
})
|
|
579
582
|
|