21 KiB
Workstream 10 Implementation Plan — Quality, Testability, And Documentation Alignment
Status
Planned implementation for Workstream 10, Quality, testability, and
documentation alignment, in refactoring.md. Workstreams 1
through 9 are complete prerequisites. This is the final actionable workstream
in the consolidated refactoring roadmap; it closes cross-cutting gaps that were
not already completed alongside the behavior-owning workstreams.
Authoritative Scope And Fixed Decisions
Workstream 10 in refactoring.md is authoritative. Its
supporting first-pass findings are H07, H08, H12, H13, H17, H18, H23, and H25
in audit.md. The corresponding contract decisions are C01 through
C06 in audit-decisions.md; their full rationale remains
in contract-audit.md. Read those cited sections rather
than re-auditing unrelated hypotheses.
Most of Workstream 10's risk coverage and documentation alignment landed with Workstreams 1 through 9. Preserve that completed work. The remaining decisions are:
- Do not reopen completed product, compatibility, filesystem, manifest, analyze, configuration, publication, lock, or cleanup decisions. This workstream changes internal testability and test diagnostics without changing CLI, configuration, artifact, manifest, integration, publish, restore, or cleanup behavior.
- Remove the seven remaining mutable application function seams:
readSecretsDirectoryEntries,loadPipelineConfigFn,downloadObjectToTempFn,newObjectStoreFromConfigFn,discoverRemoteCurrentStateFn,buildRestorePlanFn, andexecuteRestorePlanFn. Compiled regular-expression values, canonical stage relations, and fixed command-name data are not collaborator seams and are not part of this removal. - Use one package-private, value-owned
commandDependenciesset for these collaborators. A normalization function supplies production defaults for nil fields. Each command invocation receives its own value; there is no mutable default singleton, context-carried service locator, dependency-injection framework, or new public interface. Keep runnerinvocationDependenciesseparate because it owns a different lifecycle. - Preserve exported command entry points and their existing signatures. Production wrappers use normalized defaults. Private dependency-aware entry points let tests supply only the external or phase boundary they control. Thread the dependency value explicitly through application-owned command, configuration-loading, secret-loading, object-store, and restore helpers.
- Tests must never replace or restore package-level functions. Use dependency
values, real temporary filesystems,
storage.FakeBackend, and existing controlled subprocess helpers at their durable boundaries. Process environment mutation remains permitted where credential behavior itself is under test, but such tests must remain non-parallel and restore state throught.Setenvor the existing focused helper. - Add
github.com/google/go-cmp/cmponly for the audit's selected large structural comparisons: composed configuration values, analyze plans/state, restore plans/actions, and publish output plans/mappings. Preserve nil versus empty and ordering semantics unless a domain contract explicitly equates them. Use direct comparison orslices.Equalfor simple comparable values; do not mechanically replace everyreflect.DeepEqualcall. - Apply
slicesormapsin production only when code touched by this workstream performs a genuinely shallow clone, membership check, or deterministic key operation with identical nil, empty, and ordering behavior. Do not conduct a repository-wide style rewrite and do not replace domain normalization, deep copies, provenance merges, or topological order. - Retain the focused behavior coverage already established for atomic file visibility, bounded reads and adapter diagnostics, group modes, validated downloads, Notarius rename, destructive confinement, restore symlink conflicts, secret protection, session-only progress, per-artifact analyze checkpoints, planning/execution parity, remote-lock carry-forward, and idempotent cleanup. Delete only dead helpers and reset choreography associated with the removed mutable seams; do not delete a current-contract test merely to reduce test lines.
- Update only canonical internal documentation whose implemented dependency or test boundary changes. Because public behavior is unchanged, do not churn CLI, configuration, operations, troubleshooting, integration, examples, or release documentation unless implementation uncovers a factual inaccuracy in its owning document.
Instructions For Every Stage
Before each stage:
- Read
docs/development.md, all documents underdocs/policy/, this plan, Workstream 10 inrefactoring.md, and only its cited audit sections. Read../internal/overview.mdand, for restore changes,../internal/command-restore.md. - Inspect the affected production code and focused tests. Prefer the codebase knowledge graph for Go definitions and call paths; use text search for mutable assignments, test reset choreography, documentation, fixtures, and final removal checks.
- Confirm worktree state and preserve unrelated changes. Do not implement release work or any deferred/no-action audit hypothesis.
During each stage, keep dependencies package-private and invocation-owned,
preserve acyclic package direction, keep default tests deterministic and
offline, and retain credential redaction and filesystem safety. Prefer behavior
assertions over collaborator choreography. Run gofmt on changed Go files,
go mod tidy when module state changes, and git diff --check. Commit only a
completed stage with a short plain-English message, and update a canonical
internal document in the same stage only when its owned behavior changes.
Stage 1 — Replace Configuration And Secret-Loading Globals
Status: Completed
Goal
Establish the command-scoped dependency value and remove global substitution from configuration and API-key loading.
Required Work
- Add a package-private
commandDependenciestype ininternal/appwith typed function fields for pipeline loading, temporary object download, secrets-directory reading, object-store construction, remote-current discovery, restore-plan construction, and restore-plan execution. Add a value-returning normalizer that fills nil fields with the current production functions. Do not store a normalized instance in a package variable. - Add dependency-aware private variants of the configuration-loading path:
loadCommandConfig,loadPipelineCampaignConfig, andloadPipelineConfigshould receive or delegate to an explicit dependency value. Preserve local session precedence, remote fallback, source provenance, bounded temporary download behavior, and idempotent temporary-file cleanup. - Add a dependency-aware private secret loader used by focused tests and by later command composition. Production callers continue through a wrapper using normalized defaults. Preserve directory/file privacy checks, symlink rejection, bounded reads, existing-environment precedence, redaction, and the ancestor-replacement failure case.
- Remove
loadPipelineConfigFn,downloadObjectToTempFn, andreadSecretsDirectoryEntries. Rewriteconfig_loader_test.go, loader-focused cases inremote_session_test.go, andsecrets_env_test.goto pass copied dependency values instead of saving, replacing, and restoring globals. Leave command-routing remote-session cases for the command stage that owns their entry point. Retain tests at the narrowest existing behavior boundary; do not reproduce the loader's internal call sequence with mocks. - Keep production command behavior unchanged while this foundation is introduced. It is acceptable for the object-store and restore fields to use their production defaults until Stages 2 through 4 migrate their callers.
Verification And Exit Criteria
Run:
go test ./internal/app -run 'Config|RemoteSession|Secret'
go test -race ./internal/app -run 'Config|RemoteSession|Secret'
git diff --check
The three named mutable globals are absent, remote/local configuration behavior and secret protections remain covered, and tests supply collaborators through local dependency values.
Stage 2 — Carry Dependencies Through Pipeline Commands
Status: Completed
Goal
Carry one dependency value through the pipeline-oriented command paths, including remote session fallback, without changing their public API.
Required Work
- Add a private dependency-aware execution dispatcher alongside
Execute.Executeretains its signature and uses normalized production dependencies. Extend the existing private pipeline-executor path rather than creating a second command switch or an application framework. - Thread
commandDependenciesthroughrun,regenerate-artifacts,run-stage,analyze,publish,plan, andstatuspaths and the shared configuration loader they use. Preserve the independentpipelineExecutorseam and pass both values explicitly where required. - Make remote session fallback initialize its object store through the dependency value and preserve the rule that secrets load before the factory. Public command wrappers continue to use production defaults.
- Rewrite the assembled pipeline-command tests and only those remote-session
tests reached through pipeline commands to call the dependency-aware
dispatcher with a fake store. Remove their global object-store
replacement/reset choreography. Leave remote-session cases reached through
sessioncommands for Stage 3. It is acceptable for the factory global itself to remain temporarily for restore and operator callers until Stages 3 and 4 migrate them. - Preserve command syntax, exit classification, stdout/stderr behavior, configuration discovery, artifact selection, executor injection, and remote session provenance. Do not move adapter construction into configuration or storage packages.
Verification And Exit Criteria
Run:
go test ./internal/app -run 'Run|Stage|Analyze|Publish|Plan|Status|RemoteSession|Command'
go test -race ./internal/app -run 'Run|Stage|Analyze|Publish|Plan|Status|RemoteSession'
git diff --check
Pipeline commands use invocation-owned dependencies, production defaults still select the real storage constructor, and focused tests remain offline.
Stage 3 — Carry Dependencies Through Operator Commands
Status: Pending
Goal
Migrate session and maintenance commands to the same invocation-owned command dependency path before removing the final shared storage seam.
Required Work
- Thread
commandDependenciesthrough thesessiondispatcher, session initialization and validation, artifact listing, lock listing/add/remove, and session-scoped or cache-aware clean helpers. Exported command functions remain production-default wrappers. - Update the dependency-aware top-level dispatcher so every operator command
reached through
Executereceives the invocation's value. Keep local-only paths independent of remote construction; do not initialize an object store merely because a dependency value exists. - Rewrite
object_store_test.go,operator_helpers_test.go, affected session/clean tests, and remote-session cases reached through thesessiondispatcher to supply a local object-store factory. Keep complete CLI routing tests at the private dispatcher and narrow construction-order tests atnewCommandObjectStore. Remove original-value capture and reset boilerplate in the migrated tests. - Preserve secret-before-factory ordering, local/remote session initialization,
validation findings, artifact availability, per-source remote lock behavior,
cleanup scope, output, and errors. Continue using
storage.FakeBackendfor remote behavior. - Leave only restore callers on the temporary global storage seam. Do not remove it until Stage 4 can update restore and its tests atomically.
Verification And Exit Criteria
Run:
go test ./internal/app -run 'ObjectStore|SessionInit|SessionValidate|Artifacts|Locks|Clean|Operator'
go test -race ./internal/app -run 'ObjectStore|SessionInit|SessionValidate|Artifacts|Locks|Clean'
git diff --check
All non-restore commands use invocation-owned dependencies, their tests mutate no factory global, and remote behavior remains deterministic and offline.
Stage 4 — Replace Restore Phase Globals With Explicit Dependencies
Status: Pending
Goal
Make restore sequencing testable per invocation without global phase hooks or a new restore framework.
Required Work
- Keep
Restoreas the public production wrapper and add one private dependency-aware restore entry point. Normalize dependencies once at that boundary and use its object-store constructor, remote-current discovery, plan builder, and plan executor throughout the invocation. - Remove
newObjectStoreFromConfigFn,discoverRemoteCurrentStateFn,buildRestorePlanFn, andexecuteRestorePlanFn. Call the concrete production functions through the normalized dependency value; do not add interfaces around pure phase functions or move restore orchestration out ofinternal/app. - Rewrite
restore_test.go,restore_execution_test.go, andrestore_recovery_test.goto pass local dependencies. Consolidate repeated dependency construction into one small test helper that returns a fresh value and never mutates package state. - Preserve the observable restore boundaries: dry-run performs no local installation; non-dry-run planning occurs while holding the session lock; conflicts do not execute; the incomplete marker brackets installation; reports reflect failure or success; manifest installation remains last; and lock release errors remain joined with an earlier failure.
- Prefer real
buildRestorePlanandexecuteRestorePlanwithstorage.FakeBackendwhere the test is about restore results or filesystem state. Stub a phase only when the phase call or its timing is the behavior being tested, such as proving the transition lock is already held. - Update
docs/internal/command-restore.mdonly if its description of phase composition or test seams is no longer accurate. Do not change the operator restore contract.
Verification And Exit Criteria
Run:
go test ./internal/app -run 'Restore'
go test -race -shuffle=on -count=3 ./internal/app -run 'Restore'
git diff --check
No command has a mutable production function seam, restore transaction ordering and recovery outcomes remain covered, and restore tests can execute independently without save/restore choreography.
Stage 5 — Improve Selected Large Structural Test Diagnostics
Status: Pending
Goal
Make failures in the four audit-selected large-value test categories readable without imposing a repository-wide assertion style.
Required Work
- Add
github.com/google/go-cmp/cmpas a direct test dependency and updatego.modandgo.sumwith the Go toolchain. Do not add an assertion framework or helper wrapper aroundcmp.Diff. - Replace existing whole-value
reflect.DeepEqualassertions withcmp.Diffwhere a field-level diff is materially useful in these files:- composed/effective configuration comparisons in
internal/config/pipeline_composition_test.goandinternal/config/pipeline_profiles_test.go; - complete analyze plans, artifact-state maps, or stage records in
internal/stage/analyze_plan_test.go,internal/stage/analyze_reconciliation_test.go, andinternal/app/analyze_checkpoint_recovery_test.go; - restore action collections or complete plans in
internal/app/restore_plan_test.go; and - complete output outcomes, declarations, or mappings in
internal/stage/publish_output_plan_test.go.
- composed/effective configuration comparisons in
- Keep direct field assertions that intentionally isolate one invariant. Use
slices.Equalfor simple ordered slices in the touched files where it is clearer; retainreflect.DeepEqualwhen its value is small and the mismatch is already obvious. Do not expand this stage to every test package. - Do not use
cmpopts.EquateEmpty,cmp.AllowUnexported, blanket ignore options, or custom comparers merely to force a passing comparison. Compare the same observable values as before and preserve distinctions in nil, empty, order, timestamps, and provenance. - Run
go mod tidyand confirm the dependency is used only by tests. Applyslicesandmapsto production touched in Stages 1–4 only where the fixed decisions above permit an identical shallow operation; do not manufacture a production change to demonstrate their use.
Verification And Exit Criteria
Run:
go mod tidy
go test ./internal/config ./internal/stage ./internal/app
go test -race ./internal/config ./internal/stage ./internal/app
git diff --check
The selected large comparisons emit useful structural diffs, simple assertions remain simple, module state is tidy, and no broad assertion-style migration has occurred.
Stage 6 — Verify Coverage, Align Internal Documentation, And Close The Roadmap
Status: Pending
Goal
Prove that the reduced test and documentation system protects the final refactored architecture, then close Workstream 10 and the consolidated roadmap.
Required Work
- Search production and tests for all seven retired function-seam names and for assignments that replace application functions. Remove any remaining dead wrappers, original-value captures, reset helpers, or cleanup callbacks whose sole purpose was global substitution. Do not remove compiled regexes, canonical data tables, or legitimate local function values.
- Review the existing focused owners—not the entire suite indiscriminately—to
confirm retained coverage for:
- atomic visibility and bounded reads in
internal/fileopsand storage adapter tests; - POSIX group modes in
internal/fileops/modes_posix_test.go; - validated storage downloads and Notarius promotion in storage adapter and extract tests;
- destructive confinement and restore symlink conflicts in fileops, cleanup, and restore tests;
- credential privacy and redaction in secrets, logging, subprocess, and adapter tests;
- session-only progress and per-artifact recovery in manifest, stage, and application checkpoint tests;
- planning/execution parity in
internal/app/run_control_test.go; - remote-lock carry-forward in publish planning/execution tests; and
- idempotent final cleanup in stage and application cleanup tests. Add a test only if a listed consequential boundary is genuinely absent; consolidate or delete only demonstrably redundant mechanism coverage.
- atomic visibility and bounded reads in
- Review
docs/policy/architecture.md,docs/internal/overview.md, focused internal component documents, and their test inventories for current accuracy. Remove stale implementation claims and duplicate volatile detail; retain links to the canonical CLI, configuration, operations, integration, and release owners rather than copying those contracts. - Confirm no current-behavior document treats a retired compatibility path, run manifest, cleanup obligation, central lock manifest, custom directory promotion, or mutable test hook as supported behavior. Historical roadmap and audit records remain unchanged.
- Run the complete validation matrix below. Use a fresh temporary directory
for the release-asset build and remove it afterward; do not create a release
note, tag, or publication. Record native-platform limitations exactly as in
docs/development.mdrather than treating cross-compilation as runtime evidence. - Only after every check passes, mark Stages 1–6 in this plan and Workstream 10
in
refactoring.mdComplete. Confirm every numbered workstream is then complete. Do not delete the audits, roadmap, or implementation plan and do not prepare or publish a release.
Verification And Exit Criteria
At minimum run:
go mod tidy -diff
test -z "$(gofmt -l $(git ls-files '*.go'))"
go test -count=1 ./...
go test -race -count=1 ./...
go test -race -shuffle=on -count=3 ./...
go vet ./...
go build ./...
go test -count=1 ./internal/doccheck
go test -count=1 ./internal/config -run '^TestExamplesLoadAndValidate$'
git diff --check
Also run scripts/build-release-assets.sh v0.0.0-test <fresh-absolute-temp-dir>
and delete only that verified temporary directory afterward. The workstream is
complete when application collaborators are invocation-scoped, selected large
test failures are diagnostic, retained high-risk behavior is credibly covered,
canonical documentation describes the implemented reduced contracts, all ten
workstreams are complete, and the worktree contains no release artifact.
Open Questions
None. The audits, completed prerequisite workstreams, and fixed decisions above make the remaining quality and documentation work decision-complete.