15 KiB
Code Quality Cleanup Roadmap
Current Baseline
The codebase has completed the local, SSH/SFTP, S3, public bundle package, manifest creation, JSON output, path mapping, link generation, and HTTP upload work documented in the current user and internal docs.
The audit in docs/roadmap/audit.md found no major architectural risk. The
remaining cleanup work should be narrow, behavior-preserving, and focused on
reducing drift in upload staging, runtime config setup, run reporting, backend
config handling, output projection, path validation tests, and internal
coordination code.
One intentional behavior change is part of this cleanup roadmap: malformed
authenticated upload archives should be rejected before 202 Accepted, rather
than accepted and later marked failed. Valid staged uploads should still run
asynchronously after admission.
Cleanup Principles
- Preserve public CLI behavior, config schema, manifest schema, destination state schema, backend behavior, and JSON envelopes unless a stage explicitly says otherwise.
- Keep config parsing and validation in
internal/config. - Keep CLI parsing in
internal/cli. - Keep upload archive policy in
internal/ingest; keep HTTP routing, authentication, and status projection ininternal/app. - Keep backend-specific filesystem, SSH/SFTP, and S3 behavior in adapter packages.
- Prefer small package-local helpers over broad abstractions.
- Add or strengthen tests before refactoring behavior that affects public output, upload admission, path safety, or run reporting.
Active Cleanup Stages
Implement these stages in order. Each stage should be small enough for one implementation prompt and should leave the repository passing the listed focused tests before moving to the next stage.
Stage 1: HTTP Upload Staging Boundary
Goal:
Move archive validation and upload body staging fully behind internal/ingest,
stop app-layer full-body buffering, and reject malformed archives before
returning 202 Accepted.
Implementation scope:
- Add an ingestion-owned content-type helper, such as
ValidateContentType(contentType string) error, and remove duplicated content-type policy from the HTTP handler. - Replace the current handler-side
readUploadBodybuffering with streaming staging throughinternal/ingest. - Introduce a two-step upload coordinator admission model:
- reserve a run id and queue slot before consuming the request body;
- stage and validate the archive using that reserved run id;
- enqueue only a successfully staged local bundle root for async execution.
- Keep queue-full rejection before reading the body.
- Preserve
401for missing or invalid bearer tokens,415for unsupported content type,413for oversized uploads, and503for a full queue. - Return a pre-acceptance
400for malformed tar/gzip content or invalid staged bundles. - Preserve async queued/running/succeeded/failed status after a valid staged bundle is accepted.
- Do not add durable queues, idempotency keys, zstd, or new routes.
Current-behavior documentation updates:
- Update
docs/cli.md,docs/config.md,docs/operations.md,docs/troubleshooting.md,docs/internal/app.md, anddocs/internal/ingest.mdonly as needed to describe the new pre-acceptance failure boundary.
Tests:
go test ./internal/ingest ./internal/app ./internal/cli- Queue-full upload rejection does not read the request body.
- Unsupported content type is rejected through ingestion-owned policy.
- Oversized uploads return
413and do not retain a staged run. - Malformed tar/gzip content returns
400before a run id is issued. - Valid tar and tar.gz uploads return
202after staging and still transition through async status. - HTTP responses and status records do not leak bearer tokens or secret values.
Completion criteria:
internal/appno longer buffers the full upload body before staging.- A valid accepted upload has a committed staged bundle root before the
202response is sent. - Invalid archive content cannot create an accepted run id.
Stage 2: Runtime Config And Secret Setup Helper
Goal:
Centralize runtime config path resolution, config loading, secret loading, environment resolver creation, and secret-conflict warning projection in one app-layer helper.
Implementation scope:
- Add a small
internal/appruntime setup helper that:- defaults an empty config path to
config.DefaultConfigPath; - calls
config.LoadFile; - calls
config.LoadSecretEnvironment; - exposes the loaded config, config path,
config.Environment, and[]OutputWarningfor secret conflicts.
- defaults an empty config path to
- Use the helper from
Run,RunPipeline,RunPipelineWithLocalSource, configuredValidate/Inspect, andServewhere applicable. - Keep
manifest createoutside runtime config loading. - Keep YAML structs, defaults, validation, and secret-directory parsing in
internal/config. - Preserve app test injection points for backend factories and upload handler tests.
Current-behavior documentation updates:
- Update
docs/internal/app.mdif helper boundaries or flow descriptions change. User-facing docs should not change unless observable behavior changes.
Tests:
go test ./internal/config ./internal/app ./internal/cli- Default config path behavior remains unchanged.
- Secret conflict warnings still appear in text and JSON output for
run, configuredvalidate, and configuredinspect. servestill fails startup safely for missing, empty, or duplicate upload tokens without leaking values.- Explicit S3 credential references still resolve through the config-owned environment resolver.
Completion criteria:
- Runtime commands no longer repeat config path defaulting and secret loading.
- Config policy remains owned by
internal/config.
Stage 3: Run Destination Processing Extraction
Goal:
Reduce complexity in the main run loop while preserving run report behavior, warning ordering, action ordering, failure aggregation, and text/JSON output.
Implementation scope:
- Extract narrow helpers from
internal/app/run.gofor destination-scoped processing. - Centralize destination-scoped failure recording so one helper updates
runFailures,runSummary,RunReport.Actions, and pipeline events. - Centralize normalization of partial
publish.Planidentity fields before converting plans to run action records. - Keep app orchestration explicit; do not introduce a generic workflow engine, stage framework, or broad runner abstraction.
- Preserve independent destination fan-out and partial-result behavior.
Current-behavior documentation updates:
- Update
docs/internal/app.mdonly if helper names or package layout descriptions materially change.
Tests:
go test ./internal/app ./internal/publish ./internal/state- Destination open failures for multiple selected bundles keep action records, output errors, summary counters, and pipeline events aligned.
- JSON partial-result output remains unchanged when destination planning or execution fails after a report exists.
- Fixed-path dry-run warnings appear in the same order as before.
- Existing run text output assertions continue to pass.
Completion criteria:
run.godelegates destination-scoped record/failure bookkeeping to helpers.- No public output shape or ordering changes.
Stage 4: Backend Config Normalized View
Goal:
Reduce source/destination backend config drift while preserving the current YAML schema and public config behavior.
Implementation scope:
- Add package-local normalized backend view helpers in
internal/configfor source and destination backend fields. - Use the normalized view to reduce duplication in backend defaulting and validation where it remains clearer than the current paired code.
- Keep
config.Backendandconfig.DestinationYAML structs and tags unchanged. - Preserve destination-only policy fields on
Destination. - Preserve
http_uploadas source-only and invalid for destinations. - Update app backend opening only if the normalized view provides clearer handoff without leaking config internals.
Current-behavior documentation updates:
- None expected unless internal docs mention the old paired implementation shape in a way that becomes misleading.
Tests:
go test ./internal/config ./internal/app- Equivalent local, SSH, and S3 source/destination validation remains consistent.
- Defaults for SSH port/host key policy, S3 region/prefix/force-path-style, and HTTP upload staging fields remain unchanged.
http_uploadremains valid only for sources.
Completion criteria:
- Adding a future backend field has one obvious defaulting/validation path.
- Public config files and examples continue to load unchanged.
Stage 5: Command Output Projection Cleanup
Goal:
Reduce drift in bundle and file metadata projection for app command JSON results.
Implementation scope:
- Add small app-local projection helpers for bundle summaries and manifest file
records used by
validate,inspect, andmanifest create. - Use
time.RFC3339consistently instead of equivalent literal layouts. - Preserve existing JSON envelope fields, command names, command-specific result field names, text output, and fatal error behavior.
- Do not redesign CLI JSON output or HTTP JSON responses.
Current-behavior documentation updates:
- None expected unless tests reveal current docs are stale.
Tests:
go test ./internal/app ./internal/cli- JSON output for
validate,inspect, andmanifest createremains structurally stable. - RFC3339 timestamps remain unchanged, including offset-preserving source timestamps where current behavior preserves them.
- Text output remains unchanged.
Completion criteria:
- Bundle/file projection logic is shared where semantics match.
- Command-specific result structs remain easy to read.
Stage 6: Archive And Source Path Validation Alignment
Goal:
Protect path safety by aligning archive path tests with source and storage path policy, without blurring archive-specific rules.
Implementation scope:
- Add mirrored path-safety table tests around
internal/ingest,pkg/bundle,internal/bundle, andinternal/storagewhere useful. - Keep archive-specific directory handling, root-level manifest rules, duplicate
file rejection, symlink rejection, hardlink rejection, and special-entry
rejection in
internal/ingest. - Centralize code only if the helper can preserve clear archive semantics and current error behavior.
- Do not add new public
pkg/bundleAPIs unless the existing public API cannot safely support the needed shared behavior.
Current-behavior documentation updates:
- None expected unless implementation changes error boundaries or internal package descriptions.
Tests:
go test ./pkg/bundle ./internal/bundle ./internal/ingest ./internal/storage- Absolute paths, traversal, backslashes, empty paths, dot segments, nested
manifests,
.distributor.jsonhandling, symlinks, hardlinks, devices, and sockets remain covered. - Archive directories remain accepted where safe.
Completion criteria:
- Path safety policy has regression coverage across archive staging, source bundle validation, and storage logical path validation.
- Any code sharing is smaller and clearer than the duplicated logic it replaces.
Stage 7: Pipeline Run Coordinator Removal
Goal:
Remove the currently unused internal PipelineRunCoordinator to avoid
maintaining two similar coordination concepts.
Implementation scope:
- Delete
PipelineRunCoordinator,PipelineRunRecord,DuplicatePipelineRunError, related helpers, and their tests. - Remove or rewrite
docs/internal/app.mdsections that describe the removed coordinator. - Keep
UploadCoordinator; do not merge upload queueing with the removed duplicate-run coordinator. - Before deletion, confirm with
rgthat production code does not referenceNewPipelineRunCoordinator,PipelineRunCoordinator, orDuplicatePipelineRunError.
Current-behavior documentation updates:
- Update
docs/internal/app.mdbecause it currently documents the coordinator as an internal implemented component.
Tests:
go test ./internal/app ./internal/clirg -n "PipelineRunCoordinator|NewPipelineRunCoordinator|DuplicatePipelineRunError" internal docsshould show no stale references after removal.
Completion criteria:
- No production, test, or internal documentation references remain for the removed coordinator.
- Upload coordination behavior is unchanged.
Stage 8: Narrow CLI And Test Helper Cleanup
Goal:
Apply only low-risk CLI setup and test fixture cleanup that remains useful after the earlier stages.
Implementation scope:
- Add tiny CLI helpers for repeated
flag.FlagSetsetup or output-format parsing only where command behavior remains obvious. - Keep the standard-library CLI; do not introduce a CLI framework.
- Keep
manifest createinterspersed positional parsing local unless another command now needs the same parsing behavior. - Expand
internal/testutilonly for repeated setup touched by earlier stages. - Do not rewrite tests wholesale just to use shared helpers.
Current-behavior documentation updates:
- None expected unless CLI help or syntax changes. This stage should avoid such changes.
Tests:
go test ./internal/cli ./internal/app- CLI usage-error tests remain stable.
manifest create <path> --id x,manifest create --id x <path>, missing flag values, invalid--format, and help output remain covered.
Completion criteria:
- Remaining CLI/test cleanup is small, readable, and behavior-preserving.
- No public CLI syntax or output changes.
Refactors To Avoid
- Do not introduce a generic workflow engine or stage framework.
- Do not add a CLI framework.
- Do not merge local, SSH, S3, and fake backend adapter implementations.
- Do not collapse
pkg/bundleandinternal/bundle. - Do not move destination state comparison into
internal/publishorinternal/app. - Do not redesign CLI JSON envelopes.
- Do not change HTTP JSON response shapes except where Stage 1 requires pre-acceptance error behavior.
- Do not add durable upload queues, retry workers, zstd support, in-app TLS, idempotency keys, browser UI, or other feature work.
- Do not rewrite tests wholesale to use new fixture helpers.
Validation
After each implementation stage, run the stage-specific tests listed above.
After all cleanup stages:
go test ./...
Recommended consistency checks:
rg -n "LoadFile\\(|LoadSecretEnvironment\\(|DefaultConfigPath" internal/app internal/cli
rg -n "application/x-tar|application/gzip|application/x-gzip" internal docs
rg -n "2006-01-02T15:04:05Z07:00" internal pkg
rg -n "PipelineRunCoordinator|NewPipelineRunCoordinator|DuplicatePipelineRunError" internal docs
The cleanup is complete when:
- all staged tests and
go test ./...pass; - current-behavior docs describe the implemented Stage 1 upload failure boundary;
docs/roadmap/audit.mdfindings have either been addressed or consciously left in place as noted in this cleanup roadmap;- no completed cleanup behavior is documented only as future work.