Framework
framework is what is posed on a project and the record of it: the manifest that tracks every
installed file, and every flow that reads or changes that record — setup, doctor, sync (restore),
uninstall, plugin install/update/remove, and the global chain orchestrators. It is the one
context the dependency chain lets reach every other context but telemetry (framework → translate → tools → kernel, plus framework → distribution), because assembling what goes on
disk is exactly the job that needs all three.
What goes in
| Concept | Location |
|---|---|
| The manifest aggregate and its members | domain/manifest.ts, domain/manifest/ (tool-entry, tracked-files, merge-files, native-registrations) |
| A plugin's declared state | domain/plugins/ (installed-plugin, source-resolver, requested-version-policy) |
| The diagnosis shape | domain/doctor.ts |
| Setup orchestration state | domain/setup-flow.ts |
A port only framework needs | domain/ports/ (manifest-repository, plugin-distribution-reader, user-source-references) |
| A top-level flow's orchestrator | application/ root, or a feature subdirectory (clean/, doctor/, flows/, framework/, global/, install/, plugin/, restore/, setup/, uninstall/) |
| Logic needed by ≥2 top-level use-cases | application/shared/ — never called from a command |
| The manifest-repository and plugin-distribution-reader adapters | infrastructure/ |
How
- A use-case class ends in
UseCase, has a singleasync execute(options): Promise<Result>, never catches its own errors except the three carve-outs (global aggregate-error loops, cache/network fallback, typed-throw translation) — see the use-case and orchestration rules in.claude/rules/00-architecture/. - A use case that installs framework files saves the manifest and updates
.gitignorethroughPostInstallPipelineUseCase. A use case that only changes the manifest (plugin add/remove, marketplace sync, restore) saves it directly. Seereferences/post-install-pipeline.md. - Before writing any framework file: check
fs.fileExists(path) && !manifest.isFileTracked(path). If both are true, skip the write, warn, and never add it to the manifest — never overwrite a user-owned file. Seereferences/manifest.md. - A global chain orchestrator (
*-all-use-case.ts) iterates every scope and must finish even if one fails: wrap one iteration intry/catch, push a typed entry to anerrors[]array, and return it in the result — never let one tool's failure abort the whole run. - A capability-guard sub-use-case (
install-agents-use-case.tsand its siblings) checks"name" in capsbefore dispatching to a narrowed sub-use-case ininstall/— seereferences/post-install-pipeline.md, section "Capability sub-use-cases". These five files reach directly intotools' capability classes rather than through a declared public module; that reach is a tracked, shrinking exception incontext-boundary.arch.test.ts, not a pattern to add to. - A launcher spawns its target, never imports it. The decision and its cost are in
aidd_docs/memory/architecture.md.
Public surface
Nothing outside contexts/framework/ may import a module this context has not declared public.
framework is also the context most other contexts should never see: nothing in tools,
translate, or distribution may import from framework at all — the arrow only runs the other
way. Check tests/architecture/context-graph.arch.test.ts before adding an edge; check
context-boundary.arch.test.ts's PUBLIC_MODULES before assuming a module framework itself
exposes is reachable from presentation or runtime.
How it's tested
tests/contexts/framework/mirrorssrc/contexts/framework/— domain models are unit-tier, use-cases against in-memory ports (tests/helpers/ports/) are unit or integration depending on whether they touch a real temp filesystem.tests/e2e/exercises full CLI invocations throughrunCli();tests/golden/snapshots a built framework tree end to end — see thetestskill for tier and golden-snapshot rules.- A manifest version-guard change needs a fixture manifest at the boundary version, asserting the exact refusal message names the fix.


