Skip to content

Manifest-owned extensions

Every installed pack contributes through the same manifest contract. Generic consumers discover the pack from the compiled registry and dispatch its tools through the governed runtime. The CLI uses the generic tool:execute path for pack tools; a dedicated convenience command still requires an explicit CLI adapter.

id: acme-audit
version: 1.0.0
task_types: [audit]

config_key: acme_audit

tools:
  - name: audit:scan
    entry: tools/scan.js#scan
    permission: read
    required_scopes:
      - type: path
        pattern: reports/**
        access: read

policies:
  - id: acme-audit.scan.allow
    trigger: on_tool_request
    decision: allow

evidence_types: [acme-audit.scan-report]
state_aliases: {}
lane_templates: []

emitted_event_kinds:
  - acme-audit:scan_completed
subscribed_event_kinds: []
required_approvals: []
surfaces_required: [cli, http, mcp]

extensions:
  command_namespaces:
    - audit
  state_namespaces:
    - acme-audit:runs
  telemetry_sources:
    - id: acme-audit:scan-results
      path: .lumenflow/telemetry/acme-audit.ndjson
      mapper: telemetry-mappers.js#mapScanResult
      default_policy: metadata-only

The ordinary manifest fields and extensions block are compiled together:

| Ownership surface | Declaration | | ------------------- | -------------------------------------------------------------- | | Commands and tools | tools[].name, constrained by extensions.command_namespaces | | Configuration | config_key and optional config_schema | | Events | emitted_event_kinds | | State | extensions.state_namespaces | | Policies | policies; policy_factory is validated separately at boot | | Transport discovery | surfaces_required | | Telemetry | extensions.telemetry_sources |

Pre-v6 manifests that omit extensions remain loadable. The registry infers their command namespaces from tools[].name and treats state-namespace and telemetry-source ownership as empty. Inferred namespaces still receive the same reserved-name and cross-pack collision checks, so the compatibility path cannot weaken registry isolation.

Add an explicit extensions block when migrating a pack to v6. It is required to declare pack-owned state or telemetry and is the clearest auditable statement of command ownership. A safe migration is:

  1. list each distinct prefix used by tools[].name under command_namespaces;
  2. declare every pack-owned state namespace under state_namespaces;
  3. move each pack-owned telemetry input into telemetry_sources; and
  4. validate the pack together with every other installed pack so inferred or explicit namespaces cannot collide.
  • Command namespaces are lowercase slugs. A tool must use a namespace declared by its own pack.
  • Emitted events, state namespaces, and telemetry source IDs use <pack-id>:<owned-name>.
  • approval, fs, proc, task, and tool are kernel-reserved command namespaces. kernel: is reserved for kernel state.
  • Configuration roots, command names, state namespaces, event kinds, tool names, policy IDs, telemetry IDs, and telemetry paths cannot collide across installed packs.
  • Subscribed event kinds may reference another pack because cross-pack subscriptions are explicit.
  • Required surfaces are selected from cli, http, mcp, sidekick-channel, and scheduler.

Telemetry declarations are metadata. The generic synchronizer owns cursors, batching, retry, and cloud delivery; the pack owns source IDs, paths, mapping logic, and payload policy.

Each source:

  • declares exactly one workspace-relative path or dir_glob;
  • targets NDJSON, an *.ndjson directory glob, or an append-only .log;
  • uses canonical POSIX separators and cannot use an absolute path, Windows path, parent traversal, path alias, or mapper outside the pack module boundary;
  • refers to a mapper as module.js#exportName;
  • chooses metadata-only or full as its default wire-payload policy.

The host resolves opaque mapper references relative to each owning pack only after registry validation. If any mapper is missing, activation fails atomically and the previous workspace-scoped source set is unchanged. Installing the pack adds all definitions; removing the pin replaces the active set without leaving dispatch entries or sources visible as installed. Cursor files remain local so reinstalling a pack can resume safely.

Registry compilation sorts installed manifests and reports deterministic diagnostics. It never returns a partial catalog. Fix every diagnostic, then compile again.

Typical failures include:

Pack "beta" collides with pack "alpha" on command namespace "audit".
Pack "beta" collides with pack "alpha" on telemetry path ".lumenflow/telemetry/audit.ndjson".
Pack "alpha" declares reserved state namespace "kernel:runs".

This boundary matters for removal as much as installation: configuration, events, state, tools, policies, surfaces, and telemetry all appear or disappear as one unit.