Skip to content

os dev over a HOST config composes two registrars for stack-declared security metadata — AppPlugin over the config module and the dev-only HMR MetadataPlugin over dist/objectstack.json #14397

Description

@os-trump

Found while implementing #12892 step 2 (dev seat, session session_016yfqQh2dBgPAymYd7xipza, branch claude/issue-12892-artifact-boot-single-registrar). Filed unassigned for triage; #12892 is not addressed by this card and this card is not addressed by that PR — that PR unifies the ARTIFACT boot (createStandaloneStack), and this is a different boot shape with the same two-writer class.

Mechanism (read from source on origin/main 1dcb995f23; not driven)

packages/cli/src/commands/serve.ts, boot-mode dispatch: shouldBootWithLibrary(config) (packages/cli/src/utils/plugin-detection.ts) returns false for a HOST config — one whose plugins[] holds instantiated plugins (isHostConfig) — so the standalone stack, and with it the artifact door, is never composed. Then:

  1. serve step 3 (around line 2811): when the host config ALSO carries top-level metadata and brings no AppPlugin of its own, new AppPlugin(config) wraps the loaded module. Its ADR-0057 block registers positions / permissions / capabilities / sharingRules into the metadata service from the MODULE — forward-converted, not strict-parsed, no schema defaults, no ADR-0010 provenance (the default 'app-plugin' registrar).
  2. the dev-only metadata-HMR block (around line 3024): under isDev && flags.server && !hasMetadataPlugin, when resolveDefaultArtifactPath(readInternalArtifactPath()) resolves — i.e. when the os dev supervisor has compiled dist/objectstack.json — a MetadataPlugin({ watch: false, artifactWatch: true, artifactSource: { mode: 'local-file', path } }) is composed. That is the artifact door: it strict-parses, forward-converts and ADR-0010-stamps the COMPILED TWIN of the same stack and registers all four security collections too.

⇒ On that boot the same declarations reach the metadata service through two writers over two SOURCES of one stack (the module and its compiled artifact); the copies differ on the parse axis exactly as measured on #12892 (a sharing rule's condition is a bare string on the module copy and { dialect, source } on the door's; a capability has no scope default and no _packageVersion on the module copy). Which copy a consumer reads depends on start order. The HMR door additionally RELOADS its copy on every recompile while the module copy never refreshes, so the winner can change mid-run.

Not measured here — the two readings a triage needs

  • Whether os dev compiles a host config into dist/objectstack.json at all (if it never does, hmrArtifactPath never resolves and the second door is never composed — the path would be dead rather than double).
  • Which copy wins on this boot. The HMR MetadataPlugin is kernel.used after the app wrap, so positionally the door's copy would land last (the opposite of the artifact boot, where AppPlugin won) — an inference from list order, not a measurement.

Options as this seat sees them (none prejudged)

  1. serve.ts knows both facts in one function — when it composes the HMR door over the compiled twin of the config it just wrapped, construct that wrap with securityMetadataRegistrar: 'artifact-door' (the option Route ownership for the five artifact security collections — MEASURED: the two registrars' copies differ in TYPE on a key a consumer can read today, not just on some future retired key #12892 step 2 adds to AppPlugin). One route, one owner, on this boot shape too; costs a serve.ts edit and a measurement of the two readings above.
  2. Make the dev-only HMR door register NOTHING under the four security kinds (a door option) — engine lane, and it inverts the ownership the maintainer chose for the artifact boot.
  3. Record it as dev-only and accept it; then the class Route ownership for the five artifact security collections — MEASURED: the two registrars' copies differ in TYPE on a key a consumer can read today, not just on some future retired key #12892 exists to name survives on the objectos dev-workspace shape (serve.ts names apps/objectos's dev-workspace stack as the host-config case).

Re-check

git grep -n "hasMetadataPlugin\|shouldBootWithLibrary\|hasAppPluginAlready" -- packages/cli/src/commands/serve.ts
git grep -n "export function isHostConfig\|export function shouldBootWithLibrary" -- packages/cli/src/utils/plugin-detection.ts

Duplicate check

search_issues, one targeted query: 13 hits, none covering this boot shape; the control fired (the parent class card #12844 came back at rank 3, so the query reached the right corpus).

Refs: #12892 (the artifact-boot half — step 2 landing as its PR), #12844, #7049.

Generated by Claude Code

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions