22 KiB
Implementation Plan 0001: Seriatim Source Installation
Status: proposed
Target executor: GPT-5.6-Terra
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.builtinmodules where they directly fit. Use the approvedcommunity.general.snapmodule 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-seriatimusing 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-dndandansible-role-seriatimstart clean onmain.- The active implementation plan has been reviewed and committed before execution begins.
- The controller host is Ubuntu 26.04.1
amd64and has passwordless privilege escalation available for the approved local integration run. - Workspace tools are available at
../.venv/binfrom 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.0resolves to commit0fc92f364316c874d366b00cdc884a33e1900b25. - The upstream
v1.5.0contract is:- module requirement
go 1.25; - build package
./cmd/seriatim; CGO_ENABLED=0and-trimpath;- linker flags
-s -w -X gitea.maximumdirect.net/eric/seriatim/internal/buildinfo.Version=<version>; seriatim --versionoutputseriatim version <version>.
- module requirement
Repository Scope
ansible-role-seriatimis the only implementation repository and owns every role, test, dependency, and README change in this plan.ansible-workspace-dndowns this plan and the feature roadmap but remains read-only during out-of-band execution.../seriatimmay be inspected as the authoritative upstream contract and must remain unchanged.- All other repositories in
workspace.ymlremain 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-seriatimis clean onmainand tracksorigin/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
- Add the standard role paths
defaults/,meta/,tasks/,tests/, andcollections/. Use YAML document markers and fully qualified module names. - Define every variable in the Fixed Role Interface in
defaults/main.yml. Do not expose aliases, compatibility variables, removal controls, or configuration-file settings. - Add
meta/main.ymlwith Eric Rakestraw as author, BSD-3-Clause licensing, minimum Ansible Core 2.18, Ubunturesoluteplatform metadata, source and issue URLs, and tags appropriate to Seriatim and transcript processing. - 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. - Add
tasks/main.ymlandtasks/validate.yml. Run validation before every task that can change the host. Assert:- distribution is Ubuntu;
- distribution version is
26.04; - architecture is
x86_64oraarch64; - 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.
- Pin
community.generalversion13.3.0incollections/requirements.yml. This collection requires Ansible Core 2.18 or newer and supplies the channel-awarecommunity.general.snapmodule. - Extend
.gitignoreonly for repository-local validation state, including.ansible/, test output, and retry files. Preserve the existing general ignore entries. - Add
tests/inventorytargeting localhost with local connection and an explicit/usr/bin/python3.14interpreter. Addtests/converge.ymlthat gathers facts, enables privilege escalation, and applies the repository role throughANSIBLE_ROLES_PATH. - 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:
../.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-seriatimcontains 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 goshows the configured1.26/stablechannel.- 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
- Add
tasks/dependencies.ymland include it after validation. - When
seriatim_manage_build_dependenciesis true:- install the Ubuntu
gitpackage withansible.builtin.apt, using a reasonable cache-validity interval rather than refreshing on every run; - install the single
goSnap throughcommunity.general.snapwithclassic: true,channel: "{{ seriatim_go_snap_channel }}", andstate: present.
- install the Ubuntu
- 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. - Add
tasks/source.ymland include it after dependencies. Create the parent source directory asroot:rootbefore cloning. - Use
ansible.builtin.gitwith 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. - 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.
- Enforce root ownership on the checkout and resolve the final commit with
git rev-parse HEAD. Assert that the defaultv1.5.0execution resolves to0fc92f364316c874d366b00cdc884a33e1900b25in the local integration test; nondefault refs record their actual resolved commit without that assertion. - 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:
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.0commit. /snap/bin/go versionreports a toolchain compatible with the upstream module requirement.
Owning Repository
ansible-role-seriatim
Required Changes
- Add
tasks/build.ymlafter source convergence. - Construct one canonical build-input document containing at least:
- resolved source commit;
- complete
go versionoutput; - configured Go binary path;
- build version;
- ordered build flags;
- linker flags;
- package path;
- fixed
GOWORK,GOTOOLCHAIN, andCGO_ENABLEDvalues. Store the readable input document and its SHA-256 fingerprint inseriatim_build_state_dironly after successful installation.
- 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. - When no rebuild is required, perform no mutating build or install task and report no change.
- When rebuilding, create a unique temporary directory with
ansible.builtin.tempfile. Invoke the configured Go binary withansible.builtin.commandargv, the fixed environment, configured build flags, one-ldflagsvalue, a staged output path, and the configured package fromseriatim_source_dir. - Validate the staged binary before installation. Require exact stdout
seriatim version {{ seriatim_build_version }}and a successful exit from--version. - Install the validated staged binary to
seriatim_binary_pathusing an atomic Ansible file operation withroot:rootownership and mode0755. Validate the installed binary again, calculate its checksum, and only then write the build-input, fingerprint, and checksum state files. - Use an
alwayscleanup 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. - Give command tasks precise
changed_whenandfailed_whenbehavior so ansible-lint passes and check/reporting semantics are honest. - Update the README with the exact build contract, rebuild triggers, state
location, ownership, safe replacement behavior, and development override
example setting both
seriatim_source_refandseriatim_build_version.
Validation
Run static validation, then converge and inspect:
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
- Add
tests/verify.ymlwith 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.
- Add an executable
tests/integrationshell 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. - Have the driver run default convergence, verification, and an immediate
second convergence. Parse the second recap and fail unless
changed=0andfailed=0. - 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.
- 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.
- Test compiler-input invalidation with a temporary Go wrapper. Its
versionpath must report a distinct valid Go 1.26 patch identity while all other commands delegate to/snap/bin/go. Converge withseriatim_go_binaryset to the wrapper, require a rebuild, repeat and require idempotence, then restore the default Go path and require one final rebuild followed by idempotence. - 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 --versionstill to reportv1.5.0. - 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.
- 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.
- Remove any Stage 1 wording that says installation is not implemented.
Validation
From ansible-role-seriatim, run:
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:
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 --versionoutput; - the final source commit, Go version, binary checksum, ownership, and mode;
- the clean-repeat recap showing
changed=0andfailed=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-seriatimcommitac35c1f. - Stage 2 completed in
ansible-role-seriatimcommitb84b045. - Stage 3 completed in
ansible-role-seriatimcommit81313f0. - Stage 4 completed in
ansible-role-seriatimcommitefd4734.