From 5f207b2ab1b55a635fbcffcd7d3149f85ace3f94 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Wed, 5 Aug 2026 00:49:15 +0000 Subject: [PATCH] Document centralized LLM asset ownership --- docs/adr/0004-package-modules-by-domain.md | 2 +- docs/adr/0011-centralize-llm-assets.md | 69 ++++++++++++++++++++++ docs/internal/dnd.md | 17 ++++-- docs/internal/llm.md | 24 +++++--- docs/internal/overview.md | 1 + docs/policy/architecture.md | 13 ++++ 6 files changed, 112 insertions(+), 14 deletions(-) create mode 100644 docs/adr/0011-centralize-llm-assets.md diff --git a/docs/adr/0004-package-modules-by-domain.md b/docs/adr/0004-package-modules-by-domain.md index 1e42c6d..d9298b8 100644 --- a/docs/adr/0004-package-modules-by-domain.md +++ b/docs/adr/0004-package-modules-by-domain.md @@ -1,6 +1,6 @@ # ADR-0004: Package modules by domain, not by stage -**Status:** Accepted +**Status:** Accepted — its asset-co-location rule is superseded by [ADR-0011](0011-centralize-llm-assets.md); its domain-first module packaging decision remains accepted. **Date:** 2026-07-13 ## Context diff --git a/docs/adr/0011-centralize-llm-assets.md b/docs/adr/0011-centralize-llm-assets.md new file mode 100644 index 0000000..b65f5d3 --- /dev/null +++ b/docs/adr/0011-centralize-llm-assets.md @@ -0,0 +1,69 @@ +# ADR-0011: Centralize LLM-facing assets in a content-only package + +**Status:** Accepted +**Date:** 2026-08-05 + +## Context + +LLM prompts, private response schemas, generic schemas, and fallback profiles +are authored and reviewed as content, but package-local embedding scattered that +content across implementation trees. Finding all of the assets that contribute +to a prompt family required navigating code ownership boundaries rather than a +single discoverable content boundary. + +The repository must retain module ownership of prompt semantics, schema +identities, registration, and prompt-cache behavior. Durable artifact schemas +and non-LLM domain data have different compatibility and ownership rules, so +they must not move merely because they are embedded files. + +## Decision + +LLM-facing content is embedded by the root `assets` package. It is a data-only +dependency leaf: its single `FS() fs.FS` API returns the read-only embedded +filesystem, and the package contains no business logic or internal or PromptKit +dependencies. The accepted import path is +`gitea.maximumdirect.net/eric/notarius/assets`; it makes repository-owned +content available to its consumers, not a public extension contract. + +Consumers scope that filesystem to the subtree they own before reading or +registering content. Modules continue to own their manifests, prompt ordering, +private response-schema identity, and registration. Centralizing physical files +does not centralize domain semantics or transfer those responsibilities to the +root package. + +The root package contains prompt content, private LLM response schemas, generic +LLM schemas, shared fragments, and fallback profiles. Durable artifact schemas +and non-LLM domain data remain with their current owners. A module fingerprint +is derived from its manifest-selected module and shared files, rather than from +an entire asset tree. The relocation is accepted to cause a one-time checkpoint +invalidation. + +This decision supersedes only the physical asset-co-location portion of +ADR-0004's decision that places domain-specific prompt fragments and schemas +within the domain tree. ADR-0004's domain-first packaging and registrar +ownership decisions remain accepted. + +## Alternatives Considered + +- Keep package-local assets. This preserves physical co-location with code but + makes prompt-author discovery and cross-family review unnecessarily costly. +- Use `internal/llmassets`. This would hide content from legitimate owners + outside the `internal` subtree and would make the root asset boundary depend + on implementation-layer placement. +- Build a behavioral central registry. This would mix content discovery with + prompt selection and registration behavior, moving module semantics into a + shared registry. +- Use runtime filesystem overlays. This would add runtime configuration and + failure modes where compile-time embedded content is sufficient. + +## Consequences + +Prompt authors can find in-scope LLM content in one top-level tree while module +packages continue to define its meaning and registration. Consumers have an +explicit, narrow dependency on only the content they need. The root package is +intentionally importable but must remain a stable, content-only leaf rather +than becoming a general extension API. + +The initial relocation invalidates existing checkpoints once. Later checkpoint +identity changes remain limited to the manifest-selected prompt and shared +content, so unrelated files do not trigger recomputation. diff --git a/docs/internal/dnd.md b/docs/internal/dnd.md index cdbf007..5f41d4b 100644 --- a/docs/internal/dnd.md +++ b/docs/internal/dnd.md @@ -43,14 +43,21 @@ the contracts above define durable data. ## Prompt Construction +D&D LLM-facing content lives beneath `assets/dnd/`. New extractor content uses +its feature subtree; when a family has both extraction and normalization +content, keep those in its `extract` and `normalize` subtrees. The owning module +still defines the ordered manifest and registers the resulting scoped filesystem. +Shared fragments belong to the D&D shared implementation and are selected by +name, never copied into individual module subtrees. + D&D extractors assemble prompts from an ordered manifest of shared and -module-owned assets. The location extractor and occurrence extractor reuse the -shared D&D system, evidence, identity, reference, and transcript assets instead -of copying their text into individual modules. A manifest’s declared sequence, -including cache-control placement, is part of the prompt behavior. +module-selected assets. The location extractor and occurrence extractor reuse +the shared D&D system, evidence, identity, reference, and transcript assets +instead of copying their text into individual modules. A manifest’s declared +sequence, including cache-control placement, is part of the prompt behavior. Every maintained D&D LLM prompt selects `dnd-extraction` as its default -profile. The D&D registrar embeds that fallback profile with the maintained +profile. The D&D registrar registers that fallback profile with the maintained OpenRouter model, timeout, and service-tier policy. An operator may provide a complete profile with the same ID through the configured PromptKit source; that definition replaces the fallback rather than merging with it. The fallback diff --git a/docs/internal/llm.md b/docs/internal/llm.md index a3fe5f3..156adba 100644 --- a/docs/internal/llm.md +++ b/docs/internal/llm.md @@ -125,16 +125,24 @@ the corresponding PromptKit filesystems and rejects invalid roots, unreadable assets, duplicate paths, and missing prompt or schema files during preparation. Fallback assets receive a safe content digest for checkpoint identity; raw paths and bytes are never included. The framework’s `promptfs` helper combines -module-owned prompt files with reusable domain fragments without making the +module-selected prompt files with reusable domain fragments without making the framework depend on D&D content. -Each LLM-backed module owns its prompt declaration, package-specific assets, -and private response schema. Shared D&D wording is owned by the D&D shared -asset package; the detailed D&D conventions are in -[D&D Module Internals](dnd.md). The mounted prompt assets used by a module also -determine its prompt fingerprint. Schema loaders validate JSON, attach identity -and digest metadata, make defensive copies, and expose diagnostics without raw -schema bytes. +LLM-facing content is embedded once by the root `assets` package. Each consumer +uses only its scoped subtree, while the module retains ownership of its prompt +declaration, ordered manifest, private response-schema identity, and +registration. Shared D&D fragments are selected by D&D's shared implementation; +the detailed convention is in [D&D Module Internals](dnd.md). This physical +arrangement and its data-only boundary are defined by +[Architecture](../policy/architecture.md) and +[ADR-0011](../adr/0011-centralize-llm-assets.md), rather than by this runtime +guide. + +Mounted prompt assets determine a module's fingerprint. The fingerprint hashes +only the module and shared files explicitly selected by its manifest, so an +unrelated asset does not invalidate a checkpoint. Schema loaders validate JSON, +attach identity and digest metadata, make defensive copies, and expose +diagnostics without raw schema bytes. Private response schemas validate a model transport envelope. They are not the durable artifact schema and should not be documented as an external wire diff --git a/docs/internal/overview.md b/docs/internal/overview.md index 97cc000..aad3aec 100644 --- a/docs/internal/overview.md +++ b/docs/internal/overview.md @@ -29,6 +29,7 @@ physical state roots. | Generic models | **internal/core/source**, **internal/core/artifacts**, **internal/framework/contracts** | Source documents and chunks, manifests and provenance, plus typed artifact, reference, validation, output, and structured-completion contracts. | | Pipeline framework | **internal/framework/pipeline** | Registries, profile and reference resolution, typed preparation, validation, retry coordination, ordered execution, handoff, and result assembly. | | LLM and prompt runtime | **internal/framework/llm**, **internal/framework/promptfs** | Provider-neutral structured completions, scheduling, profile recording, prompt assets, schema registration, and credential-shaped-value redaction. | +| Embedded LLM content | **assets** | Read-only centralized LLM-facing content, scoped by its consuming package; see [LLM Runtime](llm.md#prompt-and-schema-assets) and [D&D Module Internals](dnd.md#prompt-construction). | | Runtime state | **internal/core/fileio**, **internal/core/debugbundle**, **internal/framework/checkpoint**, **internal/framework/chunkplan**, **internal/framework/chunkmap**, **internal/framework/debug** | Confined atomic files, debug bundles, checkpoint and chunk-plan state, accepted chunk maps, and pipeline-facing debug recording. | | Production extensions | **internal/modules/generic**, **internal/modules/seriatim**, **internal/modules/dnd** | Domain-neutral extensions, Seriatim input support, and D&D extraction families registered into the production catalog. | diff --git a/docs/policy/architecture.md b/docs/policy/architecture.md index 22b8ae2..fc1c7fe 100644 --- a/docs/policy/architecture.md +++ b/docs/policy/architecture.md @@ -44,6 +44,13 @@ must not compose the application or take ownership of process behavior. The current packages implementing these layers are inventoried in [Internal Overview](../internal/overview.md). +The root `assets` package is a content-only dependency leaf. It may expose a +read-only embedded filesystem, but it must contain no business logic and must +not depend on `internal` packages or PromptKit. Consumers scope that filesystem +to the content they own; the root package is not a behavioral registry or a +public extension contract. The rationale and compatibility consequence are +recorded in [ADR-0011](../adr/0011-centralize-llm-assets.md). + The following dependency boundaries are mandatory: - extractors and validators do not depend on concrete input adapters; @@ -73,6 +80,12 @@ Extract modules own artifact semantics, prompt use, response schemas, and domain interpretation. Domain-specific concepts remain in the relevant module, validator, shared domain helper, and artifact contract. +Physical centralization of LLM-facing content does not transfer semantic +ownership from those modules. Modules retain their manifests, response-schema +identities, prompt ordering, and registration, while reading only their scoped +content subtree. Generic framework code remains domain-neutral when it reads +its own scoped generic assets from the shared content container. + Typed artifact registrations declare one stable artifact kind and exact Go type from extraction through merge, normalization, and semantic validation. Pipeline resolution requires a compatible codec and matching kind-specific