diff --git a/docs/decisions/0002-shared-readable-pipeline-filesystem-policy.md b/docs/decisions/0002-shared-readable-pipeline-filesystem-policy.md index a647cbc..0266b4a 100644 --- a/docs/decisions/0002-shared-readable-pipeline-filesystem-policy.md +++ b/docs/decisions/0002-shared-readable-pipeline-filesystem-policy.md @@ -48,6 +48,13 @@ file modes are `0750` and `0640`, respectively, with shared credentials. Configuration may contain credential reference names but never raw credential values. +Narratio's general operational guidance recommends `0700` directories and +`0600` files for a single-user credential store. The suite deliberately grants +group read/traverse access instead: membership in the selected `narratio` +runtime group authorizes both pipeline execution and use of its shared provider +credentials. Deployments that do not trust every group member require a +different per-user credential policy and are outside this decision. + Version-controlled campaign and prompt assets may remain root-managed because Git is authoritative, but they are world-readable. The suite does not add confidentiality controls to non-secret D&D content. diff --git a/docs/roadmap/features/0011-managed-pipeline-credential-delivery.md b/docs/roadmap/features/0011-managed-pipeline-credential-delivery.md new file mode 100644 index 0000000..04e8f22 --- /dev/null +++ b/docs/roadmap/features/0011-managed-pipeline-credential-delivery.md @@ -0,0 +1,392 @@ +# Feature 0011: Managed Pipeline Credentials and Notarius Reuse + +Status: ready + +## Summary + +Upgrade the generic Narratio role and D&D pipeline to Narratio v2.1.0, manage +provider credentials through Narratio's conventional ConfigKit secrets +directory, and use the release's typed Notarius credential and checkpoint-reuse +controls. Credential values remain outside ordinary application YAML and are +readable only by the selected runtime user and group. Ordinary pipeline runs +reuse compatible Notarius checkpoints, while forced extraction retains +Narratio's native refresh behavior. + +## Problem or Opportunity + +The pipeline role currently writes credential reference names but manages no +credential values. A caller must prepare the invoking environment through an +external mechanism before a real run can reach the selected model providers. + +The installed Narratio v2.0.0 release also cannot forward a selected provider +credential to Notarius. Its Notarius child receives a restricted environment, +and the v2.0 configuration contract exposes no typed credential field. The +pipeline records Notarius checkpoints but cannot ask Notarius to reuse them. + +Narratio v2.1.0, immutable tag `v2.1.0` at `c45e042`, resolves both application +contract gaps. It adds optional `notarius.llm_api_key`, `notarius.resume`, and +`notarius.chunk_cache` fields without requiring migration of existing v2 +configuration. The credential field resolves through the existing process +environment or secrets-directory bootstrap, forwards only the exact selected +name and value, and participates in the application's existing redaction +boundary. Ordinary extraction may request checkpoint reuse; forced extraction +omits resume and refreshes the active chunk plan. + +The suite separately configures Audita proposal and validation with different +environment names even though both use the same endpoint and model. Narratio's +Audita boundary accepts one typed credential. This feature therefore +coordinates both suite-default Audita sections on `AUDITA_LLM_API_KEY` while +retaining their existing independently overrideable variables. + +## Governing Policies + +- [The Ansible house style](../../ansible.md) governs the direct desired-state + implementation, native configuration interface, and ordinary module + convergence. +- [The testing house style](../../testing.md) governs synthetic, native, + behavior-oriented validation without live provider calls or filesystem + metadata assertions. +- [The MVP roadmap](../mvp.md) assigns pipeline-wide secret delivery, + application-version coordination, and cache policy to the suite role. +- [Decision 0002](../../decisions/0002-shared-readable-pipeline-filesystem-policy.md) + defines the selected runtime group as the authorization boundary and fixes + `0750` directory and `0640` file modes for shared credential material. +- [Feature 0009](0009-shared-pipeline-filesystem-and-execution.md) establishes + the service-user and existing-group-member invocation model. +- [Feature 0010](0010-dnd-notarius-extraction-integration.md) establishes the + Notarius pipeline, FHS cache roots, automatic chunk-plan reuse, and checkpoint + recording extended by this feature. +- Narratio's immutable v2.1.0 release documentation and current configuration + and Notarius integration references are authoritative for the application + fields, resolution semantics, forced-run behavior, and redaction contract. + +## Scope + +- Advance the standalone Narratio role's selected application release from + v2.0.0 to v2.1.0 without changing its established source-build task flow. +- Expose the three new native optional fields through nullable, field-specific + role variables: `narratio_notarius_llm_api_key`, + `narratio_notarius_resume`, and `narratio_notarius_chunk_cache`. +- Keep the standalone role generic. All three variables default to null and + are omitted unless a caller selects them, leaving Narratio's native defaults + in control. +- Advance the pipeline role's compatible Narratio application version to + v2.1.0 and pin its Narratio role dependency to the reviewed revision that + provides the new interface. +- Use `/usr/local/etc/narratio/secrets`, the conventional directory adjacent + to `/usr/local/etc/narratio/pipeline.yml`, as the suite's sole managed + credential location. +- Create that directory as the selected pipeline runtime user and group with + mode `0750`. +- Add one public `dnd_pipeline_secrets` mapping. Keys are environment-variable + names. A non-null string value means that the named credential file must be + present with that content; null means that the specifically named managed + credential file must be absent. An omitted key is unmanaged. +- Default `dnd_pipeline_secrets` to an empty mapping so the role never invents + credentials and remains usable with externally prepared process environment + values. +- Validate selected keys as environment-variable names and reject empty + non-null values before constructing a destination path. +- Write non-null credential files as the selected runtime user and group with + mode `0640`, and remove only names explicitly assigned null. +- Suppress credential values from Ansible task output during validation, + creation, updates, and removal. +- Keep ordinary application configuration limited to non-secret credential + reference names. +- Coordinate Audita proposal and validation on the suite-default + `AUDITA_LLM_API_KEY` name and pass that name through the standalone Narratio + role's existing `narratio_audita_llm_api_key` interface. +- Add `dnd_pipeline_narratio_notarius_llm_api_key`, default it to + `OPENROUTER_API_KEY` for Notarius's selected production profile, and pass it + through Narratio's typed Notarius credential field. +- Add `dnd_pipeline_narratio_notarius_resume`, default it to true, and pass it + through Narratio so ordinary extraction requests reuse of the checkpoints + already recorded by the suite's Notarius configuration. +- Expose Narratio's native `notarius.chunk_cache` in the standalone role but do + not duplicate it behind a suite variable or set it from the pipeline role. + The suite already configures Notarius's ordinary chunk-plan mode as `auto`; + Narratio v2.1.0 independently owns its forced-extraction refresh override. +- Preserve Scriptorium's application-owned `api_key_env` profile selection. + Narratio's secrets bootstrap supplies the selected environment value and the + existing Scriptorium adapter passes that named credential to its child. +- Document the mapping interface, Ansible Vault inventory pattern, process + environment precedence, shared-group authorization boundary, explicit null + removal, application credential flow, and checkpoint-reuse policy. +- Add focused integration coverage using distinctive synthetic values and + native Narratio configuration loading. No default test contacts a model + provider. + +## Non-goals + +- Storing raw credentials in Narratio, Audita, Notarius, or Scriptorium YAML. +- Providing real credentials, unencrypted example values, or a default secret + mapping. +- Implementing Ansible Vault, choosing a vault password mechanism, or requiring + one specific inventory encryption workflow. +- Integrating an external secret manager, cloud vault, hardware-backed key + store, or dynamic credential broker. +- Making the pipeline role authoritative for files omitted from + `dnd_pipeline_secrets`, purging the whole directory, or recursively changing + pre-existing content. +- Adding a launcher script, shell profile, environment file, systemd unit, + scheduler, or persistent service merely to inject credentials. +- Passing arbitrary caller-defined environment variables through Narratio's + subprocess boundary. +- Managing human accounts or their membership in the runtime group. +- Running Audita, Notarius, or Scriptorium against a live endpoint during the + default integration suites. +- Testing Notarius checkpoint reuse end to end in the Ansible role. Narratio + and Notarius own that behavior; the role validates the selected native + configuration and immediate Ansible convergence. +- Setting Narratio's optional `notarius.chunk_cache` field redundantly when + the deployed Notarius configuration already selects `auto`. +- Changing Narratio's native forced-extraction refresh policy. +- Rotating credentials, retaining prior values, validating provider + authorization, or recovering deliberately corrupted secret files. +- Adding storage or publishing credentials before the suite selects a storage + or publishing feature. +- Modifying any upstream application source. + +## Target End State + +The standalone Narratio role installs v2.1.0 by default and can render all +three new Notarius fields when a caller supplies them. It owns no credential +files and selects no D&D-specific credential or cache policy. + +The pipeline role owns this conventional restricted subtree: + +```text +/usr/local/etc/narratio/ +├── pipeline.yml +└── secrets/ narratio:narratio 0750 + ├── AUDITA_LLM_API_KEY narratio:narratio 0640 + └── OPENROUTER_API_KEY narratio:narratio 0640 +``` + +The filenames are suite-selected reference names, never credential values. +The selected runtime user and group remain configurable through the existing +pipeline interface. + +A consumer may keep encrypted values in inventory and select explicit removal +without embedding any value in ordinary configuration: + +```yaml +dnd_pipeline_secrets: + AUDITA_LLM_API_KEY: "{{ vault_dnd_audita_llm_api_key }}" + OPENROUTER_API_KEY: "{{ vault_dnd_openrouter_api_key }}" + RETIRED_PROVIDER_KEY: null +``` + +The role owns only these three named entries for this invocation. It converges +the first two to regular files, removes the third, and leaves every omitted +name alone. The actual `vault_dnd_*` values belong in encrypted inventory and +must not appear in examples, plans, test logs, commits, or ordinary generated +configuration. + +When Narratio runs without `--secrets-dir`, it discovers the sibling directory +and loads its immediate regular files before resolving the pipeline. An +already-set process environment value wins over the corresponding file. The +base and `testing` profiles use the same delivery mechanism; profiles select +models and application profiles, not a second credential store. + +The suite deliberately uses `narratio:narratio`, directory mode `0750`, and +file mode `0640` rather than Narratio's general single-user recommendation of +`0700` and `0600`. Membership in the selected runtime group is an explicit +authorization to invoke this pipeline with its shared provider credentials. +No world access is granted. Deployments that do not trust every group member +must use separate process-environment or `--secrets-dir` provisioning and are +outside this shared-operator policy. + +Audita's proposal and validation configuration both reference +`AUDITA_LLM_API_KEY` by default. Narratio resolves that typed reference and +passes the value explicitly to the Audita child. Callers may still override +the existing Audita reference variables, but a custom split-key design also +requires an application boundary capable of forwarding both values and is not +promised by this feature. + +Narratio's effective Notarius configuration names `OPENROUTER_API_KEY`, or a +caller-selected replacement, through `notarius.llm_api_key`. Narratio resolves +the named secret and forwards only that exact name and value to Notarius. It +does not infer a provider from Notarius configuration or forward unrelated +credentials. + +Ordinary extraction sets `notarius.resume: true`. Notarius continues to own +checkpoint recording, identity, and storage beneath the configured +`/var/cache/notarius/checkpoints` root. Narratio requests reuse but does not +inspect the cache. The pipeline leaves Narratio's `notarius.chunk_cache` field +unset because the Notarius root configuration already selects `auto` at the +native owner. Forced extraction follows Narratio v2.1.0 behavior: resume is +omitted and the active chunk plan is refreshed without deleting unrelated +cache content. + +Scriptorium continues to select credential names through its PromptKit +profiles. The Narratio process receives those names from the secret bootstrap, +and its existing adapter forwards the selected profile credential. + +## Repository Ownership + +- `../narratio` at immutable tag `v2.1.0` is the read-only application + authority for secrets bootstrap, typed Notarius credential forwarding, + checkpoint reuse, forced refresh, and subprocess redaction. +- `ansible-role-narratio` advances the application release and exposes the + three native Notarius fields through its generic field-specific interface. + It does not manage secret files or select suite policy. +- `ansible-role-dnd-pipeline` owns the conventional secrets directory, managed + credential mapping, suite-level Audita and Notarius reference coordination, + checkpoint-reuse selection, consumer documentation, and behavioral tests. +- `ansible-workspace-dnd` owns this roadmap, the settled MVP policy, progress, + and independent review record. +- The Audita, Notarius, and Scriptorium application roles remain unchanged; + they continue to own only their native configuration interfaces. +- The collection, asset, remaining application, and reference repositories + remain unchanged. + +## Constraints and Settled Decisions + +- Raw credential bytes never enter ordinary application YAML, Git-tracked + examples, workspace plans, or committed test fixtures. +- The role manages secret values only through `ansible.builtin.copy` content + or an equally direct secret-safe module operation with `no_log: true`. +- The conventional directory path is fixed because automatic Narratio + discovery depends on its relationship to the fixed root pipeline document. +- Credential filenames must match the environment-variable grammar accepted + by ConfigKit. Validation exists to prevent a caller-controlled key from + becoming a path outside the secrets directory. +- Empty credential values are invalid. Null means explicit absence and is + distinct from an empty string. +- An omitted mapping key is outside the role's ownership. Exact-directory + mirroring and deletion of unlisted files are intentionally excluded. +- The shared runtime group is an authorization group. Its members may read the + managed credentials; all other local users are denied access. +- The suite's group-readable credential policy deliberately differs from + Narratio's general single-user mode recommendation. ConfigKit does not + enforce those recommended modes, and the selected policy grants no world + access. +- Application adapters, rather than Ansible, own the secure transfer of loaded + values to child processes. Ansible does not create wrappers or reconstruct + the subprocess environment. +- Notarius's root configuration remains the single owner of ordinary + chunk-plan mode. Narratio's native `resume` field expresses the distinct + request to load compatible recorded checkpoints. +- Production convergence performs no provider call, credential validity test, + cache probe, content checksum bookkeeping, backup, or post-write reread. + +## Simplest Viable Implementation Shape + +The standalone Narratio role needs only a release update and three nullable +field-specific variables rendered through its existing template. It remains +generic and does not own credential files or suite policy. + +The pipeline role needs one empty credential mapping, two Narratio Notarius +policy variables, coordinated Audita reference defaults, and ordinary +dependency wiring. Credential convergence consists of one directory task, one +validation loop, one copy loop for non-null entries, and one file loop for +explicit null removals. Both secret-bearing loops suppress their output. + +Documentation and focused native integration coverage complete the public +interface. No state probe, handler, template assembly, manifest, wrapper, +directory scan, or role-private bookkeeping is required. The validation loop +is deliberate: the mapping key becomes a filesystem path, so rejecting +anything other than a valid environment-variable name protects the declared +destination boundary. + +## Rationale and Alternatives + +Narratio's native ConfigKit bootstrap is preferable to a shell wrapper or +service-specific environment file because every pipeline-loading command uses +the same application-owned behavior, whether invoked by the service account or +an authorized human operator. It also preserves the application's existing +environment-precedence and redaction rules. + +A mapping is more ergonomic for inventory than a list of file objects because +the native identity of each entry is already its environment-variable name. +Null provides explicit removal without making an empty mapping destructive or +requiring a filesystem scan. Omitted files remain available for consumers that +combine Ansible-managed credentials with another provisioning mechanism. + +Per-user `0700` directories would follow Narratio's general recommendation but +would require separate keys or an additional invocation convention for every +authorized human. The selected shared group already defines who may execute +the suite with shared state, so group-readable and world-inaccessible +credentials provide the intended operational model with less duplication. + +Selecting Narratio checkpoint reuse completes the suite's earlier decision to +enable every available Notarius cache. Duplicating `chunk_cache: auto` in both +Narratio and Notarius configuration would add no ordinary behavior, so the +pipeline leaves the optional Narratio override unset and relies on each +application's documented ownership boundary. + +## Acceptance Criteria + +- The standalone Narratio role installs an explicitly selected v2.1.0 and can + render representative non-null and falsey values for + `narratio_notarius_llm_api_key`, `narratio_notarius_resume`, and + `narratio_notarius_chunk_cache` while omitting null values. +- The pipeline role selects Narratio v2.1.0 and pins the reviewed standalone + role revision that provides its native fields. +- The pipeline role creates `/usr/local/etc/narratio/secrets` with the selected + runtime ownership and suite credential-directory mode. +- `dnd_pipeline_secrets` defaults to an empty mapping and accepts valid + environment names with nonempty string or null values. +- Invalid names and empty non-null values fail before any destination outside + the declared secret directory can be constructed. +- Non-null entries converge to regular files with the selected runtime + ownership and mode `0640`; null entries converge to absence; omitted names + remain unmanaged. +- Credential values are suppressed from Ansible output and never appear in + application YAML, generated examples, native effective-configuration output, + test logs, or committed fixtures. +- Audita proposal and validation use `AUDITA_LLM_API_KEY` by default, and + Narratio's typed Audita field names that reference so the adapter explicitly + supplies the loaded value. +- Narratio's effective pipeline selects `OPENROUTER_API_KEY` through the typed + Notarius field and selects checkpoint resume for ordinary extraction. +- Narratio's optional chunk-cache override remains absent from the suite + configuration while Notarius's effective root configuration continues to + select `auto`. +- Scriptorium continues to receive the credential named by its selected + profile through the existing adapter behavior. +- A synthetic pipeline integration invocation with the relevant ambient + variables unset loads credentials through the conventional directory and + succeeds at native offline configuration validation as the service account + and one existing-group-member fixture. +- One representative explicit-null transition makes the selected synthetic + credential unavailable through the native bootstrap boundary without + inspecting module results or file metadata. +- A distinctive synthetic secret never appears in captured integration output. +- The baseline and representative credential applications each report + `changed=0` and `failed=0` on an immediate identical repeat. +- Syntax checking, production-profile lint, and the complete standalone + Narratio and pipeline integration entry points pass. +- No live provider, WhisperX service, storage backend, model call, or + application cache execution occurs in default tests. + +## Dependencies and Follow-up Work + +- Features 0005, 0006, 0008, 0009, and 0010 are complete prerequisites. +- Narratio v2.1.0 at immutable tag `c45e042` satisfies the former typed + Notarius credential and cache-control prerequisite. +- The completed upstream feature request was removed from Narratio's active + roadmap during its v2.1.0 release cleanup; the release reference now owns the + delivered behavior. +- The standalone Narratio-role commit produced by this feature must be + available from its canonical Git origin before the pipeline's isolated + dependency test can use it. +- Collection assembly should follow this feature so the first assembled + collection packages a pipeline with complete credential delivery and cache + reuse. +- Storage and publishing policy may later add S3 or other credential reference + names to consumer inventory without changing the file-delivery mechanism. +- Operator selection, group-membership management, services, scheduling, + upgrades, removal, and external secret-manager integration remain separate + features. + +## Potential Future Hardening + +- Integrate a dedicated external secret manager or short-lived credential + broker. +- Support distinct per-operator secret directories for deployments that do + not treat runtime-group membership as shared credential authorization. +- Add explicit credential rotation orchestration and provider-side validity + checks. +- Add platform-specific mandatory-access-control policy for credential files. diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index bf61c01..4db18c1 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -1,12 +1,617 @@ -# Implementation Plan +# Implementation Plan 0011: Managed Pipeline Credentials and Notarius Reuse -Status: pending +Status: ready -No implementation plan is currently active. Feature 0010 was implemented, -independently reviewed, and completed through pipeline-role remediation commit -`bbf01e1`. Its durable scope, decisions, and acceptance criteria remain in -[Feature 0010](features/0010-dnd-notarius-extraction-integration.md), and the -completed review record remains in [the remediation document](remediation.md). +Target executor: GPT-5.6-Terra -Select and approve the next unit of work before replacing this notice with a -new staged implementation plan. +Feature roadmap: +`docs/roadmap/features/0011-managed-pipeline-credential-delivery.md` + +Active plan location: `docs/roadmap/implementation.md` + +## Objective + +Upgrade the generic Narratio role to v2.1.0 and expose its native Notarius +credential and cache controls, then have the D&D pipeline role deliver shared +provider credentials through Narratio's conventional secrets directory, +coordinate the Audita and Notarius credential references, and request +compatible Notarius checkpoint reuse. + +The feature roadmap remains authoritative for scope, rationale, target end +state, and acceptance criteria. Decisions 0001 and 0002 remain authoritative +for direct Ansible convergence and the shared-runtime-group authorization +boundary. + +## Execution Rules + +- Execute stages in numeric order. +- Before each stage, confirm its preconditions and the working-tree state of + its owning repository. +- Before each stage, reread every document listed in that stage's Required + Policy Review. Do not rely on policy recalled from an earlier stage. +- Preserve unrelated and pre-existing changes. Stop if an owning repository is + unexpectedly dirty at a stage boundary. +- Keep `ansible-role-narratio` generic. It exposes Narratio's native optional + fields but does not select D&D credentials, manage secret files, or own + suite cache policy. +- Keep credential delivery and cross-application policy in + `ansible-role-dnd-pipeline`. Do not modify an application source repository + or move suite policy into the Audita, Notarius, or Scriptorium roles. +- Never place a real credential in source, documentation, examples, test + fixtures, command output, or commits. Tests may use distinctive synthetic + values and must prove that those values do not appear in captured output. +- Treat a `dnd_pipeline_secrets` key as untrusted path input. Validate the + complete mapping before any key is interpolated into a destination path. + Accept only environment-variable names matching + `^[A-Za-z_][A-Za-z0-9_]*$`; accept only nonempty strings or null as values. +- Apply `no_log: true` to every production task that evaluates, writes, or + removes mapping entries. Do not expose a loop label containing a credential + value. +- Use direct module-owned convergence: one fixed secrets-directory task, one + validation loop, one copy loop for non-null entries, and one file loop for + explicit null entries. Do not add preliminary `stat`, directory scans, + recursive ownership repair, checksums, handlers, wrappers, manifests, state + files, backups, or post-write validation tasks. +- An omitted credential key is unmanaged. Do not mirror the mapping onto the + directory, purge unlisted files, or infer that an empty mapping removes + anything. +- Keep ordinary application YAML secret-free. It may contain only credential + reference names. +- Leave Narratio's `notarius.chunk_cache` unset in the pipeline role. The + deployed Notarius root configuration remains the sole owner of ordinary + `auto` chunk-plan reuse, while Narratio owns its native forced-refresh + behavior. +- Test role-owned behavior through public role inputs and native application + commands. Do not test Ansible module guarantees, inspect module results, + assert task order, snapshot complete default documents, contact a live + provider, or execute the application pipeline. +- Run every stage's validation before committing it. Commit each completed + stage when it leaves its owning repository valid, using the plain-English + commit intent specified by that stage. +- Do not begin a dependent stage after an earlier stage fails validation. +- The pipeline integration suite installs role dependencies from canonical Git + origins. The Stage 1 Narratio-role commit must therefore be reachable from + its canonical origin before Stage 2 begins. If a fresh Galaxy installation + cannot resolve it, stop after Stage 1 and report the commit hash for + publication; do not substitute a branch, local role path, or weakened test. + +## Starting State and Preconditions + +- The workspace control repository contains the reviewed Feature 0011 roadmap + and this implementation plan in a clean planning commit on `main`. +- Features 0001 through 0010 and the standalone-role test-suite simplification + are complete. +- The implementation repositories are clean on `main` at these reviewed + starting commits: + + | Repository | Starting commit | + | --- | --- | + | `ansible-role-narratio` | `eeb1105590e638c1f46b42c98a5dbbce4ddeea86` | + | `ansible-role-dnd-pipeline` | `bbf01e107a5e4e70a2abd6c696bfdb9ccc38bd7e` | + +- The Narratio role matches `origin/main`. The pipeline role is intentionally + ahead of its canonical origin and must not be reset or replaced. +- `../narratio` is clean at immutable tag `v2.1.0`, commit + `c45e042462b77a8873b4215edc40e3840149db34`. +- Narratio v2.1.0 adds optional `notarius.llm_api_key`, `notarius.resume`, and + `notarius.chunk_cache` fields without requiring migration of existing v2 + configuration. Its conventional secrets bootstrap loads immediate regular + files from a sibling `secrets/` directory, gives an existing process + environment value precedence, and requires every configured typed reference + to resolve during configuration loading. +- Notarius v0.7.0 is already selected by the suite, records checkpoints under + the persistent FHS cache root, and leaves ordinary chunk-plan mode at + `auto`. +- PromptKit documents `OPENROUTER_API_KEY` as the credential name for the + OpenRouter backend selected by the suite's production Notarius profile. +- The workspace `.venv` provides Ansible Core 2.21.3 and ansible-lint 26.8.0. +- The local integration environment remains an accepted proxy for Ubuntu + 26.04, supports task-level privilege escalation, and provides ordinary POSIX + group and ACL behavior. + +## Repository Scope + +Repositories that change: + +- `ansible-role-narratio`: Narratio v2.1.0 selection, three nullable native + Notarius fields, generic documentation, and focused integration coverage. +- `ansible-role-dnd-pipeline`: compatible Narratio role and application pins, + credential delivery, Audita and Notarius reference coordination, checkpoint + reuse, consumer documentation, and focused integration coverage. +- `ansible-workspace-dnd`: implementation progress and review handoff only + after the role stages are complete. + +Repositories that may be inspected but must remain unchanged: + +- `../narratio`, which is the read-only v2.1.0 application authority. +- `../notarius`, `../audita`, `../seriatim`, `../scriptorium`, and PromptKit, + which may be consulted for current native contracts. +- `ansible-role-notarius`, `ansible-role-audita`, + `ansible-role-seriatim`, and `ansible-role-scriptorium`. +- `ansible-collection-dnd`, all asset repositories, `rakestraw-ansible`, and + every other repository in `workspace.yml`. + +## Stage 1: Upgrade the Generic Narratio Role + +### Objective + +Make the standalone role install Narratio v2.1.0 and expose the release's +three optional Notarius fields without adding suite-specific credential or +cache policy. + +### Required Policy Review + +Before beginning this stage, read: + +- `AGENTS.md` +- `docs/orientation.md` +- `docs/conventions.md` +- `docs/ansible.md` +- `docs/testing.md` +- `docs/roadmap/features/0011-managed-pipeline-credential-delivery.md` +- `docs/decisions/0001-minimal-module-driven-ansible.md` +- `../narratio/docs/releases/v2.1.0.md` +- `../narratio/docs/config.md` +- `../narratio/docs/integrations/notarius.md` +- `../narratio/examples/pipeline.full.annotated.yml` +- `ansible-role-narratio/defaults/main.yml` +- `ansible-role-narratio/meta/argument_specs.yml` +- `ansible-role-narratio/templates/pipeline.yml.j2` +- `ansible-role-narratio/tests/integration` +- `ansible-role-narratio/tests/representative.yml` +- `ansible-role-narratio/tests/verify-baseline.yml` +- `ansible-role-narratio/tests/verify-config.yml` +- `ansible-role-narratio/README.md` + +### Preconditions + +- `ansible-role-narratio` is clean at its stated starting commit. +- Its existing `tests/integration` entry point passes before modification. +- The upstream files listed above remain at the reviewed immutable v2.1.0 tag. + +### Owning Repository + +`ansible-role-narratio` + +### Required Changes + +1. Advance `narratio_version` from `v2.0.0` to `v2.1.0`. Do not change the + established source checkout, build condition, metadata convergence, or + application-account task flow. +2. Add these optional public variables beside the existing Notarius variables, + each defaulted to null: + `narratio_notarius_llm_api_key`, `narratio_notarius_resume`, and + `narratio_notarius_chunk_cache`. +3. Add matching optional argument-specification entries. Use `str` for the + credential reference and chunk-cache mode and `bool` for resume. Do not + duplicate Narratio's native enum or secret-resolution validation in the + role. +4. Extend the existing `notarius` template section and its parent-emission + condition. Render `llm_api_key`, `resume`, and `chunk_cache` only when their + corresponding variable `is not none`; preserve intentional `false` for + resume and serialize every value with the template's established filter. +5. Leave all three fields absent when null. Do not create a secrets directory, + read an environment variable, accept a raw credential value, or select a + Notarius provider in this standalone role. +6. Update the README's version, optional-variable list, native Notarius field + discussion, and validation summary. Explain that the credential field is + an environment-variable name and that credential delivery remains the + caller's responsibility. Describe resume and chunk-cache as native + application choices without assigning D&D defaults. +7. Update the integration suite to explicitly select v2.1.0 for the existing + version-selection assertion. Continue testing version selection rather than + asserting that v2.1.0 remains the default. +8. Extend the representative configuration case with a synthetic environment + reference, `resume: false` as the falsey-value case, and one valid explicit + chunk-cache mode. Supply a synthetic value for that named environment + variable only when running Narratio's native validation or safe inspection; + never put a credential value in managed YAML. +9. Observe the three representative fields and baseline null omission through + the repository's existing focused configuration checks. Retain native + `narratio config validate` coverage and immediate clean repeated + convergence. Do not test ConfigKit's resolution precedence or Narratio's + subprocess forwarding implementation. + +### Validation + +Run from `ansible-role-narratio`: + +```text +tests/integration +git diff --check +git status --short +``` + +Expected result: syntax and production-profile lint pass; an explicitly +selected v2.1.0 binary reports that version; baseline null values remain +omitted; representative credential-name, false resume, and chunk-cache values +load through Narratio's native configuration interface; immediate repeats +report `changed=0` and `failed=0`; and only intended role, test, and README +files are modified. + +### Completion Criteria + +- Defaults, argument specification, template, README, and tests expose one + coherent generic interface for the three v2.1.0 fields. +- The role owns no credential value, secrets directory, D&D provider choice, + or suite cache policy. +- Tests exercise the public configuration interface without snapshotting the + complete default document or re-testing application internals. +- The repository is valid and the stage is committed. +- The commit is reachable from the canonical Narratio-role origin before + Stage 2 begins. + +### Commit + +Commit intent: `Upgrade the Narratio role to version 2.1.0` + +## Stage 2: Implement Pipeline Credential Delivery and Reuse Policy + +### Objective + +Add the pipeline role's production credential-delivery tasks, coordinate its +Audita and Notarius references, and enable Narratio checkpoint reuse through +the reviewed Stage 1 interface. + +### Required Policy Review + +Before beginning this stage, read: + +- `AGENTS.md` +- `docs/orientation.md` +- `docs/conventions.md` +- `docs/ansible.md` +- `docs/testing.md` +- `docs/roadmap/features/0011-managed-pipeline-credential-delivery.md` +- `docs/decisions/0001-minimal-module-driven-ansible.md` +- `docs/decisions/0002-shared-readable-pipeline-filesystem-policy.md` +- the committed Stage 1 changes in `ansible-role-narratio` +- `../narratio/docs/releases/v2.1.0.md` +- `../narratio/docs/config.md` +- `../narratio/docs/integrations/audita.md` +- `../narratio/docs/integrations/notarius.md` +- `../narratio/docs/integrations/scriptorium.md` +- `ansible-role-dnd-pipeline/defaults/main.yml` +- `ansible-role-dnd-pipeline/meta/argument_specs.yml` +- `ansible-role-dnd-pipeline/meta/main.yml` +- `ansible-role-dnd-pipeline/requirements.yml` +- `ansible-role-dnd-pipeline/tasks/main.yml` +- `ansible-role-dnd-pipeline/tests/converge.yml` +- `ansible-role-dnd-pipeline/tests/integration` + +### Preconditions + +- Stage 1 is committed, validated, and clean. +- The Stage 1 commit is reachable from the canonical + `ansible-role-narratio` Git origin. Stop and report the hash if a fresh + Galaxy installation cannot resolve it. +- `ansible-role-dnd-pipeline` is clean at its stated starting commit or an + expected reviewed descendant. +- Its existing `tests/integration` entry point passes before modification. + +### Owning Repository + +`ansible-role-dnd-pipeline` + +### Required Changes + +1. Set `dnd_pipeline_narratio_version: v2.1.0` and pin the + `rakestraw.narratio` entry in `requirements.yml` to the exact Stage 1 + commit. Retain the canonical Git URL and all unrelated dependency pins. +2. Add `dnd_pipeline_secrets: {}` as a required, defaulted dictionary in the + public interface. Its keys are credential filenames/environment-variable + names; values are either nonempty strings to converge or null to remove the + exact named file. +3. Add required, defaulted + `dnd_pipeline_narratio_notarius_llm_api_key: OPENROUTER_API_KEY` as a string + and `dnd_pipeline_narratio_notarius_resume: true` as a boolean. Do not add a + suite variable for Narratio's optional chunk-cache override. +4. Change the suite default for + `dnd_pipeline_audita_llm_validation_api_key_env` from + `AUDITA_VALIDATION_LLM_API_KEY` to `AUDITA_LLM_API_KEY`. Retain the + proposal and validation fields as independently overrideable public + variables; do not add an equality assertion or promise split-key forwarding. +5. Pass `dnd_pipeline_audita_llm_proposal_api_key_env` through Narratio's + `narratio_audita_llm_api_key` interface. Pass the new Notarius reference and + resume variables through `narratio_notarius_llm_api_key` and + `narratio_notarius_resume`. Do not pass + `narratio_notarius_chunk_cache`; Notarius's root `auto` setting remains in + control for ordinary runs. +6. Before any task interpolates a mapping key into a path, validate every + `dnd_pipeline_secrets | dict2items` entry in one `ansible.builtin.assert` + loop. Require a string key matching `^[A-Za-z_][A-Za-z0-9_]*$` and a value + that is either null or a nonempty string. Apply `no_log: true` to the entire + validation task. Do not normalize, trim, rename, or infer keys or values. +7. Create `/usr/local/etc/narratio/secrets` with one ordinary + `ansible.builtin.file` task using the selected runtime user and group and + fixed mode `0750`. Do not recursively alter existing entries. +8. Converge non-null mapping entries with one `ansible.builtin.copy` loop to + `/usr/local/etc/narratio/secrets/`, using the selected runtime user and + group and fixed mode `0640`. Write only the supplied value; do not append + metadata, encode it, back it up, or reread it. Apply `no_log: true`. +9. Converge null entries to absence with one `ansible.builtin.file` loop over + the same mapping. Remove only the exact validated named file and apply + `no_log: true`. An empty mapping and omitted keys must leave existing + directory entries untouched. +10. Keep production convergence to those four operations: validation, + directory creation, non-null copy, and explicit-null removal. Do not add + `stat`, `find`, commands, handlers, environment files, shell profiles, + launchers, services, providers, rotation, or role-private state. +11. Adapt the existing integration driver's baseline arguments to supply + distinctive synthetic values for `AUDITA_LLM_API_KEY` and + `OPENROUTER_API_KEY` through `dnd_pipeline_secrets`, with both ambient + variables explicitly unset. Preserve isolated dependency installation and + cleanup restoration. +12. Extend the existing native Narratio effective-configuration check to + observe the two public credential reference names and `resume: true` while + confirming that Narratio's `chunk_cache` override is absent. Retain the + native Notarius check that its root chunk-plan mode is `auto`, the service + and existing-group-member validation paths, and the baseline clean repeat. + +### Validation + +Run from `ansible-role-dnd-pipeline`: + +```text +tests/integration +git diff --check +git status --short +``` + +Expected result: isolated role installation resolves the exact Stage 1 +commit; syntax and production-profile lint pass; the role converges the +synthetic credential mapping without displaying its values; Narratio natively +loads the conventional secrets directory as both the service account and the +existing-group-member fixture; its safe effective configuration names the +Audita and Notarius references and enables resume without a chunk-cache +override; Notarius retains `auto`; the baseline repeat reports `changed=0` and +`failed=0`; and only intended pipeline-role files are modified. + +### Completion Criteria + +- The suite installs Narratio v2.1.0 through the exact reviewed role revision. +- One public mapping owns only explicitly named credential files, including + explicit null removal, behind complete pre-path validation and secret-safe + task output. +- Audita's suite defaults use one typed reference, Notarius uses + `OPENROUTER_API_KEY`, and ordinary extraction requests checkpoint reuse. +- The pipeline does not duplicate Notarius's chunk-plan policy or add a second + credential-loading mechanism. +- The existing integration suite passes and the repository is committed. + +### Commit + +Commit intent: `Add managed credentials to the D&D pipeline` + +## Stage 3: Document and Exercise the Credential Contract + +### Objective + +Document the pipeline's credential and reuse policy and add the smallest +behavioral coverage for validation, ownership semantics, explicit removal, +redaction, authorization, and immediate idempotence. + +### Required Policy Review + +Before beginning this stage, read: + +- `AGENTS.md` +- `docs/orientation.md` +- `docs/conventions.md` +- `docs/ansible.md` +- `docs/testing.md` +- `docs/roadmap/features/0011-managed-pipeline-credential-delivery.md` +- `docs/decisions/0002-shared-readable-pipeline-filesystem-policy.md` +- the committed Stage 1 and Stage 2 changes +- `../narratio/docs/config.md` +- `../narratio/docs/cli.md` +- `../narratio/docs/integrations/notarius.md` +- `ansible-role-dnd-pipeline/README.md` +- `ansible-role-dnd-pipeline/tests/integration` +- `ansible-role-dnd-pipeline/tests/converge.yml` + +### Preconditions + +- Stages 1 and 2 are committed, validated, and clean. +- The pipeline's isolated dependency installation resolves the Stage 1 commit. +- The deployed baseline can be restored through the integration driver's + existing cleanup path. + +### Owning Repository + +`ansible-role-dnd-pipeline` + +### Required Changes + +1. Update the README's compatible Narratio version, Audita validation + credential default, public variable documentation, and obsolete statements + that API-key management and checkpoint reuse are deferred. +2. Document `dnd_pipeline_secrets` as an empty-by-default mapping with valid + environment-variable keys, nonempty string values for presence, null for + exact removal, and omission for unmanaged entries. Explain that the role + neither mirrors nor purges the directory. +3. Include an Ansible Vault inventory pattern that references encrypted + variables without showing a real credential. State that Vault is a + recommended consumer workflow, not functionality implemented by the role. +4. Document the fixed conventional path, `0750` directory and `0640` file + modes, selected runtime ownership, runtime-group authorization meaning, + process-environment precedence, and the absence of wrappers, services, or + parallel loaders. Make clear that ordinary configuration remains + world-readable and secret-free while the secrets subtree does not grant + world access. +5. Document the coordinated `AUDITA_LLM_API_KEY` default, the typed + `OPENROUTER_API_KEY` Notarius reference, Scriptorium's existing + profile-owned reference behavior, ordinary checkpoint resume, Notarius's + root-owned `auto` chunk cache, and Narratio's forced refresh behavior. +6. Add one representative expected-failure case for an invalid mapping key + and one for an empty non-null value. Require each convergence to fail before + any candidate destination is managed. Keep task output secret-safe, do not + assert a task name or other implementation trace, and do not add an + exhaustive validation matrix. +7. Exercise explicit-null removal with a uniquely named synthetic credential: + first manage it, select its name through the public Notarius reference + variable, and confirm native configuration loading succeeds. Then converge + the same mapping entry to null with the relevant ambient variable unset. + Observe unavailability through an expected failure from Narratio's native + configuration loader rather than by inspecting a module result or file + metadata. +8. Require an immediate identical repeat of the explicit-null state to report + `changed=0` and `failed=0`, then restore the baseline credential mapping so + cleanup and later tests remain deterministic. +9. Retain native configuration validation as the service account and the + existing-group-member fixture. Add at most one representative native + negative check showing that an identity outside the runtime group cannot + load the credential-backed pipeline; do not build a permission matrix or + assert numeric filesystem metadata. +10. Capture convergence and native inspection output and fail if either + distinctive synthetic credential value appears. Names may appear in safe + effective configuration; values must not. +11. Retain the existing baseline, testing-profile, selected-version, + caller-composed Notarius pipeline, shared-cache, immutable local asset, + and clean-repeat coverage. Do not contact an LLM, WhisperX, storage, or + publishing endpoint; do not execute extraction or test ConfigKit, + Narratio, Notarius, or trusted Ansible modules independently. + +### Validation + +Run from `ansible-role-dnd-pipeline`: + +```text +tests/integration +git diff --check +git status --short +``` + +Expected result: isolated dependency installation, syntax checking, and +production-profile lint pass; valid synthetic credentials load through the +conventional directory for authorized identities; invalid names and empty +values fail safely; explicit null makes only the selected credential +unavailable through native loading and converges cleanly on repeat; no +synthetic value appears in captured output; all existing representative paths +continue to pass; and documentation matches the public interface. + +### Completion Criteria + +- Consumer documentation completely describes the mapping, Vault usage, + authorization boundary, credential flows, cache policy, and exclusions. +- Tests protect role-owned validation, explicit ownership semantics, + redaction, authorized use, and clean repetition without testing module or + application internals. +- The default suite and representative explicit-null state each converge + cleanly on immediate repeat. +- The full integration suite passes and the repository is committed. + +### Commit + +Commit intent: `Document and test pipeline credential delivery` + +## Stage 4: Record the Implementation for Review + +### Objective + +Record the completed implementation and validation evidence in the workspace +without declaring the feature complete before independent review. + +### Required Policy Review + +Before beginning this stage, read: + +- `AGENTS.md` +- `docs/orientation.md` +- `docs/conventions.md` +- `docs/planning.md` +- `docs/roadmap/features/0011-managed-pipeline-credential-delivery.md` +- `docs/roadmap/implementation.md` +- `docs/status.md` + +### Preconditions + +- Stages 1 through 3 are committed and their owning repositories are clean. +- Both complete integration entry points pass at the committed revisions. +- The workspace control repository is clean at the planning commit. + +### Owning Repository + +`ansible-workspace-dnd` + +### Required Changes + +1. Update this plan's Progress Notes with the three implementation commit + hashes and concise validation results. +2. Update `docs/status.md` so Feature 0011 is `in_progress`, its evidence names + the implementation commits, and the current focus is independent review of + the completed implementation. +3. Keep the feature roadmap `ready`; do not mark Feature 0011 `done`, clean the + durable roadmap, or create or revise `docs/roadmap/remediation.md`. Those + actions belong to independent review and closeout. +4. Do not change implementation repositories or unrelated workspace policy in + this stage. + +### Validation + +Run from the workspace root: + +```text +git diff --check +./scripts/status +git status --short +``` + +Expected result: the status report identifies only the intended workspace +tracking edits before commit; both implementation repositories are clean; and +the dashboard and progress notes point to the exact completed stage commits. + +### Completion Criteria + +- Implementation and validation evidence is traceable across repositories. +- Feature 0011 remains awaiting independent review rather than being marked + complete. +- The workspace control repository is valid and the stage is committed. + +### Commit + +Commit intent: `Record the pipeline credential implementation` + +## Final Validation and Handoff + +After Stage 4, rerun both owning repositories' complete integration entry +points: + +```text +cd ansible-role-narratio && tests/integration +cd ../ansible-role-dnd-pipeline && tests/integration +``` + +Then, from the workspace root, run: + +```text +git diff --check +./scripts/status +``` + +The final handoff must: + +- identify the Stage 1 through Stage 4 commit hashes and confirm every changed + repository has a clean working tree; +- confirm syntax and production-profile lint pass in both changed roles; +- confirm the standalone role installs an explicitly selected Narratio v2.1.0, + exposes all three nullable native fields, validates representative values, + and immediately converges cleanly; +- confirm the pipeline installs the exact immutable Narratio-role commit, + loads managed synthetic credentials for authorized identities, rejects bad + mapping entries before path use, removes an explicitly null entry, suppresses + credential values, requests Notarius checkpoint reuse, leaves ordinary + chunk-plan mode with Notarius, and immediately converges cleanly; +- confirm no upstream application, asset, collection, unrelated standalone + role, real credential, external service, or real human account was modified; +- summarize any deviation, blocker, or deferred work, including any pause + required to publish the Stage 1 dependency commit; +- report any production complexity beyond the three generic Narratio fields, + suite variables and dependency wiring, and the four direct credential tasks, + together with the acceptance criterion that required it; and +- leave Feature 0011 `in_progress` for independent review rather than marking + it `done`. + +## Progress Notes + +- Planning complete; implementation has not begun. diff --git a/docs/roadmap/mvp.md b/docs/roadmap/mvp.md index 2476db7..2f8316e 100644 --- a/docs/roadmap/mvp.md +++ b/docs/roadmap/mvp.md @@ -255,9 +255,27 @@ application-role releases in one source repository. - Debug output is not enabled by the MVP. Any later debug feature must either produce world-readable non-secret D&D diagnostics or identify and justify a narrower application contract before deployment. -- Credential material will live beneath a restricted `secrets/` subdirectory - owned by `narratio:narratio`, with directories mode `0750` and files mode - `0640`. Ordinary configuration contains only credential reference names. +- Credential material lives beneath the conventional + `/usr/local/etc/narratio/secrets` subdirectory owned by + `narratio:narratio`, with directory mode `0750` and file mode `0640`. + Membership in the `narratio` group is an explicit authorization to use the + suite's shared credentials. This deliberately differs from Narratio's + general single-user `0700` and `0600` recommendation while retaining no + world access. Ordinary configuration contains only credential reference + names. +- The pipeline role exposes one empty-by-default credential mapping. Non-null + entries manage files named for valid environment variables; null explicitly + removes the named file; omitted names remain unmanaged. Credential values + are expected to come from encrypted inventory such as Ansible Vault and are + suppressed from task output. +- Narratio's native ConfigKit secrets bootstrap owns loading these files and + process-environment precedence. The suite does not add a wrapper, shell + profile, service environment file, or parallel credential loader. +- The suite coordinates Audita proposal and validation on the + `AUDITA_LLM_API_KEY` reference and selects `OPENROUTER_API_KEY` for + Notarius's production extraction profile. Narratio v2.1.0 resolves those + typed references and forwards only the selected credentials to the + corresponding subprocesses. - [Decision 0002](../decisions/0002-shared-readable-pipeline-filesystem-policy.md) records the complete rationale and boundary for this policy. @@ -309,8 +327,9 @@ application-role releases in one source repository. cache beneath `/var/cache/notarius`. That FHS cache root is owned by the `narratio` account and group with mode `2775` and the suite's shared default ACL policy. Notarius v0.7.0 leaves cache-entry access to that deployment - policy. Chunk plans are reused automatically; checkpoints are recorded but - Narratio does not yet request their reuse. + policy. Chunk plans are reused automatically. Narratio v2.1.0 requests reuse + of compatible recorded checkpoints during ordinary extraction; forced + extraction omits checkpoint loading and refreshes the active chunk plan. - The standalone Notarius role exposes Notarius v0.7.0's native ConfigKit import list while remaining independent of D&D content. The pipeline role owns an imported `pipelines/dnd-session.yml` fragment containing its @@ -378,7 +397,6 @@ The following areas require investigation and decisions before implementation: - The mechanism used to incorporate the independently developed pipeline role into collection releases -- Secret injection and storage - Optional suite-level operator-selection and group-membership management - Upgrade, downgrade, and removal behavior - Collection bootstrap and end-to-end validation details beyond the locked diff --git a/docs/status.md b/docs/status.md index e3f4417..37a2134 100644 --- a/docs/status.md +++ b/docs/status.md @@ -1,10 +1,10 @@ # Workspace Status -Last updated: 2026-09-15 +Last updated: 2026-09-16 Current milestone: M1-03 suite-level pipeline role -Current focus: Select the next unit of work. +Current focus: Begin the Feature 0011 implementation sprint. | ID | Deliverable | Owner | State | Evidence or next step | | --- | --- | --- | --- | --- | @@ -15,7 +15,7 @@ Current focus: Select the next unit of work. | M0-05 | Suite-level role repository | ansible-role-dnd-pipeline | done | Repository created and added to `workspace.yml` | | M1-01 | Remaining MVP design decisions | workspace | done | Feature 0003 roadmap fixes the Notarius configuration interface | | M1-02 | Standalone application roles | role repositories | done | All five standalone roles implemented and independently reviewed | -| M1-03 | Suite-level pipeline role | ansible-role-dnd-pipeline | in_progress | Feature 0010 is complete; select the next suite-policy feature | +| M1-03 | Suite-level pipeline role | ansible-role-dnd-pipeline | in_progress | Execute the ready Feature 0011 implementation plan | | M1-04 | Collection assembly | ansible-collection-dnd | proposed | Select and implement the pipeline-role incorporation mechanism | | F0001 | Install Seriatim from pinned source | ansible-role-seriatim | done | Implementation, integration coverage, and final remediation completed through `fb8dc41` | | F0002 | Install and configure Audita | ansible-role-audita | done | Field-specific configuration completed in `e61f02f` and `847c4c3`; full suite passed | @@ -27,6 +27,7 @@ Current focus: Select the next unit of work. | F0008 | Define opinionated D&D processing configuration | ansible-role-dnd-pipeline | done | Implemented and independently reviewed through remediation commit `3e253d7` | | F0009 | Establish shared pipeline filesystem and execution | configuration-bearing application roles and ansible-role-dnd-pipeline | done | Implemented and independently reviewed through remediation commit `b15fe8d` | | F0010 | Integrate D&D Notarius extraction | ansible-role-notarius and ansible-role-dnd-pipeline | done | Implemented and independently reviewed through remediation commit `bbf01e1` | +| F0011 | Deliver pipeline credentials and Notarius reuse | ansible-role-narratio and ansible-role-dnd-pipeline | ready | Staged implementation plan is ready at `docs/roadmap/implementation.md` | | A0001 | Audit completed standalone-role test suites | workspace and standalone roles | done | Findings recorded in `docs/roadmap/test-suite-audit-findings.md` | | R0001 | Simplify completed standalone-role test suites | workspace and standalone roles | done | Role commits `e08c63c`, `d2d0b9d`, and `cfecf37` passed implementation and independent review |