Skip to content

apps.json becomes a source-only registry; kb-docs.json is the source of truth for name, description, slug and pages #75

Description

@oto-macenauer-absa

Part of #73. Depends on #74.

Problem

apps.json duplicates everything the docs repo already declares:

{ "slug": "user-guide", "name": "User Guide", "description": "", "icon": "book-open", "tags": ["guide"], "repo": "AbsaOSS/knowledge-base-docs-example", "version": "latest" }

Worse, the duplication is not even resolved the way the docs say. scripts/fetch-apps.js merges marketplace.json over the registry entry ("manifest wins"), but scripts/build-vite.js throws that result away for packaged apps — only single-page expansions are recorded — and Astro's loadRegistry() reads apps.json alone. So for a packaged app:

  • the catalog card shows whatever the knowledge-base PR typed, not what the repo published;
  • marketplace.json's pages navigation manifest never reaches getAppPages(); the Array.isArray(app.pages) branch in src/utils/apps.js is dead code and every packaged app is filesystem-crawled.

Meanwhile the single-page type already has the right shape: the registry entry is { "repo", "type", "version" } and every display field comes from the artifact.

Proposal

apps.json says where an artifact comes from. kb-docs.json (#74) says what it is.

Registry entry (v2)

[
  { "repo": "AbsaOSS/knowledge-base-docs-example", "version": "latest" },   // GitHub Release asset kb-docs.tar.gz
  { "repo": "AbsaOSS/some-service", "version": "v2.1.0", "headless": false },
  { "prebuilt": "tests/fixtures/docs-example.kb-docs.tar.gz" },              // tarball or unpacked dir
  { "localPath": "../knowledge-base-docs-example", "optional": true },       // sibling checkout
  { "type": "iframe", "slug": "external-docs", "url": "https://…", "name": "", "description": "", "icon": "book-open", "tags": [], "temporary": true }
]

Allowed keys on an artifact entry: exactly one of repo / prebuilt / localPath; optional version (only with repo), headless, optional. No slug, name, description, icon, tags, entryPoint, type: "single-page" — the build fails with a message pointing at kb-docs.json if any of them is present, so the duplication cannot creep back through an onboarding PR.

iframe entries are the one exception: there is no artifact, so they keep their display fields and stay marked temporary.

One install path

Every artifact entry, whatever its source, goes through the same steps:

  1. obtain kb-docs.tar.gz (download from the release / copy the prebuilt path / run the repo's pack command in localPath mode);
  2. extract with the existing traversal/symlink guards (scripts/artifacts.js);
  3. read and validate kb-docs.json against the schema;
  4. for each app: copy <slug>/apps/{slug}/, verify entryPoint exists and carries the headless attribute;
  5. record the expanded apps in the expansion map, keyed by the entry's source (repo, prebuilt or localPath).

src/utils/single-page.js becomes src/utils/registry.js (or similar): bundleKey, expandBundle, readBundleManifest, resolveRegistry, assertUniqueSlugs generalise to every artifact entry rather than only type: "single-page". apps/.single-page.json becomes apps/.registry.json. The "manifest wins" merge and the TMP_DIR legacy mirror in fetch-apps.js go away.

localPath mode today runs npm run build:headless inside the checkout, which only works for Node repos (the example is Python/mkdocs). Replace with an explicit per-entry "pack": "bash scripts/pack.sh --headless" command (default: npm run pack:kb) that must leave a kb-docs.tar.gz in the checkout root; the result then takes the same path as prebuilt. Same behaviour, one fewer special case.

Astro side

loadRegistry() resolves every artifact entry from the expansion map, so getAppPages() finally sees pages and manifest-driven routing works. Rendering picks the reading-column layout when an app has exactly one page and no pages manifest (today keyed on type === 'single-page') — or, if that heuristic proves fragile, on an explicit kind field added to the contract.

Registry path is configurable

The deployment repo (#78) owns its own registry. Read the path from KB_REGISTRY (default apps.json), plumbed through src/utils/config.js like PATH_PREFIX and isHeadlessBuild(), and used by both scripts/build-vite.js and loadRegistry().

Scope

  • Registry v2 validation up front in build-vite.js (exactly one source, forbidden metadata keys, version only with repo, optional only with prebuilt/localPath)
  • One install path for repo / prebuilt / localPath; single-page special-casing removed from fetch-apps.js, build-vite.js, apps.js, [...path].astro
  • KB_REGISTRY in src/utils/config.js
  • Expansion map renamed and keyed by source; loadRegistry() cache stamp updated
  • pages navigation manifest actually honoured; test in build-integrity.spec.js that a fixture with pages produces exactly those routes and no crawled extras
  • apps.json in this repo trimmed to source-only entries; committed fixtures regenerated
  • Fetch error messages updated: missing asset names kb-docs.tar.gz; missing/invalid manifest links contract/ARTIFACT.md
  • Docs: README.md registry section, CLAUDE.md / AGENTS.md, contract/*.md "ask for the registry entry" sections show the two-line entry

Non-goals

Renaming data-mp-headless and friends is #77; the packaging action is #76.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

dependentThe item depends on some other open item (Issue or PR)enhancementNew feature or requestrefactoringImproving code quality, paying off tech debt, aligning APIs

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions