From fc803587b8fc735f3dfc3cff8bbbb1d6ce98910c Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Tue, 8 Sep 2026 00:16:12 +0000 Subject: [PATCH] Revise the Seriatim plan and workspace conventions --- AGENTS.md | 102 +--- docs/conventions.md | 49 ++ .../0001-minimal-module-driven-ansible.md | 52 ++ docs/orientation.md | 44 ++ docs/planning.md | 44 ++ .../0001-seriatim-source-installation.md | 282 ++++++---- ...a-source-installation-and-configuration.md | 35 ++ docs/roadmap/features/template.md | 13 + docs/roadmap/implementation-template.md | 12 + docs/roadmap/implementation.md | 513 +----------------- docs/roadmap/mvp.md | 33 +- docs/roadmap/remediation.md | 185 +------ docs/status.md | 10 +- 13 files changed, 479 insertions(+), 895 deletions(-) create mode 100644 docs/conventions.md create mode 100644 docs/decisions/0001-minimal-module-driven-ansible.md create mode 100644 docs/orientation.md create mode 100644 docs/planning.md create mode 100644 docs/roadmap/features/0002-audita-source-installation-and-configuration.md diff --git a/AGENTS.md b/AGENTS.md index e017895..77d0653 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,96 +7,14 @@ deploy the D&D transcript-processing pipeline. It owns cross-repository plans, decisions, status, and workspace tooling. Each application and Ansible project is an independent Git repository. -## Repository Map +## Required Reading -Repositories inside this directory: - -- `ansible-collection-dnd`: the `rakestraw.dnd` collection that bundles and - distributes the pipeline suite. -- `ansible-role-dnd-pipeline`: independent source repository for the - suite-level role exposed by the collection as `rakestraw.dnd.pipeline`. -- `ansible-role-{narratio,notarius,audita,seriatim,scriptorium}`: standalone - application roles. -- `rakestraw-ansible`: working deployment repository containing the - `pipeline_dnd` proof of concept; treat it as read-only reference unless the - task explicitly includes it. - -Relevant upstream repositories are siblings of this directory: - -- `../{narratio,notarius,audita,seriatim,scriptorium}`: application source and - authoritative application contracts. -- `../narratio-prompts`: pipeline prompt-library content. - -Use `workspace.yml` as the complete machine-readable inventory of paths and -remotes. - -## Sources of Truth - -- `docs/roadmap/mvp.md`: settled MVP scope, architecture, and open design work. -- `docs/roadmap/features/`: durable scope and target state for selected units - of work. -- `docs/status.md`: current cross-repository progress and next work. -- `docs/decisions/`: durable rationale for significant architectural choices. -- `docs/roadmap/implementation.md`: the single active staged implementation - plan, replaced for each selected feature after its feature roadmap is agreed. -- Application repositories: CLI, configuration, build, and integration - contracts. -- Individual Ansible repositories: implementation and repository-local tests. - -Do not duplicate an application contract in workspace documentation when a -link and a concise deployment consequence are sufficient. - -## Working Conventions - -- Confirm the target repository before editing; never treat this directory as - a monorepo. -- Inspect the relevant upstream application before designing or changing its - Ansible role. -- Keep standalone application roles independently usable. Cross-application - policy belongs in `ansible-role-dnd-pipeline` and is exposed to consumers as - `rakestraw.dnd.pipeline`. -- Prefer `ansible.builtin` modules where they directly fit the task. - `community.general` is an approved, stable dependency and its modules may be - selected freely when appropriate; declare and pin the collection wherever a - role or collection depends on it. -- Prefer codebase-memory graph tools for code discovery. Use text/file search - for documentation, configuration, literals, or when the graph is - insufficient. -- Preserve unrelated and pre-existing working-tree changes in every - repository. -- Never place credentials or secret values in plans, examples, inventory, or - tests. -- Validate changes in the repository that owns them and report the commands - run. - -## Planning and Progress - -- Update `docs/roadmap/mvp.md` when a design question becomes a settled scope - or architecture decision. -- Discuss and select the next unit of work before planning implementation. -- Give each selected feature a four-digit identifier and a durable document at - `docs/roadmap/features/-.md`. Add the same identifier to - `docs/status.md`. -- Write and agree on the feature roadmap before writing its implementation - plan. The roadmap owns scope, target end state, rationale, constraints, and - acceptance criteria. -- Always write the current implementation plan to - `docs/roadmap/implementation.md`. Replace that file when planning the next - selected feature; Git history preserves prior plans when needed. -- Write implementation plans as dependency-ordered stages suitable for - GPT-5.6-Terra to execute sequentially. Number stages with consecutive - integers beginning with `Stage 1`; do not skip numbers or use nested stage - numbering. Each stage must identify its repository, preconditions, required - behavior, validation, completion criteria, and expected commit. -- Resolve architectural choices during roadmap and plan review. Do not leave - unresolved design judgment to the implementation agent. -- Update `docs/status.md` when cross-repository work starts, becomes blocked, - or is completed. Completed items must point to concrete evidence. -- Out-of-band implementation should commit each completed stage when that - stage leaves its owning repository valid. Review the implementation and its - validation evidence before marking the feature complete. -- Retain feature roadmaps as durable records. Remove or archive obsolete - implementation detail after completion; durable architectural rationale - belongs in `docs/decisions/`. -- Use the status values `proposed`, `ready`, `in_progress`, `blocked`, `done`, - and `deferred`. +- Read [the workspace orientation](docs/orientation.md) before selecting, + inspecting, or modifying a repository. It maps the repositories and defines + the canonical sources of truth. +- Read [the workspace conventions](docs/conventions.md) before reviewing or + changing Ansible, preparing a feature roadmap or implementation plan, or + making any repository change. It defines the normative house style and + working conventions. +- Read [the planning policy](docs/planning.md) before preparing a feature + roadmap or implementation plan or managing an ongoing work stream. diff --git a/docs/conventions.md b/docs/conventions.md new file mode 100644 index 0000000..266bc2b --- /dev/null +++ b/docs/conventions.md @@ -0,0 +1,49 @@ +# Workspace Conventions + +## Working Conventions + +- Confirm the target repository before editing; never treat this directory as + a monorepo. +- Inspect the relevant upstream application before designing or changing its + Ansible role. +- Keep standalone application roles independently usable. Cross-application + policy belongs in `ansible-role-dnd-pipeline` and is exposed to consumers as + `rakestraw.dnd.pipeline`. +- Prefer codebase-memory graph tools for code discovery. Use text/file search + for documentation, configuration, literals, or when the graph is + insufficient. +- Preserve unrelated and pre-existing working-tree changes in every + repository. +- Never place credentials or secret values in plans, examples, inventory, or + tests. +- Validate changes in the repository that owns them and report the commands + run. +- Do not duplicate an application contract in workspace documentation when a + link and a concise deployment consequence are sufficient. + +## Ansible House Style (Normative) + +- Always use the smallest clear sequence of declarative tasks that reaches the + required end state. +- Prefer `ansible.builtin` modules where they directly fit the task. + `community.general` is an approved, stable dependency and its modules may be + selected freely when appropriate; declare and pin the collection wherever a + role or collection depends on it. +- Trust the documented contract of each Ansible module, including its + idempotence and change detection. +- Gather state only when the result controls necessary behavior that the + responsible module cannot determine. Fingerprints, checksums, sidecar state, + manual drift detection, and speculative recovery paths require an explicit + acceptance criterion and rationale. Do not precede a declarative module with + a probe or recreate its internal logic unless a concrete requirement cannot + be expressed by the module itself. +- Consider whether a conditional task or a notified handler best expresses the + dependency. A `when` condition keeps triggers visible at the action site but + grows with their number. Notifications keep the action independent of the + number of notifiers, but distribute the control flow and defer execution + until the next handler flush. An explicit + `ansible.builtin.meta: flush_handlers` affects all pending handlers in the + play. +- Treat `rakestraw-ansible` as a style reference for direct tasks, shallow + control flow, and concise variable use, while retaining current practices + such as fully qualified collection names. diff --git a/docs/decisions/0001-minimal-module-driven-ansible.md b/docs/decisions/0001-minimal-module-driven-ansible.md new file mode 100644 index 0000000..618e30b --- /dev/null +++ b/docs/decisions/0001-minimal-module-driven-ansible.md @@ -0,0 +1,52 @@ +# Decision 0001: Minimal, Module-Driven Ansible + +Status: accepted + +Date: 2026-09-07 + +## Context + +The first Seriatim implementation surrounded ordinary Ansible operations with +preflight inspection, fingerprints, sidecar build state, manual drift logic, +and recovery behavior. Much of that machinery duplicated behavior already +provided by Ansible modules or addressed threats and failure modes outside the +MVP. The result was harder to understand, review, and maintain than the +deployment it represented. + +The proof-of-concept roles in `rakestraw-ansible` demonstrate the preferred +style: direct module use, shallow task flow, small public interfaces, and +loops only for genuinely uniform resources. + +## Decision + +Roles in this workspace will express the required end state with the most +direct suitable Ansible module and trust that module's documented idempotence, +change detection, check-mode behavior, and error handling. + +Additional inspection or state machinery is justified only when a selected +requirement falls outside that module contract. In particular: + +- Do not use `stat`, package queries, content comparisons, or account lookups + merely to decide whether to invoke an idempotent module. +- Gather state when it is genuinely needed to control application behavior, + such as determining whether a locally built executable is missing or has + the wrong application version. +- Do not introduce fingerprints, checksums, sidecar state, staged replacement, + custom drift recovery, or abstractions without an explicit acceptance + criterion and rationale. +- Keep role variables to meaningful consumer choices. Do not expose every + fixed argument of an upstream build command as a public interface. +- Test the intended operating contract and idempotent convergence. Do not + build a speculative hardening test matrix into the MVP. + +## Consequences + +Feature roadmaps describe the simplest viable implementation shape, and +implementation plans must justify any material complexity beyond it. Reviews +should remove logic that merely reimplements a module's contract. + +Potential security hardening and unusual recovery behavior may be recorded for +future consideration without becoming part of the initial implementation. +This accepts that an MVP role may fail plainly when the managed host contains +malicious, corrupted, or unsupported state rather than attempting automatic +recovery from every condition. diff --git a/docs/orientation.md b/docs/orientation.md new file mode 100644 index 0000000..a8fb472 --- /dev/null +++ b/docs/orientation.md @@ -0,0 +1,44 @@ +# Workspace Orientation + +## Repository Map + +Repositories inside this directory: + +- `ansible-collection-dnd`: the `rakestraw.dnd` collection that bundles and + distributes the pipeline suite. +- `ansible-role-dnd-pipeline`: independent source repository for the + suite-level role exposed by the collection as `rakestraw.dnd.pipeline`. +- `ansible-role-{narratio,notarius,audita,seriatim,scriptorium}`: standalone + application roles. +- `rakestraw-ansible`: working deployment repository containing the + `pipeline_dnd` proof of concept; treat it as read-only reference unless the + task explicitly includes it. + +Relevant upstream repositories are siblings of this directory: + +- `../{narratio,notarius,audita,seriatim,scriptorium}`: application source and + authoritative application contracts. +- `../narratio-prompts`: pipeline prompt-library content. + +Use [the workspace inventory](../workspace.yml) as the complete +machine-readable inventory of paths and remotes. + +## Sources of Truth + +- [Workspace conventions](conventions.md): normative working and Ansible house + style. +- [Planning policy](planning.md): normative feature planning and progress + workflow. +- [MVP roadmap](roadmap/mvp.md): settled MVP scope, architecture, and open + design work. +- [Feature roadmaps](roadmap/features/): durable scope and target state for + selected units of work. +- [Workspace status](status.md): current cross-repository progress and next + work. +- [Decision records](decisions/): durable rationale for significant + architectural choices. +- [Implementation plan](roadmap/implementation.md): the fixed location for the + active staged plan or, when no plan is approved, a pending-plan notice. +- Application repositories: CLI, configuration, build, and integration + contracts. +- Individual Ansible repositories: implementation and repository-local tests. diff --git a/docs/planning.md b/docs/planning.md new file mode 100644 index 0000000..4025e45 --- /dev/null +++ b/docs/planning.md @@ -0,0 +1,44 @@ +# Planning and Progress + +- Read and follow [the workspace conventions](conventions.md) before preparing + or revising any roadmap or implementation plan. +- Update [the MVP roadmap](roadmap/mvp.md) when a design question becomes a + settled scope or architecture decision. +- Discuss and select the next unit of work before planning implementation. +- Give each selected feature a four-digit identifier and a durable document at + `docs/roadmap/features/-.md`. Start from the + [feature-roadmap template](roadmap/features/template.md) and add the same + identifier to [workspace status](status.md). +- Write and agree on the feature roadmap before writing its implementation + plan. The roadmap owns scope, target end state, rationale, constraints, and + acceptance criteria. +- Every feature roadmap must describe its simplest viable implementation shape + and identify any deliberate complexity. Keep optional security hardening, + unusual corruption recovery, and other speculative resilience outside the + MVP unless explicitly selected as requirements. +- Start from the [implementation-plan template](roadmap/implementation-template.md) + and always write the current plan to + [the fixed implementation-plan location](roadmap/implementation.md). Replace + that file when planning the next selected feature; Git history preserves + prior plans when needed. +- When a prior plan has been withdrawn and its replacement has not yet been + agreed, replace stale execution instructions with a brief pending-plan + notice. Never leave an obsolete plan looking executable. +- Write implementation plans as dependency-ordered stages suitable for + GPT-5.6-Terra to execute sequentially. Number stages with consecutive + integers beginning with `Stage 1`; do not skip numbers or use nested stage + numbering. Each stage must identify its repository, preconditions, required + behavior, validation, completion criteria, and expected commit. +- Resolve architectural choices during roadmap and plan review. Do not leave + unresolved design judgment to the implementation agent. +- Update [workspace status](status.md) when cross-repository work starts, + becomes blocked, or is completed. Completed items must point to concrete + evidence. +- Out-of-band implementation should commit each completed stage when that + stage leaves its owning repository valid. Review the implementation and its + validation evidence before marking the feature complete. +- Retain feature roadmaps as durable records. Remove or archive obsolete + implementation detail after completion; durable architectural rationale + belongs in [decision records](decisions/). +- Use the status values `proposed`, `ready`, `in_progress`, `blocked`, `done`, + and `deferred`. diff --git a/docs/roadmap/features/0001-seriatim-source-installation.md b/docs/roadmap/features/0001-seriatim-source-installation.md index 0d63407..29af37c 100644 --- a/docs/roadmap/features/0001-seriatim-source-installation.md +++ b/docs/roadmap/features/0001-seriatim-source-installation.md @@ -1,47 +1,49 @@ # Feature 0001: Seriatim Source Installation -Status: done +Status: ready ## Summary -Implement the first independently usable application role by teaching -`ansible-role-seriatim` to install Seriatim from a pinned upstream source -release. This feature establishes the source-build and role conventions that -can be reviewed before analogous work begins in the other application roles. +Provide a small, independently usable `rakestraw.seriatim` role that installs +Seriatim from a pinned Git tag, builds the native executable on the managed +host, and converges idempotently. This revised specification replaces the +original overengineered implementation target and establishes the preferred +module-driven style for subsequent application roles. ## Problem or Opportunity -The pipeline applications do not yet share a uniform prebuilt release model, -and the new Ansible role repositories contain only initial scaffolding. The MVP -therefore builds applications on the managed host. Seriatim is the smallest -useful proving ground because it has no central application configuration file, -allowing this feature to isolate installation, reproducibility, idempotence, -and validation concerns. +The current role reaches the basic deployment outcome, but it does so with +manual source cleanup, compiler parsing, build-input serialization, hashes, +checksums, sidecar state, path-shape recovery, and a large defensive test +matrix. Those mechanisms are disproportionate to the MVP and obscure the +simple job of checking out a repository, building one executable, and +installing it with known metadata. + +Seriatim remains the right proving ground because it has no central +configuration file. Simplifying it before implementing another role prevents +the current design from becoming an accidental suite-wide pattern. ## Scope -- Establish a conventional, independently usable Ansible role structure in - `ansible-role-seriatim`. +- Simplify `ansible-role-seriatim` into concise, idiomatic Ansible. - Support Ubuntu 26.04 on native `amd64` and `arm64` hosts. -- Install Git and the official Go Snap using classic confinement when build +- Install Git and the official Go Snap with classic confinement when build dependency management is enabled. -- Default the Go Snap channel to `1.26/stable` and allow consumers to override - the channel or disable build-dependency management. -- Clone the official Seriatim repository into a root-owned managed source - directory, defaulting to `/usr/local/src/seriatim`. -- Default to the immutable `v1.5.0` release tag, verified as the latest current - upstream tag on 2026-09-07, while supporting explicit branch or commit - overrides for development. -- Build a native, statically linked Seriatim binary using the upstream build - contract, including release-version injection. -- Validate a staged binary before installing it as - `/usr/local/bin/seriatim` with `root:root` ownership and mode `0755`. -- Record sufficient build state to rebuild when the resolved source commit, - Go compiler version, or effective build arguments change. -- Validate the installed binary and demonstrate convergence on a repeated role - run. -- Document the role interface, defaults, supported platform, development - overrides, and validation procedure. +- Default the Go Snap channel to `1.26/stable`. +- Check out the selected Seriatim version from its official Git repository, + defaulting to immutable tag `v1.5.0`, under a root-owned + `/usr/local/src/seriatim`. +- Build Seriatim using the fixed upstream release build contract and install + it at `/usr/local/bin/seriatim`. +- Rebuild when the source checkout changes, the binary is absent, or the + installed binary does not report the selected version. +- Converge the installed binary to `root:root` ownership and mode `0755`. +- Validate a newly built binary with `seriatim --version`. +- Keep the role interface, README, metadata, and focused local tests consistent + with the simplified behavior. +- Remove obsolete build-state files and implementation machinery from the + role repository. Existing build-state data on previously managed hosts does + not need to be removed by the role. ## Non-goals @@ -49,123 +51,169 @@ and validation concerns. - Creation or management of the `narratio` account or operator membership. - Installation or configuration of any other pipeline application. - Pipeline composition, campaign data, prompts, secrets, or scheduling. -- Installation from prebuilt Seriatim release artifacts. -- Removal or purge behavior. -- A shared build helper role or premature abstraction of the first - implementation. -- Full Ubuntu 26.04 platform-matrix or `arm64` integration infrastructure. +- Installation from prebuilt release artifacts or removal behavior. +- Detecting rebuild needs from Go compiler patch changes, fixed build-command + changes, or manually altered binary contents that still report the selected + version. +- Build fingerprints, binary checksums, or persistent sidecar build state. +- Transactional replacement that preserves a previous executable after a + failed local build. +- Automatic recovery from deliberately malformed paths, symbolic-link + redirection, hostile source trees, or other adversarial host state. +- A shared build helper role or abstraction. +- Dedicated platform-matrix infrastructure beyond the approved local MVP + proxy. ## Target End State On an Ubuntu 26.04 `amd64` or `arm64` host, applying the role with default -variables checks out Seriatim `v1.5.0` under `/usr/local/src/seriatim`, builds -it with the locally installed Go 1.26 toolchain, validates it, and installs a -root-owned executable at `/usr/local/bin/seriatim`. The installed command -reports `v1.5.0` through its global version flag. +variables installs Git and Go when requested, checks out Seriatim `v1.5.0` +under a root-owned `/usr/local/src/seriatim`, and installs a native executable at +`/usr/local/bin/seriatim`. The executable is owned by `root:root`, has mode +`0755`, and reports `seriatim version v1.5.0`. -A second application of the role makes no changes when the selected source, -compiler, build arguments, and installed binary are unchanged. Changing the -selected revision, compiler patch version, or effective build arguments causes -a rebuild. A failed build or validation does not replace an existing working -binary. +An immediate second application makes no changes. A changed Git checkout, a +missing binary, or a binary reporting another version causes one build. The +following application is again unchanged. -The managed source checkout is authoritative and may be reset to the selected -revision; local modifications beneath it are not preserved. Development users -may select a branch or commit explicitly, but released role and collection -manifests use immutable release tags. +The role trusts `apt`, `community.general.snap`, `file`, and `git` to provide +their documented idempotence and change detection. It does not inspect state +before invoking those modules or maintain a parallel model of the state they +already manage. ## Repository Ownership -- `ansible-role-seriatim` owns all implementation, defaults, documentation, - and repository-local validation added by this feature. -- `../seriatim` is the read-only authority for the application build, version, - and CLI contracts. +- `ansible-role-seriatim` owns the role implementation, defaults, metadata, + README, dependency declaration, and repository-local validation. +- `../seriatim` is the read-only authority for the build, version, and CLI + contracts. +- `rakestraw-ansible` is a read-only style reference. - `ansible-workspace-dnd` owns this roadmap, the active implementation plan, - and cross-repository status only. -- The remaining role, collection, application, content, and proof-of-concept - repositories remain unchanged. + and cross-repository status. +- All other repositories remain unchanged. ## Constraints and Settled Decisions - The suite MVP supports Ubuntu 26.04 and native `amd64` and `arm64` builds. - Local execution on the current Ubuntu 26.04 development host is a sufficient - MVP integration proxy; dedicated platform-matrix infrastructure is deferred. -- Go is installed from Snap with classic confinement. Builds use the local - toolchain with `GOTOOLCHAIN=local` rather than allowing automatic toolchain - downloads. + MVP integration proxy. +- Go is installed from Snap with classic confinement and defaults to channel + `1.26/stable`. - `community.general` is an approved dependency and provides Snap management; its version is pinned in the role's collection requirements. -- The suite standardizes on Go channel `1.26/stable`, even though Seriatim's - current `go.mod` requires Go 1.25. -- Builds use `GOWORK=off`, `CGO_ENABLED=0`, and `-trimpath`. -- Release builds inject the selected release version into - `gitea.maximumdirect.net/eric/seriatim/internal/buildinfo.Version` with - `-ldflags`. -- A change to the installed Go compiler patch version is a build-input change - and triggers a rebuild. -- `/usr/local/src/seriatim` is fully Ansible-managed. The role enforces the - selected Git revision rather than preserving local changes. -- Source and installed binaries are owned by `root` and are not writable by - runtime operators. +- The build runs from the checked-out source using `/snap/bin/go`, + `GOWORK=off`, `GOTOOLCHAIN=local`, `CGO_ENABLED=0`, `-trimpath`, package + `./cmd/seriatim`, and the upstream linker flag that injects the selected + version into `internal/buildinfo.Version`. +- The selected version is both the Git revision and the version injected into + the executable. Supporting independent source and build identities is not an + MVP requirement. +- The public interface is limited to meaningful deployment choices: + `seriatim_version`, `seriatim_repository_url`, `seriatim_source_dir`, + `seriatim_binary_path`, `seriatim_manage_build_dependencies`, and + `seriatim_go_snap_channel`. +- Fixed executable paths, build flags, linker symbols, environment values, and + package paths are implementation details, not public variables. +- `docs/decisions/0001-minimal-module-driven-ansible.md` governs the role's + implementation style. + +## Simplest Viable Implementation + +The intended Seriatim implementation is approximately the following ten +ordinary tasks. + +1. Assert the supported distribution, release, and architecture. +2. Install Git with `ansible.builtin.apt` when dependency management is + enabled. +3. Install Go with `community.general.snap` when dependency management is + enabled. +4. Create the source and binary parent directories with one + `ansible.builtin.file` loop. +5. Check out the selected source with `ansible.builtin.git` and register its + normal changed result. +6. Inspect whether the installed binary exists. +7. Read its version when it exists, without reporting a change. +8. Run the upstream Go build when the Git checkout changed, the binary is + absent, or its reported version differs from the selected version. +9. Converge source ownership and the installed binary's ownership and mode + with one `ansible.builtin.file` loop. +10. Validate the version after a build, without reporting a change. + +For this fixed set of three rebuild triggers, the build remains an ordinary +task with the conditions applied directly to its `when` expression. This keeps +the complete decision visible at the action site and avoids deferred execution +or a play-wide handler flush. Permission convergence and post-build validation +then follow in their natural task order. Do not create an intermediate +`seriatim_build_required` fact. + +The implementation may combine or split a task when required for clear and +correct Ansible, but it must not add preflight checks around idempotent modules +or reintroduce persistent build-state machinery. Prefer a single readable task +file unless a split materially improves navigation. ## Rationale and Alternatives -Starting with Seriatim exercises the common source installation path without -mixing in application configuration, credentials, shared accounts, or pipeline -composition. This makes the first implementation small enough to review -carefully and allows its conventions to be corrected before being repeated. +This design uses the Git module's changed result as the natural signal that +source changed. The installed application's version is the only additional +state needed to decide whether a binary must be built. That preserves useful +convergence without maintaining a second database of compiler and build +inputs. -Using Go `1.26/stable` across the suite favors one predictable host toolchain -over selecting each application's minimum supported version. Rebuilding after -a compiler patch change preserves an honest relationship between recorded -build inputs and installed output. Keeping the first role self-contained also -avoids committing to a shared abstraction before repeated implementation has -shown which behavior is truly common. +Building directly to the configured binary path is intentionally simpler than +staging, checksumming, and recording a transactional installation. A failed +build will fail the play visibly; preserving an older executable in every +failure mode is not an MVP requirement. ## Acceptance Criteria - The role rejects unsupported distributions, Ubuntu releases, and - architectures with a clear diagnostic before modifying application state. -- Default role execution on the local Ubuntu 26.04 host installs or confirms - the required Git and Go build dependencies. -- The source checkout resolves to the commit referenced by tag `v1.5.0` and is - root-owned at `/usr/local/src/seriatim` by default. -- The role builds with the documented local-toolchain and version-injection - contract and installs `/usr/local/bin/seriatim` as `root:root` mode `0755`. -- `/usr/local/bin/seriatim --version` reports `v1.5.0`. + architectures before modifying application state. +- Default execution on the local Ubuntu 26.04 host installs or confirms Git + and Go and checks out Seriatim tag `v1.5.0`. +- The role builds with the documented fixed upstream contract and installs + `/usr/local/bin/seriatim` as `root:root` mode `0755`. +- The managed source checkout is owned by `root:root`. +- `/usr/local/bin/seriatim --version` reports + `seriatim version v1.5.0`. - An immediate second role run reports no changes. -- A changed selected revision, Go compiler patch version, or effective build - argument invalidates recorded build state and rebuilds the binary. -- A deliberately failed staged build or version validation leaves a previously - installed working binary intact. -- Development revision overrides converge to the requested branch or commit - and discard unmanaged changes in the source checkout. -- Role defaults, metadata, README documentation, syntax validation, focused - automated checks, and local integration evidence are present in the owning - repository. +- A missing binary or a binary reporting the wrong version causes one rebuild + followed by an unchanged run. +- A changed selected version causes the Git task and build task to change and + then converges. +- The implementation contains no build-input documents, fingerprints, binary + checksums, sidecar build state, manual Git reset or clean commands, or + preliminary checks for state already managed by `apt`, Snap, `file`, or + `git`. +- Public defaults and argument specifications contain only the six variables + named in this roadmap. +- Syntax checking, ansible-lint, a focused local convergence test, and a clean + repeated run pass. +- Role documentation describes the actual concise behavior and does not claim + transactional build safety or exhaustive corruption recovery. - No repository other than `ansible-role-seriatim` and the workspace control repository is modified. ## Dependencies and Follow-up Work -This feature depends only on access to the Seriatim Git repository, Ubuntu Snap -infrastructure, and Go module sources from the managed host. +The role requires access to the Seriatim Git repository, Ubuntu Snap +infrastructure, and Go module sources from the managed host. After this revised +implementation is reviewed, its minimal conventions can inform a fresh Audita +roadmap and the remaining application roles. -After review, the source-build conventions may be adopted by the Audita, -Scriptorium, Notarius, and Narratio roles. Generalization into shared tooling, -prebuilt-binary installation, removal behavior, and dedicated multi-platform -test infrastructure remain deferred until concrete experience justifies them. +The original implementation commits remain in Git history as evidence of the +superseded design. The prior completion and remediation documents must not be +used as templates for another role. -## Completion Evidence +## Potential Future Hardening -- `ansible-role-seriatim` commits `ac35c1f` through `efd4734` implement the - staged role contract, source installation, reproducible build, and integration - coverage. -- Remediation commits `c6c1907` through `6fb1aa2` close all findings recorded in - `docs/roadmap/remediation.md`, including malformed state and metadata drift. -- The complete integration suite passes on the local Ubuntu 26.04 development - host, restores the default installation, and finishes with a clean idempotent - convergence. -- The verified installation reports `seriatim version v1.5.0`, uses upstream - commit `0fc92f364316c874d366b00cdc884a33e1900b25`, and has consistent source, - binary, ownership, and build-state metadata. +- Build in a temporary location and validate before replacing the installed + executable. +- Verify release signatures or source checksums. +- Record compiler and build-input identity for exact rebuild invalidation. +- Detect arbitrary binary modification independently of reported version. +- Protect or recover from unexpected symbolic links and malformed managed + paths. +- Add dedicated Ubuntu release and architecture test matrices. + +These are candidates for separately selected features if operational +experience or a changed threat model justifies them. diff --git a/docs/roadmap/features/0002-audita-source-installation-and-configuration.md b/docs/roadmap/features/0002-audita-source-installation-and-configuration.md new file mode 100644 index 0000000..b2be0b0 --- /dev/null +++ b/docs/roadmap/features/0002-audita-source-installation-and-configuration.md @@ -0,0 +1,35 @@ +# Feature 0002: Audita Source Installation and Configuration + +Status: proposed + +## Planning Note + +Audita remains the likely next application role, but its previously drafted +roadmap and implementation plan were withdrawn before implementation. They +depended on the original Seriatim fingerprint, sidecar-state, staged-build, +and corruption-recovery architecture, which is no longer an approved pattern. + +Do not implement this feature from the withdrawn plan. After the simplified +Seriatim role has been implemented and reviewed, discuss Audita again and +write a fresh feature roadmap using the workspace's minimal, module-driven +conventions. + +## Preserved Upstream Research + +- The latest selected immutable release was `v0.9.1`, resolving to commit + `a84941d68168a1ed9ea7423b6477ae8218be73f9` when inspected. +- The tag declares Go 1.24.0 and builds the package + `gitea.maximumdirect.net/eric/audita/cmd/audita` with `CGO_ENABLED=0`, + `GOWORK=off`, `GOTOOLCHAIN=local`, `-trimpath`, and the documented release + linker flags. +- The release does not expose a CLI version command suitable for Seriatim's + version-based rebuild check. The simplest appropriate rebuild signal must be + decided during renewed roadmap discussion. +- Audita configuration schema version is `1`. Its native validator accepts + `audita config validate --config ` and reports `config is valid` for a + valid file. +- Default configuration discovery prefers + `/usr/local/etc/audita/config.yml` over `/etc/audita/config.yml`. + +These facts should be revalidated against the authoritative `../audita` +repository before a new roadmap is approved. diff --git a/docs/roadmap/features/template.md b/docs/roadmap/features/template.md index 12ecc40..cee4cc8 100644 --- a/docs/roadmap/features/template.md +++ b/docs/roadmap/features/template.md @@ -34,6 +34,14 @@ application contracts. Link the MVP roadmap and applicable decision records. Record technical or operational constraints that implementations must preserve. +## Simplest Viable Implementation + +Outline the direct, module-driven task flow at the level needed to constrain +the implementation without turning the roadmap into an execution plan. Trust +module idempotence and change detection. Identify any non-obvious probe, +sidecar state, staging mechanism, command, or abstraction and tie it to a +specific acceptance criterion. + ## Rationale and Alternatives Explain material tradeoffs and why the selected end state is preferred. Omit @@ -47,3 +55,8 @@ this section when the choice is mechanical and needs no durable rationale. List prerequisites and explicitly deferred work without expanding this feature's scope. + +## Potential Future Hardening + +List worthwhile security or resilience improvements that are deliberately not +part of the initial implementation. Omit this section when there are none. diff --git a/docs/roadmap/implementation-template.md b/docs/roadmap/implementation-template.md index b26b203..c706964 100644 --- a/docs/roadmap/implementation-template.md +++ b/docs/roadmap/implementation-template.md @@ -23,6 +23,14 @@ for scope, rationale, target end state, and acceptance criteria. - Preserve unrelated and pre-existing changes. - Do not make unresolved product or architecture decisions during execution; stop and report the decision required. +- Start from the feature roadmap's simplest viable implementation. Trust + module-provided idempotence, change detection, check-mode behavior, and + error handling rather than reproducing them with preliminary probes or + supplemental state. +- Do not add a task merely for defensive completeness. Every probe, fact, + checksum, custom `changed_when`, shell or command invocation, sidecar state + file, staging layer, or abstraction must be required by an + acceptance criterion and must solve behavior that a direct module does not. - Run every stage's validation before committing it. - Commit each completed stage when it leaves the repository in a valid state, using the commit intent specified by that stage. @@ -57,6 +65,8 @@ State the single outcome delivered by this stage. 1. Describe concrete behavior to implement, including relevant files or components where known. 2. State invariants, compatibility requirements, and explicit exclusions. +3. Give the expected module-driven task flow. Call out every required use of + `command`, `shell`, state inspection, or manual change detection. ### Validation @@ -91,6 +101,8 @@ and subsequent consecutive integers as needed. - Confirm every changed repository is clean and identify all resulting commit hashes. - Summarize deviations from this plan, unresolved findings, and deferred work. +- Report any implementation complexity added beyond the planned task flow and + the acceptance criterion that required it. - Do not mark the feature `done`; implementation results require review first. ## Progress Notes diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index 3f7d6c7..e3a3225 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -1,507 +1,12 @@ -# Implementation Plan 0001: Seriatim Source Installation +# Active Implementation Plan -Status: proposed +There is no approved implementation plan at present. -Target executor: GPT-5.6-Terra +Feature 0001 has been reopened with a revised, minimal specification at +`docs/roadmap/features/0001-seriatim-source-installation.md`. Prepare its +numbered staged implementation plan here only after that revised roadmap and +the workspace implementation conventions have been reviewed. -Feature roadmap: -`docs/roadmap/features/0001-seriatim-source-installation.md` - -## Objective - -Implement `ansible-role-seriatim` as an independently usable role that installs -Seriatim `v1.5.0` from its pinned Git source, builds it with the local Go 1.26 -Snap toolchain, installs it safely, and converges idempotently. Preserve the -feature roadmap as the authority for scope, rationale, target state, and -acceptance criteria. - -## Execution Rules - -- Execute Stage 1 through Stage 4 in numeric order. -- Before each stage, confirm its preconditions and the working-tree state of - every repository named by that stage. -- Preserve unrelated and pre-existing changes. Stop if the owning repository - is not clean at a stage boundary unless the changes are the expected output - of the immediately preceding stage. -- Do not modify `rakestraw-ansible`, any upstream application repository, any - other role, or the collection. -- Do not make new product or architecture decisions. Stop and report any - conflict between this plan, the feature roadmap, and an upstream contract. -- Prefer `ansible.builtin` modules where they directly fit. Use the approved - `community.general.snap` module for channel-aware Snap management and pin - its collection dependency. -- Run every stage's validation before committing it. Do not begin a dependent - stage after a validation failure. -- Commit each completed stage in `ansible-role-seriatim` using the specified - commit intent. Do not push unless separately instructed. -- Treat `/usr/local/src/seriatim`, `/usr/local/bin/seriatim`, and the adjacent - role build-state directory as disposable managed test state during local - integration. Do not remove or alter unrelated host paths. -- Leave workspace roadmap and status files unchanged during implementation; - report stage commits and evidence for review instead. - -## Starting State and Preconditions - -- `ansible-workspace-dnd` and `ansible-role-seriatim` start clean on `main`. -- The active implementation plan has been reviewed and committed before - execution begins. -- The controller host is Ubuntu 26.04.1 `amd64` and has passwordless privilege - escalation available for the approved local integration run. -- Workspace tools are available at `../.venv/bin` from the role repository, - including Ansible Core 2.21.3 and ansible-lint 26.8.0. -- The managed host has working Snap infrastructure and outbound access to the - Seriatim Git repository, the Snap Store, and Go module sources. -- Upstream tag `v1.5.0` resolves to commit - `0fc92f364316c874d366b00cdc884a33e1900b25`. -- The upstream `v1.5.0` contract is: - - module requirement `go 1.25`; - - build package `./cmd/seriatim`; - - `CGO_ENABLED=0` and `-trimpath`; - - linker flags `-s -w -X - gitea.maximumdirect.net/eric/seriatim/internal/buildinfo.Version=`; - - `seriatim --version` output `seriatim version `. - -## Repository Scope - -- `ansible-role-seriatim` is the only implementation repository and owns every - role, test, dependency, and README change in this plan. -- `ansible-workspace-dnd` owns this plan and the feature roadmap but remains - read-only during out-of-band execution. -- `../seriatim` may be inspected as the authoritative upstream contract and - must remain unchanged. -- All other repositories in `workspace.yml` remain unchanged. - -## Fixed Role Interface - -Define and document these public defaults. All paths are managed-host paths. - -| Variable | Default | Purpose | -| --- | --- | --- | -| `seriatim_version` | `v1.5.0` | Release identity expected from the installed binary | -| `seriatim_repository_url` | `https://gitea.maximumdirect.net/eric/seriatim.git` | Authoritative source repository | -| `seriatim_source_ref` | `{{ seriatim_version }}` | Git tag, branch, or commit to check out | -| `seriatim_build_version` | `{{ seriatim_version }}` | Value injected into Seriatim build metadata | -| `seriatim_source_dir` | `/usr/local/src/seriatim` | Fully managed source checkout | -| `seriatim_binary_path` | `/usr/local/bin/seriatim` | Installed executable | -| `seriatim_build_state_dir` | `/usr/local/src/seriatim.ansible-build-state` | Sidecar build inputs, fingerprint, and checksum | -| `seriatim_manage_build_dependencies` | `true` | Whether the role installs Git and Go | -| `seriatim_go_snap_channel` | `1.26/stable` | Go Snap channel | -| `seriatim_go_binary` | `/snap/bin/go` | Go executable used for version discovery and builds | -| `seriatim_go_build_flags` | `[-trimpath]` | Ordered arguments passed before `-ldflags` | -| `seriatim_go_ldflags` | upstream release linker flags using `seriatim_build_version` | Linker argument passed as one value | -| `seriatim_go_package` | `./cmd/seriatim` | Package built from the checkout | - -`seriatim_source_ref` and `seriatim_build_version` are deliberately distinct. -A development branch or commit override must set an honest build-version value -as well as the source ref. Fixed build environment values are not public -overrides: `GOWORK=off`, `GOTOOLCHAIN=local`, and `CGO_ENABLED=0`. - -## Stage 1: Establish the Role Contract and Static Validation - -Completed: `ac35c1f` (`feat: define Seriatim role contract`) - -### Objective - -Create a conventional role skeleton with a fully specified variable contract, -argument validation, platform guards, dependency manifest, and syntax-test -entry point, without installing application state yet. - -### Preconditions - -- `ansible-role-seriatim` is clean on `main` and tracks `origin/main`. -- The workspace virtual environment passes - `../.venv/bin/ansible-playbook --version`. -- The feature roadmap and Fixed Role Interface above have no unresolved - conflicts. - -### Owning Repository - -`ansible-role-seriatim` - -### Required Changes - -1. Add the standard role paths `defaults/`, `meta/`, `tasks/`, `tests/`, and - `collections/`. Use YAML document markers and fully qualified module names. -2. Define every variable in the Fixed Role Interface in `defaults/main.yml`. - Do not expose aliases, compatibility variables, removal controls, or - configuration-file settings. -3. Add `meta/main.yml` with Eric Rakestraw as author, BSD-3-Clause licensing, - minimum Ansible Core 2.18, Ubuntu `resolute` platform metadata, source and - issue URLs, and tags appropriate to Seriatim and transcript processing. -4. Add `meta/argument_specs.yml`. Validate booleans, strings, lists, and paths - by type; require nonempty repository/ref/version/build values; and describe - every public variable. -5. Add `tasks/main.yml` and `tasks/validate.yml`. Run validation before every - task that can change the host. Assert: - - distribution is Ubuntu; - - distribution version is `26.04`; - - architecture is `x86_64` or `aarch64`; - - source, binary, and state paths are absolute and pairwise distinct; - - repository, source ref, build version, Go binary, build flags, linker - flags, and package are nonempty; - - build version contains no whitespace. -6. Pin `community.general` version `13.3.0` in - `collections/requirements.yml`. This collection requires Ansible Core 2.18 - or newer and supplies the channel-aware `community.general.snap` module. -7. Extend `.gitignore` only for repository-local validation state, including - `.ansible/`, test output, and retry files. Preserve the existing general - ignore entries. -8. Add `tests/inventory` targeting localhost with local connection and an - explicit `/usr/bin/python3.14` interpreter. Add `tests/converge.yml` that - gathers facts, enables privilege escalation, and applies the repository - role through `ANSIBLE_ROLES_PATH`. -9. Replace the placeholder README with the role purpose, supported platform, - dependency-installation command, complete variable table, development-ref - rule, and an explicit statement that this stage does not yet install a - binary. - -### Validation - -From `ansible-role-seriatim`, run: - -```sh -../.venv/bin/ansible-galaxy collection install \ - -p .ansible/collections -r collections/requirements.yml -ANSIBLE_COLLECTIONS_PATH="$PWD/.ansible/collections" \ - ../.venv/bin/ansible-playbook -i tests/inventory tests/converge.yml \ - --syntax-check -ANSIBLE_COLLECTIONS_PATH="$PWD/.ansible/collections" \ - ../.venv/bin/ansible-lint . -git diff --check -``` - -Expected result: `community.general` 13.3.0 is installed under the ignored -repository-local path, the test playbook passes syntax checking, ansible-lint -reports no violations, and Git reports no whitespace errors. Do not run the -converge playbook without `--syntax-check` during this stage. - -### Completion Criteria - -- The public defaults exactly match the Fixed Role Interface. -- Invalid platform and malformed-variable paths fail before host mutation. -- Static validation passes with the pinned collection available locally. -- Only `ansible-role-seriatim` contains implementation changes. - -### Commit - -Commit intent: `feat: define Seriatim role contract` - -## Stage 2: Manage Build Dependencies and the Source Checkout - -Completed: `b84b045` (`feat: manage Seriatim source prerequisites`) - -### Objective - -Implement idempotent Git and Go prerequisite management and converge the -root-owned Seriatim checkout to the selected source revision, without building -or installing the application binary yet. - -### Preconditions - -- Stage 1 is committed and its validation still passes. -- `snap info go` shows the configured `1.26/stable` channel. -- No unmanaged data that must be retained exists under the configured source - directory; that tree is deliberately authoritative and disposable. - -### Owning Repository - -`ansible-role-seriatim` - -### Required Changes - -1. Add `tasks/dependencies.yml` and include it after validation. -2. When `seriatim_manage_build_dependencies` is true: - - install the Ubuntu `git` package with `ansible.builtin.apt`, using a - reasonable cache-validity interval rather than refreshing on every run; - - install the single `go` Snap through `community.general.snap` with - `classic: true`, `channel: "{{ seriatim_go_snap_channel }}"`, and - `state: present`. -3. Whether dependency management is enabled or disabled, resolve Git and the - configured Go executable and fail clearly if either is unavailable. Run - `go version`, extract its semantic version, and require Go 1.25 or newer. -4. Add `tasks/source.yml` and include it after dependencies. Create the parent - source directory as `root:root` before cloning. -5. Use `ansible.builtin.git` with the configured repository and source ref, - `update: true`, `force: true`, and no shallow-clone assumption. Do not accept - host keys automatically and do not embed credentials. -6. Make the checkout fully managed: detect tracked or untracked dirt after the - Git task, reset tracked files to the selected checkout, and remove untracked - and ignored content with explicit Git commands only when dirt exists. Keep - the build-state sidecar outside the checkout so cleanup cannot remove it. -7. Enforce root ownership on the checkout and resolve the final commit with - `git rev-parse HEAD`. Assert that the default `v1.5.0` execution resolves to - `0fc92f364316c874d366b00cdc884a33e1900b25` in the local integration test; - nondefault refs record their actual resolved commit without that assertion. -8. Update the README to describe dependency opt-out behavior, channel - convergence, authoritative checkout cleanup, and requirements for private - repository authentication overrides without documenting any credentials. - -### Validation - -Run the Stage 1 static commands, then run: - -```sh -ANSIBLE_COLLECTIONS_PATH="$PWD/.ansible/collections" \ - ANSIBLE_ROLES_PATH="$(dirname "$PWD")" \ - ../.venv/bin/ansible-playbook -i tests/inventory tests/converge.yml -/snap/bin/go version -sudo git -C /usr/local/src/seriatim rev-parse HEAD -sudo test "$(stat -c '%U:%G' /usr/local/src/seriatim)" = root:root -``` - -Expected result: the play completes successfully without creating -`/usr/local/bin/seriatim`; Go is on the `1.26/stable` track and satisfies the -minimum version; the checkout resolves to the expected `v1.5.0` commit; and its -root directory is owned by `root:root`. - -Create an untracked file and a tracked-file modification beneath the checkout, -rerun convergence, and verify both are removed. Run convergence once more and -require `changed=0` in the play recap. - -### Completion Criteria - -- Dependency management and opt-out behavior fail or converge as specified. -- Go channel and minimum-version validation are explicit and idempotent. -- Default source state is the expected clean, root-owned commit. -- Unmanaged checkout changes are discarded by a subsequent role run. -- A clean repeated run reports no changes. - -### Commit - -Commit intent: `feat: manage Seriatim source prerequisites` - -## Stage 3: Build and Install Seriatim Reproducibly - -Completed: `81313f0` (`feat: build and install Seriatim from source`) - -### Objective - -Add fingerprint-driven source compilation, staged validation, atomic binary -installation, and durable build-state recording without replacing a working -binary after a failed build. - -### Preconditions - -- Stage 2 is committed and its clean repeated convergence reports no changes. -- The default checkout is the expected `v1.5.0` commit. -- `/snap/bin/go version` reports a toolchain compatible with the upstream - module requirement. - -### Owning Repository - -`ansible-role-seriatim` - -### Required Changes - -1. Add `tasks/build.yml` after source convergence. -2. Construct one canonical build-input document containing at least: - - resolved source commit; - - complete `go version` output; - - configured Go binary path; - - build version; - - ordered build flags; - - linker flags; - - package path; - - fixed `GOWORK`, `GOTOOLCHAIN`, and `CGO_ENABLED` values. - Store the readable input document and its SHA-256 fingerprint in - `seriatim_build_state_dir` only after successful installation. -3. Record the installed binary's SHA-256 checksum beside the fingerprint. - Require a rebuild when the binary, any state file, or the checkout is - missing; when the desired fingerprint differs; when the installed checksum - differs; or when the installed version command fails or differs from - `seriatim version {{ seriatim_build_version }}`. Malformed or unreadable - state must cause a rebuild rather than an unhandled template failure. -4. When no rebuild is required, perform no mutating build or install task and - report no change. -5. When rebuilding, create a unique temporary directory with - `ansible.builtin.tempfile`. Invoke the configured Go binary with - `ansible.builtin.command` `argv`, the fixed environment, configured build - flags, one `-ldflags` value, a staged output path, and the configured package - from `seriatim_source_dir`. -6. Validate the staged binary before installation. Require exact stdout - `seriatim version {{ seriatim_build_version }}` and a successful exit from - `--version`. -7. Install the validated staged binary to `seriatim_binary_path` using an - atomic Ansible file operation with `root:root` ownership and mode `0755`. - Validate the installed binary again, calculate its checksum, and only then - write the build-input, fingerprint, and checksum state files. -8. Use an `always` cleanup path so temporary build content is removed after - success or failure. Never delete or overwrite the previously installed - binary or successful build state before a replacement has passed staged - validation. -9. Give command tasks precise `changed_when` and `failed_when` behavior so - ansible-lint passes and check/reporting semantics are honest. -10. Update the README with the exact build contract, rebuild triggers, state - location, ownership, safe replacement behavior, and development override - example setting both `seriatim_source_ref` and - `seriatim_build_version`. - -### Validation - -Run static validation, then converge and inspect: - -```sh -ANSIBLE_COLLECTIONS_PATH="$PWD/.ansible/collections" \ - ANSIBLE_ROLES_PATH="$(dirname "$PWD")" \ - ../.venv/bin/ansible-playbook -i tests/inventory tests/converge.yml -/usr/local/bin/seriatim --version -sudo stat -c '%U:%G %a' /usr/local/bin/seriatim -sudo find /usr/local/src/seriatim.ansible-build-state \ - -maxdepth 1 -type f -print -exec sed -n '1,80p' {} \; -``` - -Expected result: the command prints exactly `seriatim version v1.5.0`; the -binary is `root:root` mode `755`; readable build inputs, fingerprint, and -checksum exist in the sidecar state directory; and no temporary staging -directory remains. - -Run the same convergence immediately again and require `changed=0`. Then change -one effective build flag through an extra variable, run convergence, and -require the build/install tasks to report changes. Repeat with that override -and require `changed=0`; finally restore defaults and require exactly one more -rebuild followed by a clean repeat. - -### Completion Criteria - -- The installed binary matches the upstream version and ownership contract. -- Build state records every required input and the installed checksum. -- Missing, corrupt, or changed binary/state/input conditions rebuild. -- Unchanged convergence reports no changes. -- Temporary content is cleaned on both successful and failed task paths. -- Static validation remains clean. - -### Commit - -Commit intent: `feat: build and install Seriatim from source` - -## Stage 4: Add Local Integration Regression Coverage - -Completed: `efd4734` (`test: verify Seriatim source installation`) - -### Objective - -Turn the required local validation into a repeatable, failure-safe repository -test harness and complete the role's operator and maintainer documentation. - -### Preconditions - -- Stage 3 is committed, statically clean, and manually converges twice with the - second run reporting no changes. -- The default installed binary and build state are known-good before testing - deliberate failure paths. -- No unrelated local host data exists at the managed Seriatim paths. - -### Owning Repository - -`ansible-role-seriatim` - -### Required Changes - -1. Add `tests/verify.yml` with nonmutating assertions for: - - supported platform facts; - - source commit and cleanliness; - - source, binary, and state ownership; - - binary mode; - - exact installed version output; - - presence and consistency of build inputs, fingerprint, and installed - checksum. -2. Add an executable `tests/integration` shell driver that locates the - workspace virtual environment relative to the repository, installs the - pinned collection into `.ansible/collections`, exports local collection and - role paths, and runs syntax checking plus ansible-lint before host mutation. -3. Have the driver run default convergence, verification, and an immediate - second convergence. Parse the second recap and fail unless `changed=0` and - `failed=0`. -4. Test the mutation guard with separate plays that override gathered facts to - represent an unsupported distribution, Ubuntu release, and architecture. - Each play must fail in validation before a mutating task; capture the - installed binary checksum and successful state beforehand and require both - to remain unchanged afterward. -5. Test source authority by creating both a tracked modification and an - untracked file in the managed checkout, converging, and verifying a clean - checkout at the expected commit. -6. Test compiler-input invalidation with a temporary Go wrapper. Its `version` - path must report a distinct valid Go 1.26 patch identity while all other - commands delegate to `/snap/bin/go`. Converge with - `seriatim_go_binary` set to the wrapper, require a rebuild, repeat and - require idempotence, then restore the default Go path and require one final - rebuild followed by idempotence. -7. Test failure preservation with a second temporary Go wrapper that reports a - valid version but fails the build invocation. Capture the installed binary - checksum and successful state files before the expected failed play. After - failure, require the binary checksum and state contents to be byte-for-byte - unchanged and require `seriatim --version` still to report `v1.5.0`. -8. Use a temporary directory and cleanup trap for wrappers and captured test - output. The driver must restore default role convergence before exit where - possible and must never delete unrelated host content. -9. Update the README with development-environment setup, static validation, - local integration warnings, the single test-driver command, expected host - mutations, and recovery instructions limited to rerunning default - convergence. -10. Remove any Stage 1 wording that says installation is not implemented. - -### Validation - -From `ansible-role-seriatim`, run: - -```sh -tests/integration -git diff --check -git status --short -``` - -Expected result: the driver completes every static, convergence, idempotence, -source-cleanup, compiler-change, failure-preservation, and final verification -case; the installed host state ends at default Seriatim `v1.5.0`; Git reports -no whitespace errors; and only intended repository changes are present before -the stage commit. - -After committing, run `tests/integration` once more. The repository must remain -clean afterward. - -### Completion Criteria - -- One documented command reproduces all MVP validation on the local Ubuntu - 26.04 proxy. -- Idempotence, compiler-version invalidation, managed-source cleanup, exact - versioning, and failed-build preservation have executable regression - coverage. -- README instructions match the implemented role and test behavior. -- The post-commit integration run passes and leaves the role repository clean. - -### Commit - -Commit intent: `test: verify Seriatim source installation` - -## Final Validation and Handoff - -After Stage 4, run: - -```sh -tests/integration -git log --oneline -4 -git status --short --branch -cd .. -./scripts/status -``` - -The implementation handoff must report: - -- the four stage commit hashes in order; -- the final `seriatim --version` output; -- the final source commit, Go version, binary checksum, ownership, and mode; -- the clean-repeat recap showing `changed=0` and `failed=0`; -- confirmation that deliberate build failure preserved the prior binary and - state; -- every deviation from this plan or feature roadmap; -- any unresolved findings or recommended follow-up work. - -Do not mark Feature 0001 `done` and do not edit workspace status during the -out-of-band execution. Completion requires subsequent review. - -## Progress Notes - -- Stage 1 completed in `ansible-role-seriatim` commit `ac35c1f`. -- Stage 2 completed in `ansible-role-seriatim` commit `b84b045`. -- Stage 3 completed in `ansible-role-seriatim` commit `81313f0`. -- Stage 4 completed in `ansible-role-seriatim` commit `efd4734`. +The prior Audita plan was withdrawn because it propagated the superseded +Seriatim build-state and hardening architecture. Git history preserves that +research, but those execution instructions must not be used. diff --git a/docs/roadmap/mvp.md b/docs/roadmap/mvp.md index 8a3d846..2168f3a 100644 --- a/docs/roadmap/mvp.md +++ b/docs/roadmap/mvp.md @@ -50,17 +50,21 @@ role will manage the native configuration needed to connect to it. - Released roles build an explicitly selected application release tag. Development may override the selected revision with a branch or commit. - Source checkouts reside at `/usr/local/src/` and are owned by `root`. -- Roles build into a staging location, validate the result, and then install - the binary at `/usr/local/bin/` with `root:root` ownership and mode - `0755`. A failed build must not replace a working installed binary. +- Roles build and install the binary at `/usr/local/bin/` with + `root:root` ownership and mode `0755`, using the application's native build + command and the simplest clear Ansible task flow. - Builds use the application repository's own build contract, including any application-specific version-injection linker flags. The common baseline is `GOWORK=off`, `GOTOOLCHAIN=local`, `CGO_ENABLED=0`, and `-trimpath`. -- Build state must account for the checked-out commit, Go toolchain, and build - arguments so that roles rebuild when a relevant input changes rather than - relying only on the existence of the installed binary. -- Each application role exposes variables for its repository, selected - version or revision, source directory, binary path, and build settings. +- Roles normally rebuild when the Git task reports a changed checkout, the + binary is absent, or an application-level version or smoke check shows that + the installed binary is unsuitable. They do not maintain fingerprints, + checksums, or sidecar build-state files for the MVP. +- Each application role exposes variables only for meaningful consumer + choices, normally including its repository, selected version or revision, + source directory, binary path, dependency management, and Go Snap channel. + Fixed details of the upstream build contract need not become public + variables. ### Go toolchain @@ -131,6 +135,13 @@ application-role releases in one source repository. - Small amounts of repeated prerequisite or installation logic are acceptable during the MVP. A shared helper role or collection plugin should be introduced only if substantial, stable shared behavior emerges. +- Ansible modules are trusted to implement their documented state transitions, + idempotence, and change detection. Roles do not add preliminary existence + checks, manual comparisons, or supplemental state around a module that + already owns the desired behavior. +- State gathering is appropriate only when it controls behavior outside the + responsible module's contract, such as deciding whether an application + binary must be rebuilt. ### Dependency and release reproducibility @@ -213,6 +224,12 @@ remain to be designed. - Deploying or managing WhisperX - Versioned Narratio prompt-library releases and pinning - A role-managed scheduler or persistent Narratio service +- Cryptographic artifact verification, supply-chain hardening, and protection + against adversarial source or dependency infrastructure +- Transactional binary replacement and preservation of the prior executable + after a local build failure +- Compiler-identity fingerprints, binary checksums, sidecar build-state files, + and recovery from deliberately malformed or redirected managed paths ## Design Work Still Open diff --git a/docs/roadmap/remediation.md b/docs/roadmap/remediation.md index 7c977e8..cc3a302 100644 --- a/docs/roadmap/remediation.md +++ b/docs/roadmap/remediation.md @@ -1,174 +1,19 @@ -# Feature 0001 Remediation Findings +# Feature 0001 Prior Remediation Record -Status: resolved +Historical record: superseded -## Review Scope +The original Feature 0001 review required increasingly elaborate recovery for +build-state files, metadata drift, custom path shapes, and symbolic links. +Those findings were resolved in `ansible-role-seriatim` commits `c6c1907` +through `6fb1aa2`, but the resulting architecture was later rejected as +disproportionate to the MVP. -This review compared `ansible-role-seriatim` commits `ac35c1f` through -`efd4734` with the Feature 0001 roadmap and staged implementation plan. It also -ran the committed static and integration validation and performed targeted -state-corruption, permission-drift, and custom-path checks on the managed local -installation. +The authoritative replacement specification is +`docs/roadmap/features/0001-seriatim-source-installation.md`. It deliberately +removes persistent build state and treats unusual corruption, adversarial path +redirection, exact compiler-input tracking, and transactional replacement as +potential future hardening rather than current requirements. -The four planned implementation stages are structurally present and the -committed integration suite passes. The findings below are gaps that must be -addressed before Feature 0001 is marked complete or its patterns are copied to -other application roles. - -## Finding 1: Build-input state content is not part of convergence - -Severity: high - -`tasks/build.yml` checks whether `build-inputs.json` exists but never reads or -compares its content. The rebuild decision trusts `fingerprint.sha256` -independently. An empty, corrupted, stale, or replaced build-input document is -therefore accepted as converged when the fingerprint and binary checksum files -remain unchanged. - -This diverges from the plan's requirement that malformed or unreadable state -cause a rebuild and from the role's claim that readable inputs and their -fingerprint describe the installed result. A targeted test truncated -`build-inputs.json`; convergence returned `changed=0` and `failed=0`, while the -subsequent verification play correctly failed its input/fingerprint assertion. - -Required remediation: - -- Read the stored build-input document safely during convergence. -- Require its exact canonical content to match the desired build inputs and - require its SHA-256 digest to match the stored and desired fingerprint. -- Treat missing, unreadable, malformed, or inconsistent input state as a - rebuild condition rather than an unhandled failure. -- Extend `tests/integration` to corrupt or replace `build-inputs.json`, verify - that convergence repairs it, and verify that the following run is - idempotent. - -## Finding 2: Managed ownership and modes drift without repair - -Severity: medium - -The binary's checksum and version participate in the rebuild decision, but its -owner, group, and mode do not. Binary metadata is set only inside the rebuild -block. Build-state directory and file metadata are likewise set only after a -build. A content-identical installation can therefore remain in an unsafe or -incorrect metadata state indefinitely. - -A targeted test changed `/usr/local/bin/seriatim` from mode `0755` to `0777`. -Convergence returned `changed=0` and left mode `0777` in place. This violates -the target state requiring a root-owned, non-operator-writable binary. The same -design leaves drifted state-file ownership and permissions uncorrected. - -Required remediation: - -- Converge `seriatim_binary_path` to `root:root` mode `0755` whenever a valid - installed regular file exists, without forcing an unnecessary rebuild. -- Converge `seriatim_build_state_dir` to `root:root` mode `0755` and its three - managed files to `root:root` mode `0644` on every successful role run. -- Treat unexpected file types, including a directory at the binary path, as a - clear validation or repair condition rather than passing them into the build - copy operation. -- Add integration cases that drift binary and build-state metadata, require - one repairing run, and require an idempotent repeat. - -## Finding 3: A configurable binary path requires a pre-existing parent - -Severity: medium - -`seriatim_binary_path` is a documented public path override, but the role does -not create or validate its parent directory. `ansible.builtin.copy` consequently -fails after a successful build when the configured parent is absent. A -targeted run using `/tmp/seriatim-review-missing-parent/seriatim` failed with -`Destination directory ... does not exist`. - -Required remediation: - -- Inspect the configured binary parent before building. -- Fail clearly if an existing parent is not a directory. -- Create a missing parent as `root:root` mode `0755` without changing ownership - or mode on an already existing shared directory. -- Add an isolated integration case proving that a nondefault binary path with - a missing parent installs, validates, and converges idempotently. - -## Finding 4: Role metadata and privilege documentation are incomplete - -Severity: low - -Stage 1 required role source and issue information, but `meta/main.yml` omits -the supported `issue_tracker_url` field. The README also does not state plainly -that consumers must apply the role with privilege escalation, even though it -manages packages, snaps, root-owned source, and `/usr/local/bin`. - -Required remediation: - -- Add the role repository's Gitea issue tracker to `meta/main.yml`. -- Add role repository and issue links to the README where appropriate. -- State explicitly that the consuming play must use `become: true` or provide - equivalent root privileges. -- Re-run metadata/schema validation and ansible-lint. - -## Remediation Completion Criteria - -- All four findings are addressed in `ansible-role-seriatim`. -- Static validation and the expanded `tests/integration` suite pass. -- Targeted corruption, metadata-drift, and alternate-path cases converge to - the documented state and then report `changed=0` on an immediate repeat. -- The default local installation ends at Seriatim `v1.5.0`, source commit - `0fc92f364316c874d366b00cdc884a33e1900b25`, with a clean checkout and - consistent build state. -- The role and workspace repositories are clean, and remediation commits are - ready for review before Feature 0001 is marked complete. - -## Resolution Evidence - -- Finding 1 was resolved by `ansible-role-seriatim` commit `c6c1907`. -- Finding 2 was resolved by `ansible-role-seriatim` commit `f2c1b1a`. -- Finding 3 was resolved by `ansible-role-seriatim` commit `92906eb`. -- Finding 4 was resolved by `ansible-role-seriatim` commit `07dd736`. -- Malformed state-object recovery was hardened by - `ansible-role-seriatim` commit `ca49dae` during the completion audit. -- The expanded `tests/integration` suite passed after all remediation commits - and restored the default Seriatim installation. - -## Completion Review - -The four original findings are resolved, and the expanded integration suite -passes. A focused review of the malformed-state recovery identified one -additional edge case, which has also been resolved. - -## Finding 5: A malformed build-state directory is not handled - -Severity: medium - -`tasks/build.yml` inspects the three state files beneath -`seriatim_build_state_dir` without first inspecting the state-directory path. -If that configured path exists as a regular file, the first -`ansible.builtin.stat` fails with `Not a directory`. This is an unhandled -malformed-state failure rather than the planned recovery behavior. A targeted -run with an otherwise valid custom binary confirmed that the role exits before -rebuilding or converging the state directory. A second targeted run using a -symbolic link as the state-directory path succeeded and wrote all three -root-owned state files into the link target while leaving the configured path -as a symbolic link. - -Required remediation: - -- Inspect `seriatim_build_state_dir` before inspecting any child state path. -- Define and implement deliberate behavior for an existing non-directory - object. Because the state directory is fully managed, replacing that object - and rebuilding is consistent with the existing malformed-state-file - behavior; alternatively, fail with a purpose-written assertion if preserving - an unexpected object is preferred. -- Ensure an existing symbolic link is not silently treated as the managed - state directory or used to redirect root-owned state writes. -- Add integration coverage for the selected non-directory behavior and require - an idempotent immediate repeat after recovery when recovery is supported. - -Resolution: - -- `ansible-role-seriatim` commit `6fb1aa2` makes the build-state directory - authoritative, replacing regular files and symbolic links with a root-owned - directory before inspecting child state. -- Integration coverage confirms repair and immediate idempotence for both - regular-file and symbolic-link drift, and confirms that the symbolic-link - target remains untouched. -- The complete integration suite passes and restores a verified default - installation. +This file is retained only to explain the historical commit sequence. It is +not an active remediation specification and must not be used as a template for +another role. diff --git a/docs/status.md b/docs/status.md index 11b2958..f051539 100644 --- a/docs/status.md +++ b/docs/status.md @@ -4,7 +4,8 @@ Last updated: 2026-09-07 Current milestone: MVP standalone application roles -Current focus: Select the next standalone application-role feature. +Current focus: Review the revised Feature 0001 specification and prepare a +minimal Seriatim simplification plan. | ID | Deliverable | Owner | State | Evidence or next step | | --- | --- | --- | --- | --- | @@ -13,11 +14,12 @@ Current focus: Select the next standalone application-role feature. | M0-03 | Tracked MVP roadmap | workspace | done | `docs/roadmap/mvp.md` established | | M0-04 | Aggregate repository status | workspace | done | `scripts/status` created | | 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 | ready | Select the next item from the roadmap | -| M1-02 | Standalone application roles | role repositories | in_progress | Seriatim complete; select the next application role | +| M1-01 | Remaining MVP design decisions | workspace | ready | Resume after the revised Seriatim pattern is accepted | +| M1-02 | Standalone application roles | role repositories | in_progress | Simplify Seriatim before continuing to other roles | | M1-03 | Suite-level pipeline role | ansible-role-dnd-pipeline | proposed | Begin after standalone role interfaces stabilize | | 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 | Role commit `6fb1aa2`; feature and remediation acceptance checks pass | +| F0001 | Install Seriatim from pinned source | ansible-role-seriatim | ready | Revised roadmap ready; staged simplification plan is next | +| F0002 | Install and configure Audita | ansible-role-audita | proposed | Replan after the simplified Seriatim role is reviewed | ## Updating This File