refactor: make apps.json a source-only registry with one install path - #81
Merged
oto-macenauer-absa merged 1 commit intoSep 4, 2026
Merged
Conversation
oto-macenauer-absa
force-pushed
the
chore/source-only-registry
branch
from
September 4, 2026 13:33
d59d824 to
2e055e3
Compare
apps.json duplicated everything the docs repo already declared — slug, name,
description, icon, tags — and the duplication was not even resolved the way the
docs claimed. fetch-apps.js merged the manifest over the registry entry
("manifest wins"), build-vite.js discarded that result for packaged apps, and
Astro's loadRegistry read apps.json alone. So a catalog card showed whatever the
onboarding PR typed rather than what the repo published, and the `pages`
navigation manifest never reached getAppPages at all: the Array.isArray(app.pages)
branch in apps.js was dead code and every packaged app was filesystem-crawled
regardless of what its manifest said.
A registry entry now names a source and nothing else:
[{ "repo": "AbsaOSS/my-docs", "version": "latest" }]
Display fields are rejected rather than ignored. An onboarding PR that sets one
has a mistaken idea of where metadata lives, and silently dropping it would
leave the author waiting for a card that never changes.
One install path replaces two. stageEntry puts an artifact on disk — download,
copy, or pack a local checkout — and is the only place the three sources differ.
installArtifact then reads the manifest, validates it, copies one directory per
declared app into apps/{slug}/ and checks the headless marker, identically for
all of them. fetch-apps.js is now purely a downloader; src/utils/single-page.js
becomes src/utils/registry.js and generalises from bundles to every artifact.
Consequences worth naming:
* `pages` is honoured, and is authoritative when present — the directory is
not crawled, so nothing it happens to contain becomes a route the publisher
did not ask for.
* The headless-marker check runs for every source. It used to run only on the
GitHub path, so the prebuilt fixtures CI actually uses were never checked —
and the vendored one turned out to carry no marker at all.
* The single-page type is gone as a registry concept. A markdown bundle and a
docs site are both artifacts with a manifest; the reading-column layout is
inferred from the artifact (one HTML file, no `pages`) rather than declared.
* localPath runs a per-entry `pack` command (default `npm run pack:kb`)
instead of a hard-coded `npm run build:headless`, which could not run in the
Python example repo at all.
* KB_REGISTRY selects the registry file, so a deployment repo can own its own
list without forking this one.
* The artifact size budget from contract/ARTIFACT.md is enforced on download,
and archive members no app claims are reported rather than silently ignored.
Fixtures rebuilt for the v1 layout. The vendored artifact is now one
kb-docs.tar.gz whose manifest declares two apps: user-guide, crawled and
complete, and guide-mirror, which carries a `pages` list of four and omits a
fifth page that exists on disk. That asymmetry is what tests the feature, and it
drops the artifact from 6.5 MB to 4.2 MB by not duplicating the vendored CMS
bundle. Its HTML is now stamped with the headless marker, because a fixture that
does not meet the contract cannot verify it.
Suites green: 118 embedded (up 3 — the pages manifest had no coverage before),
22 standalone.
Part of #73. Closes #75.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PYtmxSeSXFg4wEXRcKCmAi
oto-macenauer-absa
force-pushed
the
chore/source-only-registry
branch
from
September 4, 2026 13:36
2e055e3 to
dfd5caf
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #75. Part of #73.
Stacked on #80 (
chore/kb-docs-contract), which is stacked on #79. Review in that order; this PR's base is #80's branch, so the diff here is only the registry work.The duplication was worse than "duplicated"
apps.jsonrestated everything the docs repo already declared. But the resolution never worked as documented:fetch-apps.jsmerged the manifest over the registry entry ("manifest wins"),build-vite.jsdiscarded that result for packaged apps, and Astro'sloadRegistryreadapps.jsonalone.Two consequences, both live on master:
pagesnever reachedgetAppPages. TheArray.isArray(app.pages)branch inapps.jswas dead code, and every packaged app was filesystem-crawled no matter what its manifest said.A registry entry now names a source and nothing else
[ { "repo": "AbsaOSS/my-docs", "version": "latest" }, { "localPath": "../my-docs" }, { "prebuilt": "tests/fixtures/my-docs.kb-docs.tar.gz" } ]Display fields are rejected, not ignored. An onboarding PR that sets one has a mistaken idea of where metadata lives, and silently dropping it would leave the author waiting for a card that never changes.
One install path replaces two
stageEntryputs an artifact on disk — download, copy, or pack a local checkout — and is the only place the three sources differ.installArtifactthen reads the manifest, validates it, copies one directory per declared app intoapps/{slug}/, and checks the headless marker, identically for all of them.fetch-apps.jsis now purely a downloader, andsrc/utils/single-page.jsbecomessrc/utils/registry.js, generalised from bundles to every artifact.Things that follow from having one path:
pagesworksprebuiltfixtures CI actually uses were never checkedpages) rather than declared in the registrylocalPathcan pack non-Node repospackcommand, defaultnpm run pack:kb, replacing a hard-codednpm run build:headlessthat could not run in the Python example repo at allKB_REGISTRYThe fixture now tests the feature
The vendored artifact is one
kb-docs.tar.gzwhose manifest declares two apps from one tree:user-guide— nopages, so crawled. Six routes, including the admin page the inline-script hoisting tests need.guide-mirror— apageslist of four, deliberately omittingdocs/some-new-page/index.html, which is present on disk.That asymmetry is the test: the same file is a route under one app and not under the other. It also drops the artifact from 6.5 MB to 4.2 MB, by not duplicating the 4.8 MB vendored CMS bundle into the mirror.
Its HTML is now stamped with
data-kb-headless="true". The old fixture had no marker anywhere — a gap nothing could catch, because nothing checked prebuilt artifacts. A fixture that does not meet the contract cannot verify the contract.Verification
npm test(embedded)playwright.config.ci.js(standalone)npm run build:headlessThe route list is the readable proof:
guide-mirroremits exactly its four manifest pages,user-guideemits all six crawled ones.Not run locally:
test:container(needs Docker) — CI's image job covers it.🤖 Generated with Claude Code
https://claude.ai/code/session_01PYtmxSeSXFg4wEXRcKCmAi