# Centralized LLM Assets Implementation Plan ## Objective Implement the target state defined in [Centralized LLM Assets](assets.md): move embedded LLM-facing content into a minimal top-level `assets` package while leaving prompt semantics, schema identities, registration, interpretation, and fingerprint composition with their current framework and module owners. This plan is ordered. Implement each stage in sequence and complete its focused validation before beginning the next stage. Do not retire either roadmap when the stages are complete; roadmap cleanup is a separate maintainer decision. ## Plan-Wide Constraints These constraints apply to every stage: - Treat [the feature roadmap](assets.md) as the canonical source for scope, ownership, target layout, and non-goals. - Follow the architecture, documentation, and testing policies under `docs/policy/`. - Move asset files without changing their bytes. Use Git rename detection or content hashes to investigate any move reported as a content edit. - Move a coherent asset set and update every consumer in the same stage. Never leave a second authoritative copy in the old package. - Keep `assets/package.go` as the only Go source file under `assets/`. Do not add tests, generated Go files, path constants, module descriptors, lookup helpers, registration, validation, hashing, or other behavior to that package. - The root package may import only `embed` and `io/fs`. It exposes one read-only `FS() fs.FS` accessor and contains no Notarius-internal or PromptKit dependency. - A consuming package must import the root package consistently as `rootassets`, call `fs.Sub(rootassets.FS(), "")`, and operate only on its own subtree. The alias distinguishes content from local registry parameters commonly named `assets`. Do not pass the global filesystem through the application or introduce a global asset catalog. - Retain prompt IDs, schema keys, IDs, versions, names, registered filenames, profile IDs and contents, prompt message order, rendered bytes, and cache controls. - Keep `promptAssetRoot = "assets/prompts"` and each manifest's `ModuleDir` unchanged where they define the synthesized PromptKit filesystem. Only the source paths inside the manifest change. - Register a scoped module filesystem's `schemas` directory as the schema root so the flattened PromptKit schema filenames remain unchanged. - Keep prompt fingerprints manifest-scoped. Accept the feature roadmap's one-time fingerprint change caused by new source paths; do not preserve old paths through compatibility files or hash the complete root filesystem. - Do not move codec schemas, chunk-map or evidence-context durable schemas, the spell catalog, examples, or operator-provided profiles. - Do not rewrite prompts or schemas during this migration. - Update existing tests only where their setup refers to the old filesystem or path. Add a test only for a meaningful behavior not already protected. Do not add prompt-prose snapshots, asset counts, source-path snapshots, exact shared prefix lengths, or other change-detector tests. For each moved set, inspect `git diff --summary` and the ordinary diff before finishing the stage. Asset files should appear as exact renames unless their parent directory changes prevent Git from displaying the move immediately; their content must nevertheless be identical. ## Stage 1: Establish The Data-Only Package And Move Generic LLM Schemas ### Goal Create the minimal root package and use it for the two generic private response schemas currently embedded by the LLM framework. This proves the dependency direction and scoped-filesystem pattern before any D&D assets move. ### Changes 1. Create `assets/package.go` with: - a package comment explaining that it exposes embedded LLM-facing content; - imports of only `embed` and `io/fs`; - an unexported `embed.FS` populated initially with `//go:embed generic`; - one exported `FS() fs.FS` accessor returning the embedded filesystem; and - no other declarations or behavior. 2. Move these files without editing their contents: - `internal/framework/llm/assets/schemas/test_artifact.v1.json` to `assets/generic/schemas/test_artifact.v1.json`; - `internal/framework/llm/assets/schemas/test_validator_decision.v1.json` to `assets/generic/schemas/test_validator_decision.v1.json`. 3. Update `internal/framework/llm/schema_registry.go`: - remove its `embed` import, embed directive, and package-local embedded filesystem; - import the root `assets` package; - create its existing package-private schema filesystem from `fs.Sub(rootassets.FS(), "generic")`; - keep the existing fail-fast initialization behavior for an invalid compile-time subtree, using a small private helper in the LLM package if needed; and - change only the two source paths from `assets/schemas/...` to `schemas/...`. Preserve every schema definition identity and the existing defensive loading behavior. 4. Remove the now-empty `internal/framework/llm/assets/` tree. Do not introduce a reusable root-package subdirectory helper. The private framework helper, if used, is an initialization mechanism for the existing framework registry and does not belong in `assets`. ### Acceptance Criteria - `assets/package.go` is the sole Go file beneath `assets/` and has the exact minimal responsibility above. - The two generic schemas exist only in `assets/generic/schemas/`. - `RegisteredResponseSchemas`, lookup, schema identities, and schema content digests behave exactly as before. - The generic framework imports no D&D package or path. - No unrelated D&D asset has moved. ### Validation ```sh gofmt -w assets/package.go internal/framework/llm/schema_registry.go go test ./assets ./internal/framework/llm go vet ./assets ./internal/framework/llm git diff --check git diff --summary ``` This stage is sized for one gpt-5.6-terra implementation prompt. ## Stage 2: Centralize Shared D&D Prompts, Reconciliation Schema, And Profile ### Goal Create the shared D&D asset foundation required by every later D&D migration, including the single physical copy of shared prompt wording, the shared private entity-reconciliation schema, and the built-in fallback profile. ### Changes 1. Extend the embed directive in `assets/package.go` to embed both `generic` and `dnd`. Make no other root-package API change. 2. Move the seven files under `internal/modules/dnd/shared/assets/prompts/` unchanged to `assets/dnd/shared/prompts/`. 3. Update `internal/modules/dnd/shared/assets.go`: - remove the local embed declaration and `embed` import; - import the root `assets` package; - obtain a scoped filesystem with `fs.Sub(rootassets.FS(), "dnd/shared")` inside the shared package; - update `sharedPromptPaths` from `assets/prompts/` to `prompts/`; - continue resolving only the names explicitly declared by a `PromptAssetManifest`; and - retain the existing validation, virtual `sharedassets` mount names, defensive copying, ordering, and manifest-scoped hashing. Propagate subtree errors through the existing `PromptFS` and `Hash` error paths. Do not panic for D&D module asset preparation and do not move the manifest implementation into the root package. 4. Update the shared asset tests to obtain the scoped shared filesystem through the shared package's private mechanism instead of referring to the removed `embeddedAssets`. Retain the behavioral coverage for missing files, unknown names, exact declared parts, and deterministic hashing without asserting the repository tree or prompt prose. 5. Move `internal/modules/dnd/shared/entityreconcile/assets/schemas/dnd_entity_reconcile_llm.v1.json` unchanged to `assets/dnd/entity-reconciliation/schemas/dnd_entity_reconcile_llm.v1.json`. In `entityreconcile/schema.go`, scope the root filesystem to `dnd/entity-reconciliation`, change `SchemaAssetPath` to `schemas/dnd_entity_reconcile_llm.v1.json`, and use the same scoped filesystem for loading and registration. Register the `schemas` root so the flattened filename remains `dnd_entity_reconcile_llm.v1.json`. Delete the obsolete `entityreconcile/assets.go`. 6. Move `internal/modules/dnd/register/assets/profiles/dnd-extraction.yaml` unchanged to `assets/dnd/profiles/dnd-extraction.yaml`. Update `register/profiles.go` to scope `rootassets.FS()` to `dnd/profiles` and register that filesystem at `.`. Preserve the profile's virtual filename, ID, contents, fallback precedence, and digest behavior. 7. Remove the now-empty local shared/profile asset directories. Do not touch module-local prompt or schema assets in this stage. ### Acceptance Criteria - Every shared D&D prompt fragment has one physical source under `assets/dnd/shared/prompts/`. - Existing D&D manifests still select shared files explicitly and mount them at the same virtual names. - Entity reconciliation loads and registers the same private schema identity and bytes. - The `dnd-extraction` fallback profile resolves with unchanged effective content and precedence. - Full D&D registration succeeds while the remaining modules still consume their package-local assets. - `assets/package.go` remains the only Go file in the root package. ### Validation ```sh gofmt -w assets/package.go internal/modules/dnd/shared/assets.go internal/modules/dnd/shared/entityreconcile/schema.go internal/modules/dnd/register/profiles.go go test ./assets ./internal/framework/llm ./internal/modules/dnd/shared/... ./internal/modules/dnd/register git diff --check git diff --summary ``` This stage is sized for one gpt-5.6-terra implementation prompt. ## Stage 3: Migrate Scene Planning And Scene Descriptions ### Goal Move the two scene-oriented prompt/schema sets and establish the repeatable module-consumer pattern used in subsequent stages. ### Changes Move these directories without editing asset contents: | Current source | Target | | --- | --- | | `internal/modules/dnd/chunk/scenes/assets/prompts/` | `assets/dnd/scenes/prompts/` | | `internal/modules/dnd/chunk/scenes/assets/schemas/` | `assets/dnd/scenes/schemas/` | | `internal/modules/dnd/extract/scenedescriptions/assets/prompts/` | `assets/dnd/scene-descriptions/prompts/` | | `internal/modules/dnd/extract/scenedescriptions/assets/schemas/` | `assets/dnd/scene-descriptions/schemas/` | For both consuming packages: 1. Delete the package-local `assets.go` embed file. 2. In `prompt_assets.go`, add a private `moduleAssetFS() (fs.FS, error)` that scopes the root filesystem to the package's exact target subtree. 3. Change manifest source paths from `assets/prompts/` to `prompts/`. Do not change `ModuleDir`, the declared file order, shared file order, or `promptAssetRoot`. 4. Resolve the scoped filesystem in `RegisterPromptAssets`, pass it to the existing manifest, and register its `schemas` root. 5. Resolve the same scoped filesystem in the synchronized prompt fingerprint initializer and hash only the manifest's declared assets. 6. Update the response-schema loader to use the scoped filesystem and `schemas/`, preserving all identity constants. 7. Update focused tests that directly used `embeddedAssets` or old source paths to use `moduleAssetFS`. Preserve tests of prepared inputs, message placement, cache control, diagnostics redaction, schema compatibility, and registration. Keep scene planning in the chunk module and scene-description semantics in the extractor; the common asset location must not merge their behavior. ### Acceptance Criteria - Both scene asset sets exist only in their target root directories. - Both packages use scoped filesystems and retain their own manifests, schema definitions, registration, and fingerprints. - Prepared prompt IDs, rendered messages, schema identities, and PromptKit virtual filenames are unchanged. - Scene chunking and scene-description extraction tests pass, including D&D family registration. ### Validation ```sh gofmt -w internal/modules/dnd/chunk/scenes/*.go internal/modules/dnd/extract/scenedescriptions/*.go go test ./internal/modules/dnd/chunk/scenes ./internal/modules/dnd/extract/scenedescriptions ./internal/modules/dnd/register git diff --check git diff --summary ``` This stage is sized for one gpt-5.6-terra implementation prompt. ## Stage 4: Migrate The NPC Prompt Family ### Goal Centralize NPC extraction, NPC normalization, and NPC-interaction assets while preserving their distinct module behavior and shared-prefix contracts. ### Changes Move these directories unchanged: | Current source | Target | | --- | --- | | `internal/modules/dnd/extract/npcs/assets/prompts/` | `assets/dnd/npcs/extract/prompts/` | | `internal/modules/dnd/extract/npcs/assets/schemas/` | `assets/dnd/npcs/extract/schemas/` | | `internal/modules/dnd/normalize/npcs/assets/prompts/` | `assets/dnd/npcs/normalize/prompts/` | | `internal/modules/dnd/extract/npcinteractions/assets/prompts/` | `assets/dnd/npc-interactions/prompts/` | | `internal/modules/dnd/extract/npcinteractions/assets/schemas/` | `assets/dnd/npc-interactions/schemas/` | Apply the module-consumer pattern established in Stage 3 to all three packages: - delete each local embed-only `assets.go`; - add a private scoped filesystem accessor in the owning `prompt_assets.go`; - make manifest and response-schema paths relative to the scoped subtree; - retain virtual prompt roots, module directories, IDs, manifest ordering, and shared file selection; - use the scoped filesystem for registration, schema loading, and manifest-scoped fingerprinting; and - update focused tests only for the filesystem/path move. NPC normalization has no module-private response schema; it must continue using the shared entity-reconciliation schema migrated in Stage 2. NPC extraction and NPC interactions retain their own private response schemas. Do not combine the three manifests or add NPC behavior to the root package. ### Acceptance Criteria - NPC extraction, normalization, and interaction assets exist only in the target directories. - The modules retain three independent manifests and their existing semantic responsibilities. - NPC normalization still registers and loads the shared reconciliation schema rather than a copied schema. - Extraction prompt prefix order, normalization ordering, generated-reference grounding, cache controls, schema identities, and diagnostics behavior are unchanged. - Focused and family registration tests pass. ### Validation ```sh gofmt -w internal/modules/dnd/extract/npcs/*.go internal/modules/dnd/normalize/npcs/*.go internal/modules/dnd/extract/npcinteractions/*.go go test ./internal/modules/dnd/extract/npcs ./internal/modules/dnd/normalize/npcs ./internal/modules/dnd/extract/npcinteractions ./internal/modules/dnd/register git diff --check git diff --summary ``` This stage is sized for one gpt-5.6-terra implementation prompt. ## Stage 5: Migrate The Location Prompt Family ### Goal Centralize location extraction, location normalization, and location-occurrence assets while preserving registry grounding and shared entity-reconciliation behavior. ### Changes Move these directories unchanged: | Current source | Target | | --- | --- | | `internal/modules/dnd/extract/locations/assets/prompts/` | `assets/dnd/locations/extract/prompts/` | | `internal/modules/dnd/extract/locations/assets/schemas/` | `assets/dnd/locations/extract/schemas/` | | `internal/modules/dnd/normalize/locations/assets/prompts/` | `assets/dnd/locations/normalize/prompts/` | | `internal/modules/dnd/extract/locationoccurrences/assets/prompts/` | `assets/dnd/location-occurrences/prompts/` | | `internal/modules/dnd/extract/locationoccurrences/assets/schemas/` | `assets/dnd/location-occurrences/schemas/` | Apply the Stage 3 module-consumer pattern to all three packages. In particular: - delete local embed-only files; - scope each package to only its target subtree; - update manifest and schema source paths without altering identities or virtual PromptKit paths; - keep fingerprints limited to each manifest's module and selected shared assets; and - update focused tests that refer to the old filesystem. Location normalization continues to use the shared entity-reconciliation schema. Location occurrence extraction retains its own private response schema and its existing required location-registry input. Do not conflate the locations registry artifact with occurrence events. ### Acceptance Criteria - All three location-oriented asset sets exist only under their target root directories. - Extraction, normalization, and occurrence modules retain separate manifests, schema ownership, prompt inputs, and fingerprints. - Reconciliation and location-registry grounding behavior is unchanged. - Prompt preparation, schema compatibility, normalization, reference handoff, and family registration tests pass. ### Validation ```sh gofmt -w internal/modules/dnd/extract/locations/*.go internal/modules/dnd/normalize/locations/*.go internal/modules/dnd/extract/locationoccurrences/*.go go test ./internal/modules/dnd/extract/locations ./internal/modules/dnd/normalize/locations ./internal/modules/dnd/extract/locationoccurrences ./internal/modules/dnd/register git diff --check git diff --summary ``` This stage is sized for one gpt-5.6-terra implementation prompt. ## Stage 6: Migrate Spell And Combat-Turn Assets ### Goal Move the spell and combat-turn LLM assets while preserving their distinct catalog, NPC-grounding, and combat-scene eligibility behavior. ### Changes Move these directories unchanged: | Current source | Target | | --- | --- | | `internal/modules/dnd/extract/spells/assets/prompts/` | `assets/dnd/spells/prompts/` | | `internal/modules/dnd/extract/spells/assets/schemas/` | `assets/dnd/spells/schemas/` | | `internal/modules/dnd/extract/combatturns/assets/prompts/` | `assets/dnd/combat-turns/prompts/` | | `internal/modules/dnd/extract/combatturns/assets/schemas/` | `assets/dnd/combat-turns/schemas/` | Apply the Stage 3 module-consumer pattern to both extractors. Preserve the spell manifest's catalog fragment and optional NPC registry placement. Preserve combat-turn routing so the LLM runs only for exact combat scene classifications; this refactor must not move eligibility logic into the assets package or prompt declaration. Do not move or modify `internal/modules/dnd/spells/catalog/assets/dnd_5e_2014_srd_spells.json`; it is domain reference data, not an LLM interaction asset. ### Acceptance Criteria - Spell and combat-turn prompts and private response schemas exist only in the new root subtrees. - Both packages retain independent manifests, response-schema definitions, registrations, and component fingerprints. - Spell catalog overlay identity, NPC grounding, combat eligibility, prompt message order, cache controls, schema identities, and diagnostics remain unchanged. - The spell catalog stays with its existing package. - Focused and family registration tests pass. ### Validation ```sh gofmt -w internal/modules/dnd/extract/spells/*.go internal/modules/dnd/extract/combatturns/*.go go test ./internal/modules/dnd/extract/spells ./internal/modules/dnd/extract/combatturns ./internal/modules/dnd/spells/catalog ./internal/modules/dnd/register git diff --check git diff --summary ``` This stage is sized for one gpt-5.6-terra implementation prompt. ## Stage 7: Migrate Item-Event And Enemy-Event Assets ### Goal Complete the module asset migration with item-event and enemy-event extraction, leaving every D&D LLM prompt and private response schema under the root asset tree. ### Changes Move these directories unchanged: | Current source | Target | | --- | --- | | `internal/modules/dnd/extract/itemevents/assets/prompts/` | `assets/dnd/item-events/prompts/` | | `internal/modules/dnd/extract/itemevents/assets/schemas/` | `assets/dnd/item-events/schemas/` | | `internal/modules/dnd/extract/enemyevents/assets/prompts/` | `assets/dnd/enemy-events/prompts/` | | `internal/modules/dnd/extract/enemyevents/assets/schemas/` | `assets/dnd/enemy-events/schemas/` | Apply the Stage 3 module-consumer pattern to both packages. Preserve the enemy-event grounding fragment, required generated references, combat-scene eligibility, and observation semantics. Preserve item-event categories and prompt structure. Do not change deterministic normalizers or durable codecs. ### Acceptance Criteria - Item-event and enemy-event LLM assets exist only in their target directories. - Both extractors retain their module-owned manifests, schema definitions, registrations, response interpretation, and fingerprints. - Prompt preparation, reference grounding, scene gating, schema compatibility, normalization, and family registration behavior is unchanged. - No D&D prompt, private LLM schema, shared fragment, or built-in profile remains beneath an `internal/modules/dnd/**/assets/` directory. ### Validation ```sh gofmt -w internal/modules/dnd/extract/itemevents/*.go internal/modules/dnd/extract/enemyevents/*.go go test ./internal/modules/dnd/extract/itemevents ./internal/modules/dnd/extract/enemyevents ./internal/modules/dnd/register git diff --check git diff --summary ``` This stage is sized for one gpt-5.6-terra implementation prompt. ## Stage 8: Audit Asset Boundaries And Consolidate Behavioral Verification ### Goal Review the completed physical migration as one system, remove obsolete embedding surfaces, and ensure existing behavior-level tests protect the meaningful asset contracts without introducing structural change detectors. ### Changes 1. Inventory every remaining `//go:embed` directive and every directory named `assets` under `internal/`. Classify each remaining file against the feature roadmap. Expected retained local embeds include durable codec/framework schemas and the spell catalog; no migrated LLM interaction asset may remain. 2. Confirm all prompt-bearing consumers import the root package, scope it with `fs.Sub`, and expose no root filesystem beyond their package. 3. Confirm every old embed-only module `assets.go` has been deleted. Keep files named `assets.go` that still own actual manifest behavior, such as the D&D shared implementation; file names alone are not grounds for deletion. 4. Inspect all `PromptAssetManifest` values and response-schema definitions: - source paths are relative to the correct scoped filesystem; - virtual `ModuleDir`, prompt IDs, schema identities, and registration names are unchanged; - shared fragments remain explicitly enumerated in order; and - hashes consume only the manifest's selected files. 5. Run and review the existing family-wide prompt-prefix, schema-compatibility, profile, registration, and composition tests. Repair coverage only if a meaningful behavior lost protection during the move. Prefer existing public or package-level preparation boundaries; do not test the literal root tree shape, exact prose, file counts, or private call sequences. 6. Verify that only `assets/package.go` is Go code in the root asset tree and that its imports and declarations still satisfy the strict data-only rule. Useful non-test inspection commands include: ```sh rg -n '//go:embed' --glob '*.go' . find internal -type f -path '*/assets/*' | sort find assets -type f | sort find assets -name '*.go' -print go list -f '{{join .Imports "\n"}}' ./assets ``` These are manual audit checks, not assertions to encode in permanent tests. ### Acceptance Criteria - The root tree contains every in-scope LLM asset and no out-of-scope durable contract or domain-data asset. - Old module-local LLM assets and embed declarations are gone. - There is one physical copy of each shared prompt fragment. - Modules and framework consumers access only scoped subtrees. - PromptKit preparation, response schemas, fallback profiles, family registration, and production composition remain operational. - No low-value structural or prose change-detector test has been added. ### Validation ```sh go test ./assets ./internal/framework/llm/... ./internal/modules/dnd/... ./internal/cli/... go vet ./assets ./internal/framework/llm/... ./internal/modules/dnd/... git diff --check git diff --summary ``` This audit and focused remediation stage is sized for one gpt-5.6-terra implementation prompt. ## Stage 9: Record The Decision And Update Canonical Documentation ### Goal Document the implemented ownership boundary in its canonical homes without duplicating volatile details or presenting the root package as a public extension contract. ### Changes 1. Add `docs/adr/0011-centralize-llm-assets.md` in the policy-required Nygard format. Record: - why prompt-author discoverability now outweighs package-local physical co-location; - the data-only top-level package and single-filesystem API; - continued module ownership of semantics and registration; - scoped consumer access and dependency direction; - exclusion of durable schemas and non-LLM domain data; - the accepted public import path and one-time checkpoint invalidation; - alternatives of package-local assets, `internal/llmassets`, a behavioral central registry, and runtime filesystem overlays; and - the exact portion of ADR-0004 that this decision supersedes. 2. Change only ADR-0004's status metadata to state that its asset-co-location rule is superseded by ADR-0011 while its domain-first module packaging decision remains accepted. Do not rewrite its accepted decision text. 3. Update `docs/policy/architecture.md` at the layer/domain and LLM boundaries: - define the root package as a content-only dependency leaf; - prohibit business logic and internal/PromptKit dependencies there; - state that physical centralization does not transfer semantic ownership from modules; and - keep generic framework code domain-neutral even when it reads its own scoped generic assets from the shared container. 4. Update `docs/internal/llm.md` to describe the implemented root filesystem, scoped consumers, asset registry flattening, private schema distinction, and manifest-scoped fingerprints. Link to architecture and the ADR instead of repeating their rationale. 5. Update `docs/internal/dnd.md` to document the convention for locating new D&D LLM assets, shared-fragment ownership, extract/normalize subtrees, and module-owned manifests. Preserve its existing canonical prompt-order and caching guidance. 6. Update `docs/internal/overview.md` with a concise current component entry for `assets/` and links to the focused LLM/D&D documents. Do not duplicate the full directory tree. 7. Search current documentation for claims that LLM assets are physically package-local and revise only the canonical owners. Do not change user, operator, configuration, or integration contracts unless inspection finds a directly false statement caused by this internal-only move. ### Acceptance Criteria - ADR-0011 records the durable decision and ADR-0004's status identifies the limited supersession without altering its historical decision. - Architecture contains the normative data-only boundary. - Internal documentation accurately describes the implemented mechanics and convention for future modules. - No document outside `docs/roadmap/` describes unimplemented behavior. - Documentation does not duplicate prompt contents, schema definitions, module inventories, or volatile asset path lists unnecessarily. - Relative links resolve to existing files. ### Validation ```sh rg -n 'package-specific assets|module-owned assets|internal/modules/dnd/.*/assets|internal/framework/llm/assets' README.md docs git diff --check go test ./internal/framework/llm/... ./internal/modules/dnd/... ``` Manually verify changed Markdown links because the repository currently has no dedicated documentation link checker. This documentation stage is sized for one gpt-5.6-terra implementation prompt. ## Stage 10: Final Repository Verification ### Goal Perform a clean final review of the complete migration, correct any remaining in-scope defect, and demonstrate that the repository builds and tests as a coherent whole. ### Changes 1. Re-read the feature roadmap and inspect the complete diff from the base commit, not only the most recent stage. 2. Confirm every target-tree entry is present and every excluded asset remains with its original owner. 3. Confirm moved prompt, schema, and profile files have unchanged content. 4. Confirm `assets/package.go` is still the sole Go file under `assets`, exposes only `FS() fs.FS`, and imports only `embed` and `io/fs`. 5. Confirm no consumer uses an unrelated subtree and no generic package names a D&D asset path. 6. Confirm all old local LLM embed declarations, files, and directories are gone, without deleting retained durable schemas or the spell catalog. 7. Review test changes for compliance with the testing policy and remove any redundant structural or exact-content assertion introduced during the migration. 8. Review documentation changes for canonical ownership, current-state wording, correct links, and absence of duplicated volatile content. 9. Run formatting and repository-wide validation. Fix only defects within this roadmap's scope; report unrelated pre-existing failures separately. ### Acceptance Criteria - Every target-end-state and verification expectation in `assets.md` is met. - The complete diff contains no prompt, schema, profile, module key, profile default, durable contract, or pipeline behavior change beyond source relocation and the accepted internal fingerprint invalidation. - The working tree contains no accidental generated files or obsolete empty asset directories. - All repository-wide checks pass. ### Validation ```sh go fmt ./... go test ./... go vet ./... go build ./cmd/notarius git diff --check git status --short ``` Review `git diff --stat`, `git diff --summary`, and the full diff before reporting completion. Do not delete `docs/roadmap/assets.md` or `docs/roadmap/implementation.md` in this stage. This final verification stage is sized for one gpt-5.6-terra implementation prompt. ## Open Questions None. The feature roadmap and this plan resolve the package API, target tree, asset scope, consumer pattern, fingerprint policy, documentation ownership, and accepted compatibility consequences needed for implementation.