Conversation
- Resolve the full public surface from the TypeScript compiler API (ts-morph) across all secondary entry-point barrels, emitting a deterministic .storybook/public/llms-full.txt + deprecations.json — 1002 entries / 711 inputs, vs 214 inputs under the closed Compodoc-1.1.16 attempt (#5167), 0 unmatched. - Wire docs:llms into build-storybook, add generate+smoke+coverage gates to build.yml, and a node-env `api-docs` vitest project (33 tests, byte-deterministic). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s and index - extract-stories.mjs: static ts-morph extraction of stories/documentation (620 stories, 1159 argTypes descriptions, both Angular and HTML&CSS arms) - generate-llms.mjs: documents @lucca/prisme alongside @lucca-front/ng, emits llms.txt index + one llms/<slug>.md per entry point and per story category, appends the stories corpus to llms-full.txt - check-llms-surface.mjs: feed-count consistency gate (every generated feed linked from llms.txt), floors per feed kind, internal-host guard
Referenced by no workflow or script; the Storybook builder's own compodoc integration (angular.json) is untouched.
|
🚀 Storybook preview deployed: https://pub-dc6d99acd6874e2aaff6219dd8a13ae2.r2.dev/PR-5224/index.html |
There was a problem hiding this comment.
Pull request overview
Adds a generated, Storybook-hosted LLM documentation surface for public APIs and stories.
Changes:
- Extracts API and Storybook metadata with ts-morph.
- Generates full, indexed, windowed, and deprecation feeds.
- Adds Vitest coverage and CI validation gates.
Reviewed changes
Copilot reviewed 12 out of 14 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/build.yml |
Runs documentation validation in CI. |
.gitignore |
Ignores generated feeds. |
package.json |
Adds scripts and ts-morph. |
package-lock.json |
Locks the new dependency. |
vitest.config.ts |
Adds the API-docs test project. |
tsconfig.compodoc-html.json |
Removes obsolete Compodoc configuration. |
scripts/api-docs/extract-api.mjs |
Extracts public TypeScript APIs. |
scripts/api-docs/extract-api.spec.mjs |
Tests API extraction. |
scripts/api-docs/extract-stories.mjs |
Extracts documented stories. |
scripts/api-docs/extract-stories.spec.mjs |
Tests story extraction. |
scripts/api-docs/generate-llms.mjs |
Generates documentation feeds. |
scripts/api-docs/generate-llms.spec.mjs |
Tests feed rendering. |
scripts/api-docs/check-llms-surface.mjs |
Smoke-tests generated artifacts. |
scripts/api-docs/check-api-coverage.mjs |
Enforces JSDoc coverage. |
Suppressed comments (3)
scripts/api-docs/extract-api.mjs:179
- The generated input name must use Angular's public alias, not the backing property name. The current output documents
luTooltipInputalthough consumers bind[luTooltip](packages/ng/tooltip/trigger/tooltip-trigger.directive.ts:68), and the repository contains several other aliased signal inputs. Read thealiasoption forinput,input.required, andmodel.
name: prop.getName(),
scripts/api-docs/extract-api.mjs:175
- Angular
model()declares both an input and an implicit<name>Changeoutput, but this branch records only the input. Consequently APIs such asIndexTableRowComponent.selectedChange(packages/ng/index-table/index-table-row/index-table-row.component.ts:52) disappear from the promised output surface. Add the corresponding output, including alias handling, whenever the callee ismodelormodel.required.
if (INPUT_CALLEES.has(callee)) {
scripts/api-docs/extract-api.mjs:169
getProperties()only yields declarations on this class, so inherited Angular inputs/outputs are omitted. For example,DurationPickerComponentextendsBasePickerComponent, whose publicstep,size, anddisabledmodel (packages/ng/time/core/base-picker.component.ts:15-19) are part of the derived component API but will not be rendered. Traverse base classes and merge inherited members while allowing derived overrides.
for (const prop of classNode.getProperties()) {
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…y stories, exact gates - extract decorator-based @Input/@output (properties and setters, aliases), EventEmitter payloads and outputFromObservable() - publish overload signatures instead of the implementation signature - keep stories built by non-test factory calls (generateStory) and resolve shorthand template properties - coverage gate compares the exact ratio, not the rounded display value - deprecations.json declares both documented packages
- generate-package-llms.mjs writes dist/ng and dist/prisme llms-full.txt after the ng-packagr build (publish.yml ships the whole dist folder) - committed guardrail: the build fails when a package's extraction collapses below its floor or its dist folder is missing - agents read the doc from node_modules at the exact installed version (npm channel of the doc-for-LLM epic, same shape as lu-swag#260)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 15 changed files in this pull request and generated no new comments.
Suppressed comments (7)
scripts/api-docs/extract-api.mjs:241
- Signal input aliases are not applied here, so the generated binding names are wrong. For example,
LinkComponent.luHref = input('', { alias: 'href' })is emitted asluHref, and the same happens forluLink,luTooltip, and other aliased signals. Parse the signal options forinput,input.required, andmodeland emit the configured alias as the public name.
name: prop.getName(),
scripts/api-docs/extract-stories.mjs:237
- The renderer never emits
story.name, and stories without described arg types are skipped entirely by this loop. Files such assimple-select.stories.tscontain many distinct variants (Basic,Minimal,WithDisplayer, etc.); their templates are currently dumped together under one file heading with no indication of which story they belong to. Preserve template/description data per story and render a heading for every extracted story so the promised story corpus remains identifiable.
for (const story of file.stories) {
scripts/api-docs/extract-api.mjs:237
model()andmodel.required()enter this input-only branch, but an Angular model also exposes an output named<modelName>Change. Consequently components such asCalendar2ComponentlosedateChange,modeChange, and the other model outputs from the advertised full API. Add the corresponding output entry (using the model alias when present).
This issue also appears on line 241 of the same file.
if (INPUT_CALLEES.has(callee)) {
scripts/api-docs/extract-api.mjs:373
- The entity is built only from members declared directly on the concrete class. Angular components inherit inputs and outputs: for example,
LuSimpleSelectInputComponentextendsALuSelectInputComponent, whoseplaceholder,clearable,panelClosed, and many other bindings are part of the usable component API, but this feed emits only the subclass's three own inputs. Traverse base classes and merge inherited Angular members and public methods, with derived declarations taking precedence.
return { ...base, selector: selectorOf(node), ...membersOf(node), methodsClass: methodsOf(node) };
scripts/api-docs/generate-llms.mjs:329
- The method signature is placed in a Markdown table without escaping pipes in argument types. Public methods such as
callCanDeactivateFn(canDeactivateFn: CanDeactivateFn<C> | DeprecatedGuard)therefore add an unintended table column and corrupt the generated feed. Escape|in the complete signature before writing the cell.
const args = (m.args || []).map((a) => `${a.name}: ${a.type || 'unknown'}`).join(', ');
lines.push(`| \`${m.name}(${args})\` | ${typeCell(m.returnType)} | ${cleanCell(m.rawdescription || m.description)} |`);
scripts/api-docs/extract-stories.mjs:183
- For helper-built stories,
storyObjectis thegenerateStory({...})configuration, while the actual StorybookargTypesare understoryPartial.argTypes. CallingargTypesOfonly at the top level drops those descriptions; for example,simple-select.stories.tsdocumentsAddOption'saddOptionLabelandaddOptionStrategythere, but neither reaches the feed. Resolve the helper'sstoryPartialobject and merge its arg types with the Meta defaults.
This issue also appears on line 237 of the same file.
const own = argTypesOf(storyObject);
stories.push({ name: varDecl.getName(), argTypes: own.length ? own : metaArgTypes });
scripts/api-docs/generate-llms.mjs:52
- Hard-coding
mastermeans every versioned deployment's index crosses versions: following a link fromv21.3/storybook/llms.txtloads the master API/story feed rather than v21.3. This contradicts the PR's per-ref documentation contract and can give agents APIs unavailable in the selected release. Use URLs relative to the current Storybook directory, or inject the deployed ref when generating the index.
export const CANONICAL_BASE_URL = 'https://lucca-front.lucca.io/master/storybook';
Resolve the package.json/package-lock.json conflict that made Jenkins pr-merge unbuildable: keep master's storybook ~10.5.3 and stylelint ^17.14.1 bumps, keep this branch's ts-morph ~28.0.0, and regenerate the lockfile from master's baseline. Verified after the merge: npm run docs:api renders 1066 public exports and passes all 11 llms-surface smoke gates. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dbd3kZcm4ms8MAAVH97BQ2
- deprecations.json carries the import paths of each deprecated symbol: the path, not the bare name, is the discriminant — 3 of 103 entries surface through two entry points, so a consumer keyed on the name alone cannot say which import to flag - the llms smoke gate pins 18 feed anchors by name: index and feeds come out of the same extraction, so a single lost family stays above every numeric floor (measured: dropping ng-icon keeps 119/80 feeds and the link check green) - the npm channel step packs instead of trusting the write: a `files` allowlist or .npmignore added upstream drops the feed at publish time with the build still green (measured on dist/ng, 230 files, feed absent) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TN1HaKioHi3vwVHDyeUafV
- Add `renderPackageIndex()`: a per-package llmstxt.org index written next to the
corpus in `dist/{ng,prisme}`, so an agent that finds `node_modules/@lucca-front/ng/
llms.txt` gets the entry-point list and a relative `./llms-full.txt` pointer at the
exact installed version. The split per-entry-point feeds stay out of the tarball —
they would duplicate ~300 KB of the same bytes for no new information.
- Assert the tarball per file instead of on `llms-full.txt` alone: a `files` allowlist
naming only the corpus dropped the index with the build still green (proof: exit 1
once asserted, exit 0 restored).
- Cost measured: +11 KB on @lucca-front/ng (9.9 MB), +1.5 KB on @lucca/prisme.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TN1HaKioHi3vwVHDyeUafV
The alias was resolved for the decorator forms only. `input()`, `model()`,
`output()` and `outputFromObservable()` published `prop.getName()`, so the
corpus named members that do not exist in the template API — an agent reading
the feed wrote `(closeOutput)` where the component accepts `(close)`.
- resolve `{ alias }` for every signal factory, per-form options position
- reuse the same reader in `decoratorConfig`
Measured on the library: 66 declarations carry an alias on a signal factory
(53 inputs, 13 `outputFromObservable`); before this, 61 leaked their property
name into `llms-full.txt`. `LuDatePickerComponent` now publishes `close`,
`open`, `hovered`, `onSelectValue` instead of the `*Output` property names.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dbd3kZcm4ms8MAAVH97BQ2
…ping The extractor read each class in isolation and only its own declared members, so four public forms never reached the generated feeds: - inherited bindings — `DateInputComponent extends AbstractDateComponent` lost `format`, `ranges`, `hideToday`, `calendarMode`; the base chain is now walked, most-derived first, and a derived declaration overrides the inherited one; - `model()` published its input but not the implicit `<publicName>Change` output, so `Calendar2Component.dateChange` existed nowhere. Built on the alias, not the property name, and typed from the model's value type; - `hostDirectives` forwarded bindings — `LuDropdownTriggerDirective` publishes `luDropdownPosition` and friends and none could reach the arrays. Only the listed bindings are forwarded, and `'name: alias'` publishes the alias; - interface method signatures were discarded by `propertiesOf` alone, so `ILuPopupRef` documented its observables and not `open`/`close`/`dismiss`. Measured on the generated corpus: `DateInputComponent` goes from its own inputs to 47, 64 `<name>Change` outputs appear, and `ILuPopupRef` gains its three methods. Addresses PR review comments on #5224 (threads on extract-api.mjs:220/225/264/354 and two suppressed findings on the same class of defect). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AHWgqrmwb1XiAPqZxY2sHy
Four defects in the generated output, none of them visible from the extraction: - a method argument was written into a Markdown table without escaping `|`, so `callCanDeactivateFn(canDeactivateFn: CanDeactivateFn<C> | DeprecatedGuard)` opened an extra column and corrupted the row. `typeCell` escaped the return type but nothing escaped the arguments; the whole class is swept — the enum value cell went through `defaultCell` too; - the interface renderer had no Methods table, so the signatures the extractor now produces had nowhere to land. The class and interface renderers share one `methodsTable`, and `### Properties` only appears when a Methods table follows it; - the deployed `llms.txt` hard-wired `https://lucca-front.lucca.io/master/storybook`, so opening `v21.3/storybook/llms.txt` or a PR preview sent every feed request to master — an agent could be handed APIs absent from the release it selected. The index links relatively; all four artifacts sit in `.storybook/public/`, so `.` resolves. `CANONICAL_BASE_URL` stays for the npm packages' fallbacks, which are explicitly labelled as tracking master; - the feed directory was recreated but never cleared, so a removed or renamed entry point left its `.md` behind and `build-storybook` — which runs the generator without the smoke check — copied it into the deployment. Purged first. Addresses PR review comments on #5224 (threads on generate-llms.mjs:819/839, the interface half of the extract-api:354 thread, and two suppressed findings). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AHWgqrmwb1XiAPqZxY2sHy
…as HTML
Three defects in the stories feed:
- a `TemplateExpression` was returned as raw source and then fenced as `html`,
so `button-basic.stories.ts` published `${luButton !== ...}` and
`${generateInputs(...)}` as if a consumer could paste it. Statically resolving
those interpolations would mean running the helpers, so the honest fix is to
label the block for what it is: an interpolated template fences as `ts`.
Measured on the regenerated corpus: 0 occurrences of `${` remain inside an
`html` fence;
- stories were not named in the output — a file's templates were dumped together
under one heading, and a story with no described argTypes was skipped entirely,
so `simple-select.stories.ts`'s `Basic`/`Minimal`/`WithDisplayer` were
indistinguishable. A `**Stories:**` line lists them all, and each argTypes table
is headed by the stories that share it (the dedup is kept, it is now attributed);
- for helper-built stories the real Storybook config sits under `storyPartial`,
so `argTypesOf` at the top level dropped `AddOption`'s `addOptionLabel` and
`addOptionStrategy`. Meta, own and `storyPartial` argTypes now merge by prop
name, later winning — which is how Storybook resolves them.
Addresses PR review comments on #5224 (thread on extract-stories.mjs:47 and two
suppressed findings on :183 and :237).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AHWgqrmwb1XiAPqZxY2sHy
The gate was described as a ratchet but measured coverage at 102/1066 (9.6%) against a 5% floor: half the documented exports could be deleted and CI stayed green. That is the regression the gate exists to catch. Pinning the floor at the current ratio instead would fail any PR that adds undocumented exports — not a regression in what is documented, and exactly the "blocking unrelated PRs" the original note was avoiding. So the gate ratchets the documented COUNT: it fails when a public export loses its JSDoc description, and is indifferent to the surface growing. The percentage is still reported, and a run above the baseline prints the value to raise MIN_DOCUMENTED to. Verified both paths: 102 passes, and a baseline of 103 fails with "1 public export(s) lost their JSDoc description since the baseline". Addresses the PR review comment on #5224 (check-api-coverage.mjs:19). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AHWgqrmwb1XiAPqZxY2sHy
…actor sees - split a story title at its `Angular`/`HTML&CSS` arm instead of a fixed position: 22 nested components (`Forms/Fields/CheckboxField`) stopped collapsing onto their parent, 129 → 147 component groups over 636 files - a transformed input documents its write type (`ButtonComponent.block` is a boolean-attribute union, not `boolean`), and an interface publishes the members it inherits, the nearest declaration winning - a generic class declares its type parameters, and a template-literal type gets a doubled fence so its backticks no longer break the table cell Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WNHjonxsYvpdAXQfXbydYJ
Four findings Copilot folded into its suppressed block, none of which carried a thread — the same defect family as the visible ones: the feed published what the extractor happened to walk, not what a consumer may call. - a public static method is as callable as an instance one: `PhoneNumberFormatter.format` and `PhoneNumberValidators.validPhoneNumber` rendered as empty sections, they now read as `static format(…)` - a class publishes its public instance properties and getters, minus the ones already listed as inputs or outputs (`LuTitleStrategy.title$`, `ALuPopupRef.onOpen`) - a method declares its own type parameters (`LuPopup.open<T, D, R>`), on classes and interfaces alike — the signature named `D` and `R` without defining them - an `outputFromObservable` with an inferred payload falls back to the resolved `OutputEmitterRef<T>`: `LuDropdownItemDirective.onSelect` is `boolean`, not `unknown` llms-full.txt grows 1094 → 1223 KB; coverage, pack and smoke gates stay green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WNHjonxsYvpdAXQfXbydYJ
Self-review of the previous commit, on the regenerated feed: opening the class surface let two kinds of non-API in. - an aliased signal input was published twice — once under its alias, once as the raw declaration (`addOptionLabelInput: InputSignal<…>`). The alias means the declaration name never matches the published one, so the factory call is what identifies a binding, not its name - `ngTemplateContextGuard` read as a callable static on 16 directives; it is a template type-check contract nobody calls, filtered next to the lifecycle hooks - an `_`-prefixed property is internal by convention here, and stays out Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WNHjonxsYvpdAXQfXbydYJ
Copilot's third round, plus one finding of my own in the same family. - two distinct exports sharing a name kept only the first: `LinkComponent` and `ALuSelectInputComponent` each lost one of their two APIs. Merge and selection are keyed on the declaration now, and a collision states its import path under the heading — 1083 declarations from 1066 exported names, 17 recovered - an unresolved public export vanished from every corpus while the entry floors stayed green; generation fails on it instead - the internal-host gate scanned llms.txt alone. It now scans all 139 generated files: proven by planting `dd.lucca.tech` in a windowed feed — exit 1, exit 0 once regenerated - a pipe publishes the name a template pipes through (`value | luDate`), and a class or a service its public constructor (`new LuStringDateAdapter(_locale)`) - mine, same family as the pipe name: `exportAs` was dropped on 24 directives, so `#ref="luDropdownTrigger"` could not be derived from the feed The ratchet locks the gain in: MIN_DOCUMENTED 102 → 105, as the gate itself asks. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WNHjonxsYvpdAXQfXbydYJ
…enerates it Twelve findings from an xhigh review of the whole surface. Two were live in the published artifacts, two crashed on demand, and the rest were guards the code was missing on paths it already walks elsewhere. Live: - a deprecation's import paths resolved by bare name, so `ALuSelectInputComponent .grouping` — deprecated only on the core-select class — was published as reachable from `@lucca-front/ng/select` too, whose class has no such member. Resolution goes through the declaration now; the three genuine re-exports keep both of their paths - the constructor fence reused the table's `|` escaping, so `new LuDialogRef(… LuDialogResult<C> \| typeof DISMISSED_VALUE …)` did not compile for anyone who copied it. 0 escaped pipes left in a ```ts fence Crashes, both reproduced by a test before the fix: - a hostDirectives cycle recursed until the stack gave out — the guard classChain and interfaceChain already carry - an exported name merged from an interface and a function called `getParameters` on the interface: `TypeError`, killing `npm run build` Gates and blast radius: - `assertFullyResolved` threw inside `generateAll`, and `build-storybook` runs it — an `export * as ns` would have taken the Storybook deploy down for a docs gap. It is strict in `docs:api` (CI, every push) and a warning elsewhere: measured with a real namespace export, exit 1 strict, exit 0 with a warning otherwise - two entry points flattening to one slug would have overwritten a feed with every floor still green — refused now - the smoke gate read `deprecations.json` outside its own try/catch, turning a missing artifact into a raw ENOENT stack - an argTypes table keyed on prop names alone would attribute one story's prose to another; keyed on the whole descriptor now (0 of 637 files hit it today) - `docs:llms-pack` paid the 11 s extraction before the dist check that is the likely failure Lint, and the reason none of this was caught: - `lint:es` covered `packages/` only, and every eslint block targets `**/*.ts`, so the ~2400 lines under `scripts/api-docs/` shipped unlinted — an unused const there reports 0 problems, the same line under `packages/` reports 1. Both scopes are linted now, and the tree is clean under the base JS rules - the compodoc-shaped fallbacks the extractor never fills are gone: 12 shims removed, `llms-full.txt` byte-identical before and after (05e0e370a1e575b2) Not taken, with the measurement: the double extraction is real but costs 11 s per run, not minutes; and `cell`/`cleanCell` plus the two directory walkers diverge in no file of the current corpus, while sharing them needs a new module. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WNHjonxsYvpdAXQfXbydYJ
Warning
Experimental — under test. The feeds and their format may change or be withdrawn without a deprecation cycle.
Description
Ships an LLM-consumable documentation surface with every Storybook deploy.
Coding agents (Copilot, Claude, CI bots) can read the whole public API of
@lucca-front/ngand@lucca/prisme, plus every documented story, fromhttps://lucca-front.lucca.io/<ref>/storybook/llms.txt— unauthenticated, versioned per ref, zero new infrastructure.llms.txtindex,llms-full.txt(API + stories),deprecations.json, plus 136 windowed per-entry-point and per-category feedsllms.txtproxiesllms-api.txtbeside it, plusllms-stories.txton ng — read fromnode_modulesat the installed versionImpact
staticDirs; tarballs grow +161 KB gz (ng), +7 KB (prisme)Evidence — gates output and shipped files
Evidence — every gate verified by breaking it
Post-deploy proof:
curl -I https://lucca-front.lucca.io/master/storybook/llms-full.txtreturns 200 once the next master deploy runs (it 404s today).Technical notes for reviewers
input()/output()(feat(docs): single LLM documentation surface via Compodoc #5167 was closed for that); ts-morph reads them straight from source. Output is deterministic — identical source yields byte-identical files.@lucca/prismeshares the ng topology in-repo (secondary entry points withpublic-api.tsbarrels); documenting it also resolves the@lucca-front/ngbutton/icon re-exports cleanly.title,template:literals (following one level of in-file indirection — theTemplate/getTemplateandcleanupTemplate(...)idioms), story export names,argTypes.*.description;createTestStorywrappers skipped. 28/636 files carry no static template (component-rendered) — they still contribute titles and prop descriptions.deprecations.jsonresolves by declaration, not by name. 3 of its 126 entries reach several entry points (both tooltip barrels, both button barrels); where two classes share a name, only the barrel declaring the deprecated one is listed, so a lint rule keyed on the import path flags the right barrel.llms/folder are produced by the same extraction, so a lost entry point vanishes from both and a count floor of 80 against ~120 feeds lets it through. The 18 anchors cover one feed per package, per nesting depth and per story-category arm. The packaged stories corpus carries the same discipline: 4 named component anchors, because a floor of 400 against 636 story files also lets a whole family through. Add an anchor when a family becomes load-bearing; never trim the list to make the gate pass.filesallowlist or.npmignoreintroduced upstream drops a feed at publish time with the build still green — onlynpm packsees the published file list (~0.9 s per package). An allowlist namingllms-api.txtalone would drop the proxy and the stories, so all three names are checked.llms.txtproxies, it never copies. In the tarball it lists the entry points and relatively links./llms-api.txtand./llms-stories.txt; the 136 split feeds stay on the deploy, since shipping them would duplicate ~300 KB of the same bytes. The split is what makes the corpora usable: an agent after a signature reads 288 KB (23 KB on prisme), never the 722 KB of templates. And the link must stay relative — an absolute docs URL answers formasterwhile the package is pinned, the version leak this channel exists to close.@lucca-front/ngcarries the stories;@lucca/prismeopts out. 388 of the 636 story files import neither package (they document the CSS design system), so splitting the corpus per package by import would drop 61% of the examples from both, so ng carries it whole (+134 KB gz, +11%). That corpus meant +94% on a 90 KB package, so prisme ships its API alone (+7 KB gz) and its proxy links the stories on the deploy — which tracksmasterwhile the package is pinned, so the version guarantee holds for prisme's API, not its examples. FlipstoriesinPACK_TARGETSto undo.@lucca/docs-kit. The kit's bundler covers the tag-sourced Starlight pipeline (Pagga.Front, lu-swag): itsfiles-allowlist guard is a no-op here and its archived-snapshot guard has no subject (the Storybook is versioned per deployment, not by tag). lucca-front carries no external@lucca/*dependency — both of its own are in-repo.lucca docsis a parallel channel, not a substitute. It serves authenticated devs fleet-wide from an Astro site published by abuild.cakePublishAstroDocstask; lucca-front has none, is absent from its registry, and per LuccaSA/Guildes#75 the DD arm is 401-gated.build-compodocnpm script and itstsconfig.compodoc-html.json(referenced by no workflow or script; the Storybook builder's own compodoc integration inangular.jsonis untouched).🤖 Generated with Claude Code
https://claude.ai/code/session_01TN1HaKioHi3vwVHDyeUafV