ADR 0008: The managed-set is declared in managed.json, not hardcoded
Retired — made moot by ADR 0019. The managed-file set and
managed.jsonno longer exist. Kept for history; not current architecture.
Status: accepted; made moot by ADR 0019 — see Update (single-binary cutover) below.
Update (single-binary cutover)
§The single-binary cutover removes managed files entirely: with no committed engine to sync, managed.json, the managed-set classifier, and src/lib/manifest.ts are deleted. The "managed vs seed" contract this ADR refined no longer exists — ownership is now yours (committed seeds) vs the binary's (gitignored, re-published artifacts).
Context
§"Managed vs seed" is the contract that makes upgrade safe: a managed file (bin/agent, .discern/engine/**, .ai/skills/**) is hash-tracked and refreshed by upgrade (preserved as .new when the user edited it), while a seed file (everything else) is write-once. Until now that classification was hardcoded in the installer — MANAGED_EXACT / MANAGED_PREFIXES constants in src/lib/manifest.ts.
Two problems with hardcoding it:
- The contract is invisible from the template tree. What the kit owns is a property of the templates, but you had to read installer source to learn it.
- An adapter can't own a managed file.
add-adapteroverlays files through the same plan builder, so an adapter file is managed only if it happens to fall under a hardcoded prefix (.ai/skills/**). An adapter that wants to own a managed file anywhere else had no way to say so.
Decision
§Move the managed-set into a declaration the template ships, and let the installer read it.
templates/managed.jsondeclares the set:{ "exact": ["bin/agent"], "prefixes": [".discern/engine/", ".ai/skills/"] }. It is installer metadata, never scaffolded (the plan walker skips it, the same wayadd-adapterskipsadapter.json).manifest.tsexposes aManagedSpec({ exact, prefixes }),isManagedBy(path, spec), aDEFAULT_MANAGED_SPECthat mirrors the shipped file (the fallback when no declaration is present, so behaviour is identical either way), andloadManagedSpec(dir)/parseManagedSpec/mergeManagedSpecs.isManaged(path)remains as a thin wrapper over the default for callers without a spec.buildPlantakes an optionalmanagedSpec.setupandupgradeload it from the templates tree;add-adapterclassifies an adapter's overlay files byDEFAULT_MANAGED_SPECmerged with the adapter's ownmanaged.json(if it ships one), so an adapter can mark overlay files it owns.
Consequences
§- The managed/seed contract is now data in the template tree, not buried in installer code. A kit maintainer adjusts it by editing
managed.json. - An adapter can declare managed overlay files. They are hash-tracked in the project manifest and preserved-as-
.newif edited, so re-runningadd-adapterwon't clobber local edits — the same safety the kit's own managed files get. - Honest limitation:
upgraderefreshes only the kit's templates, not adapter files (adapters aren't part of the kit tree). So an adapter-managed file is tracked and edit-preserved, but re-applied viaadd-adapter, notupgrade. Full adapter-aware upgrade would be a separate, larger feature; this ADR does not promise it. - Behaviour is unchanged for every existing install: the shipped
managed.jsonequals the old hardcoded set, and the default applies when it is absent. - One new metadata file at the templates root, filtered from the scaffold like
adapter.json.
Alternatives considered
§- Keep the set hardcoded. Rejected: it hides the contract and blocks adapter-owned managed files — the two problems above.
- Record the rule in the project manifest instead of the template. The manifest already lists the managed files (paths + hashes), but
setupmust classify new files from the template walk before any manifest exists for them, so the rule has to come from the template. The template declaration is the right source of truth; the manifest stays a record of what was written. - A glob engine for the spec. Overkill: the managed set is a handful of exact paths and directory prefixes. Prefix/exact matching is enough and keeps the classification trivial to reason about (it mirrors what the old constants did).
- Let an adapter's
managed.jsonfully replace the base spec. Rejected: merging keeps the kit's defaults (so an adapter's skills stay managed) while letting the adapter add its own — replacing would surprise an adapter author by un-managing the standard trees.