24 KiB
Pre-1.0 Cleanup Implementation Plan
Purpose
This roadmap defines the staged cleanup work recommended by
docs/roadmap/audit.md. It is written for coding agents that will implement the
cleanup in order.
This is a roadmap document. It may describe planned refactors because it lives
under docs/roadmap/. Implementation agents must not document planned cleanup
as current behavior outside docs/roadmap/.
Governing Policies
Follow these policy documents before implementing any stage:
docs/policy/architecture.mddocs/policy/development.mddocs/policy/documentation.md
The cleanup must preserve these project rules:
- Keep CLI code thin: parse flags, build validated config, delegate.
- Keep config normalization and validation in
internal/config. - Keep public output contracts and validation in
schema. - Keep merge pipeline orchestration in
internal/pipeline. - Keep trim and normalize artifact-level; do not run merge modules from those commands.
- Keep behavior deterministic and sequential.
- Prefer narrow helpers over broad frameworks.
- Do not add concurrency, dynamic plugins, durable state, manifests, remote storage, or resume behavior.
Global Implementation Rules
Each stage should be implemented as a small, behavior-preserving change.
For every stage:
- Start with
git status --short. - Read the files listed in that stage before editing.
- Do not modify unrelated files.
- Preserve public CLI flags, defaults, output schemas, JSON shapes, report event ordering, report event text, and error messages unless the stage explicitly says otherwise.
- Prefer private helpers unless a cross-package helper is genuinely needed.
- Do not introduce generic engines, plugin systems, broad adapter layers, or generics-heavy abstractions.
- Run the stage-specific tests listed in the stage.
- Run
go test ./...before considering the full cleanup sequence complete.
Documentation updates during cleanup should be minimal. If a refactor changes
implemented package boundaries that are described in docs/internal/ or
docs/policy/architecture.md, update those docs in the same implementation
stage. Do not update user-facing docs if behavior did not change.
Stage 1: Centralize Output Schema Names
Goal
Make schema the canonical source for public output schema names and keep
command-specific default/precedence policy in internal/config.
Decision
Use the schema package as the canonical home for output schema name constants
because public output contracts already live there.
Add these exported string constants to schema:
OutputSchemaMinimal = "seriatim-minimal"OutputSchemaIntermediate = "seriatim-intermediate"OutputSchemaFull = "seriatim-full"
Add narrow schema-name helpers in schema, not CLI-specific helpers:
ValidOutputSchemaName(value string) boolOutputSchemaNames() []string
Do not put CLI flag names or default selection policy in schema.
Keep the internal/config constants as aliases for compatibility inside the
repository:
config.OutputSchemaMinimal = schema.OutputSchemaMinimalconfig.OutputSchemaIntermediate = schema.OutputSchemaIntermediateconfig.OutputSchemaFull = schema.OutputSchemaFull
Remove the duplicate string constants from internal/trim/artifact.go; use the
canonical schema constants instead. A type alias or package-local aliases are
acceptable only if they point directly to the schema constants and do not
repeat string literals.
Files To Inspect
schema/output.goschema/output_test.gointernal/config/config.gointernal/config/config_test.gointernal/trim/artifact.gointernal/trim/artifact_test.gointernal/artifact/transcript.gointernal/normalize/build.gointernal/cli/*_test.go
Implementation Steps
- Add canonical output schema constants and helpers to
schema/output.go. - Update
internal/configschema constants to aliasschemaconstants. - Update config validation to use
schema.ValidOutputSchemaNamewhile keeping the existing--output-schema must be one of ...error text. - Update
internal/trimto stop repeating schema string constants. - Update affected tests only where they refer to moved constants.
- Search for raw schema string literals outside tests and docs. Keep raw values in docs and test fixtures where they are intentionally testing serialized public JSON.
Acceptance Criteria
- There is one canonical set of output schema string constants in
schema. mergeandnormalizestill default toseriatim-intermediateand still honorSERIATIM_OUTPUT_SCHEMA.trimstill preserves the input artifact schema when--output-schemais omitted.- Invalid schema error text remains stable unless tests are intentionally updated.
- No new schema registry or plugin-style abstraction exists.
Validation Commands
go test ./schema ./internal/config ./internal/artifact ./internal/trim ./internal/normalize ./internal/cli
Prompt Size
Small enough for one implementation prompt.
Stage 2: Extract Trim Projection Policy
Goal
Use one implementation for trim selector policy, ID validation, retained-index selection, ID renumbering metadata, removed IDs, and empty-output handling.
Decision
Add a private projection helper in internal/trim/apply.go. Do not introduce a
generic artifact framework.
Recommended shape:
type projection struct {
retainedIndexes []int
oldToNewID map[int]int
removedIDs []int
}
func projectSegmentIDs(ids []int, opts Options) (projection, error)
The helper should:
- validate
Mode; - reject an empty selector;
- validate input IDs are positive, unique, and sequential from
1..n; - validate selected IDs exist;
- apply keep/remove semantics;
- return retained input indexes in original order;
- return deterministic old-to-new ID mappings;
- return removed input IDs in original order;
- enforce
AllowEmpty.
Keep schema-specific segment reconstruction in Apply, ApplyIntermediate,
and ApplyMinimal. Keep full-schema overlap group recomputation only in the
full-schema path.
Files To Inspect
internal/trim/apply.gointernal/trim/apply_test.gointernal/cli/trim_test.goschema/output.go
Implementation Steps
- Add the private projection helper and small supporting types.
- Refactor
Applyto call the helper, reconstruct full segments from retained indexes, renumber IDs, clear old overlap group IDs, and recompute overlap groups. - Refactor
ApplyIntermediateto call the helper and reconstruct intermediate segments. - Refactor
ApplyMinimalto call the helper and reconstruct minimal segments. - Preserve existing error messages from
validateMode, empty selectors, invalid input IDs, missing selected IDs, and empty output. - Add a focused table test proving the same selector policy is applied to all three schema shapes.
Acceptance Criteria
- Keep/remove behavior is unchanged for all supported artifact schemas.
- Segment IDs are still reassigned sequentially from
1. - Full-schema trim still recomputes overlap groups.
- Intermediate and minimal trim still do not create overlap groups.
- The common selector and ID policy exists in one helper.
Validation Commands
go test ./internal/trim ./internal/cli
Prompt Size
Small enough for one implementation prompt.
Stage 3: Move Trim Orchestration Into internal/trim
Goal
Make trim match the repository's adapter boundary: CLI parses flags and
delegates; artifact-level orchestration lives in internal/trim.
Decision
Add Run(ctx context.Context, cfg config.TrimConfig) error to internal/trim.
Move from internal/cli/trim.go into internal/trim:
- selector parsing from validated config;
- input file read;
- artifact parse;
ApplyArtifact;- output schema selection and conversion;
- output validation;
- transcript output JSON write;
- trim audit payload construction;
- report event construction;
- old-to-new ID mapping ordering.
Keep in internal/cli/trim.go:
- Cobra command definition;
- flag registration;
cmd.Flags().Changed("output-schema")handling;config.NewTrimConfig;- delegation to
trim.Run.
Temporarily keep any moved JSON writer local to internal/trim if Stage 4 has
not yet been implemented. Stage 4 will centralize file writing.
Files To Inspect
internal/cli/trim.gointernal/cli/trim_test.gointernal/trim/*.gointernal/trim/*_test.gointernal/config/config.gointernal/report/report.gointernal/normalize/normalize.go
Implementation Steps
- Create
internal/trim/run.goor another appropriately named file. - Move trim audit structs to
internal/trim. Keep JSON field names unchanged. - Add
Run(ctx, cfg)and checkctx.Err()before doing work. - Move helper functions needed only by trim orchestration, including ordered ID mapping.
- Update
internal/cli/trim.goto delegate totrim.Run. - Add direct
internal/trimtests for service-level behavior if existing CLI tests do not cover moved report/output behavior clearly. - Keep all existing trim CLI tests passing.
Acceptance Criteria
internal/cli/trim.gocontains flag wiring, config construction, and a call totrim.Run; it no longer performs artifact I/O or report construction.- Trim output files and report files are unchanged for existing tests.
- Trim error wrapping is not weakened for user-facing file, parse, validation, or report-write failures.
- No merge pipeline modules are invoked by trim.
Validation Commands
go test ./internal/trim ./internal/cli
Prompt Size
Small enough for one implementation prompt.
Stage 4: Centralize Deterministic JSON File Writing
Goal
Remove repeated os.Create plus indented json.Encoder boilerplate while
preserving current JSON formatting.
Decision
Create a small package named internal/jsonfile.
Recommended API:
package jsonfile
func Write(path string, value any) error
The helper should:
- create or truncate the target path with
os.Create; - encode JSON with
encoder.SetIndent("", " "); - preserve the current trailing newline produced by
Encoder.Encode; - return underlying create/encode/close errors with enough context for callers to wrap where they already wrap.
Do not implement atomic writes, temporary files, directory creation, lock files, or fsync behavior in this stage.
Files To Inspect
internal/builtin/output.gointernal/report/report.gointernal/normalize/normalize.gointernal/trim/run.goorinternal/cli/trim.go, depending on Stage 3 state- Existing CLI tests that read output JSON
Implementation Steps
- Add
internal/jsonfile/jsonfile.go. - Add a focused
internal/jsonfile/jsonfile_test.gocovering two-space indentation and valid JSON. - Update merge JSON output writer to use
jsonfile.Write. - Update normalize output writing to use
jsonfile.Write, preserving user-facing error context. - Update trim output writing to use
jsonfile.Write. - Update
report.WriteJSONto usejsonfile.Write. - Remove duplicated local
writeOutputJSONhelpers.
Acceptance Criteria
- Merge, trim, normalize, and report JSON are still pretty-printed with two spaces and a trailing newline.
- There is one implementation of deterministic JSON file writing.
- Report construction remains in
internal/reportor command packages; only file-writing mechanics are shared. - No new persistence semantics are introduced.
Validation Commands
go test ./internal/jsonfile ./internal/builtin ./internal/report ./internal/normalize ./internal/trim ./internal/cli
Prompt Size
Small enough for one implementation prompt.
Stage 5: Clean Up Config Path Validation
Goal
Reduce repeated path validation in single-input command config without changing config precedence or errors.
Decision
Add private helpers in internal/config/config.go only. Do not create a config
builder framework.
Recommended helpers:
func normalizeSingleInputFile(path string, flag string) (string, error)
func normalizeOptionalOutputPath(path string, flag string) (string, error)
normalizeSingleInputFile should preserve the current --input-file is required and requireFile behavior.
normalizeOptionalOutputPath should return "" for empty or whitespace-only
values and otherwise call the existing required output path validation.
Keep normalizeInputFiles separate for merge because merge accepts repeated
input files, rejects duplicates, and sorts paths.
Files To Inspect
internal/config/config.gointernal/config/config_test.gointernal/cli/trim_test.gointernal/cli/normalize_test.go
Implementation Steps
- Add private path helpers.
- Refactor
NewTrimConfigto use them. - Refactor
NewNormalizeConfigto use them. - Keep
NewMergeConfigbehavior unchanged except where it can reuse existingnormalizeOutputPath. - Preserve all current error message substrings tested by config and CLI tests.
Acceptance Criteria
- Trim and normalize single-input path normalization is implemented once.
- Optional report path handling is implemented once.
- Merge multi-input normalization remains explicit and unchanged.
- No config struct fields, env vars, flags, defaults, or precedence rules change.
Validation Commands
go test ./internal/config ./internal/cli
Prompt Size
Small enough for one implementation prompt.
Stage 6: Add Narrow CLI Flag Helpers
Goal
Reduce typo-prone repeated Cobra flag wiring while keeping command files explicit and readable.
Decision
Add helper functions in internal/cli/flags.go. Helpers should take a
*cobra.Command and a target pointer. This avoids adding a direct import of
pflag unless it becomes clearly cleaner.
Recommended helpers:
addOutputFileFlag(cmd *cobra.Command, target *string)addReportFileFlag(cmd *cobra.Command, target *string)addOutputModulesFlag(cmd *cobra.Command, target *string)addMergeOutputSchemaFlag(cmd *cobra.Command, target *string)addNormalizeOutputSchemaFlag(cmd *cobra.Command, target *string)addTrimOutputSchemaFlag(cmd *cobra.Command, target *string)
Do not create one generic output schema helper because trim has different omitted-flag semantics and help text.
For --input-file, prefer keeping command-local definitions unless a helper
improves clarity:
- merge uses
StringArrayVar; - trim and normalize use
StringVar; - usage text differs.
Files To Inspect
internal/cli/merge.gointernal/cli/trim.gointernal/cli/normalize.gointernal/cli/*_test.godocs/cli.md, only to verify flag text if behavior or help text changes
Implementation Steps
- Add
internal/cli/flags.go. - Move only identical or intentionally paired flag definitions into helpers.
- Keep command-specific flags and semantic differences local.
- Run command help manually and compare the important flag names/defaults.
- Update tests only if they assert help text that intentionally changed.
Acceptance Criteria
- Public flag names and defaults are unchanged.
- Help text remains at least as accurate as before.
- Command files remain easy to read.
- No command factory or shared command runner is introduced.
Validation Commands
go test ./internal/cli
go run ./cmd/seriatim --help
go run ./cmd/seriatim merge --help
go run ./cmd/seriatim trim --help
go run ./cmd/seriatim normalize --help
Prompt Size
Small enough for one implementation prompt.
Stage 7: Centralize Common Segment Reference Formatting
Goal
Use one helper for the shared "best available segment reference" policy used by overlap and coalesce logic.
Decision
Add a small helper in internal/model, because the helper operates on
model.Segment and expresses domain reference semantics.
Recommended API:
func SegmentReference(segment Segment) string
The helper should:
- return
fmt.Sprintf("%s#%d", segment.Source, *segment.SourceSegmentIndex)whenSourceis non-empty andSourceSegmentIndexis non-nil; - otherwise return
segment.SourceRefwhen non-empty; - otherwise return
"".
Use this helper only where the existing code already has this exact fallback
policy. Keep generated references such as word-run:%d:%d:%d, coalesce:%d,
and resolve-danglers:%d local to the modules that create them.
Files To Inspect
internal/model/model.gointernal/overlap/detect.gointernal/coalesce/coalesce.gointernal/overlap/*_test.gointernal/coalesce/*_test.gointernal/cli/merge_test.go
Implementation Steps
- Add
SegmentReferenceand tests ininternal/model. - Replace duplicated fallback logic in overlap detection.
- Replace duplicated fallback logic in coalesce.
- Do not change generated reference prefixes.
- Do not change sorting behavior or derived-from ordering.
Acceptance Criteria
- Existing provenance strings in full output remain unchanged.
source#indexformatting exists in one shared helper for matching semantics.- Module-specific generated references stay module-local.
Validation Commands
go test ./internal/model ./internal/overlap ./internal/coalesce ./internal/cli
Prompt Size
Small enough for one implementation prompt.
Stage 8: Reduce Repetition In Schema Semantic Validation
Goal
Use one private helper for cross-schema segment ID and timing invariants.
Decision
Keep this helper private to schema. Do not use generics unless the resulting
code is clearly simpler than a small projection type.
Recommended shape:
type segmentSemantics struct {
id int
start float64
end float64
}
func validateSegmentSemantics(segments []segmentSemantics) error
Preserve current error text:
segment %d has id %d; want %dsegment %d has end %.3f before start %.3f
Keep full-schema overlap group timing validation separate.
Files To Inspect
schema/output.goschema/output_test.go- callers in
internal/artifact,internal/trim, andinternal/normalize
Implementation Steps
- Add the private segment semantic helper.
- Adapt full, intermediate, and minimal validation functions to project their segment shapes into the helper.
- Keep full overlap group semantic validation in the full validation path.
- Add or preserve tests that cover invalid IDs and invalid timing for each public schema shape.
Acceptance Criteria
- Sequential ID and timing rules are implemented once.
- Public schema validation behavior and error wording remain stable.
- Full overlap group validation remains full-schema only.
Validation Commands
go test ./schema ./internal/artifact ./internal/trim ./internal/normalize ./internal/cli
Prompt Size
Small enough for one implementation prompt.
Stage 9: Test Helper Cleanup
Goal
Reduce repeated test setup after behavior-preserving refactors are complete.
Decision
Keep test helpers package-local. Do not add exported test utility packages.
Prioritize helpers that reduce noise without hiding public command behavior:
- config option builders in
internal/config/config_test.go; - command execution helpers in
internal/cliif duplication remains after trim orchestration and flag helper cleanup; - fixture builders for trim schema shapes if they stay repetitive.
Do not abstract command arguments so far that tests no longer show the public CLI contract being exercised.
Files To Inspect
internal/config/config_test.gointernal/cli/merge_test.gointernal/cli/trim_test.gointernal/cli/normalize_test.gointernal/trim/*_test.go
Implementation Steps
- Identify repeated setup that survived prior stages.
- Add package-local helper builders for config options with valid defaults.
- Consolidate duplicate read/write JSON helpers only inside the test package where they are used.
- Keep high-signal command arguments inline in CLI tests.
- Avoid golden-file rewrites or fixture churn unrelated to cleanup.
Acceptance Criteria
- Tests are shorter where setup was noisy.
- Public behavior being tested remains obvious.
- No production code changes are made in this stage unless a test-only cleanup reveals dead production code from earlier stages.
Validation Commands
go test ./internal/config ./internal/cli ./internal/trim
Prompt Size
One implementation prompt if limited to config/CLI/trim tests. Split by package if the diff becomes large.
Stage 10: Final Dead-Code And Documentation Sweep
Goal
Remove stale helpers and update internal documentation only where implemented cleanup changed current boundaries.
Decision
This is a cleanup verification stage, not a feature stage.
Do not delete samples/, move examples, redesign docs, or alter user-facing
references unless directly required by implemented cleanup.
Files To Inspect
- All files touched by Stages 1-9
docs/policy/architecture.mddocs/policy/development.mddocs/internal/artifacts.mddocs/internal/pipeline.mddocs/internal/modules.mddocs/cli.mddocs/config.md
Implementation Steps
- Search for obsolete helpers, duplicate schema constants, duplicate JSON writers, old trim CLI orchestration helpers, and unused imports.
- Run
go test ./.... - Run command help checks.
- Update internal docs only if package responsibility changed in a way current docs now describe inaccurately.
- Do not add planned cleanup notes outside
docs/roadmap/.
Acceptance Criteria
- No duplicate output schema string constants remain outside canonical aliases and intentional docs/test fixtures.
- No duplicate production
writeOutputJSONhelper remains. internal/cli/trim.gois a thin adapter.- Docs outside
docs/roadmap/describe only implemented behavior. - Full repository tests pass.
Validation Commands
go test ./...
go run ./cmd/seriatim --help
go run ./cmd/seriatim merge --help
go run ./cmd/seriatim trim --help
go run ./cmd/seriatim normalize --help
rg -n "SchemaMinimal|SchemaIntermediate|SchemaFull|writeOutputJSON|seriatim-minimal|seriatim-intermediate|seriatim-full" internal schema
The rg command is a review aid. Raw schema strings may still be appropriate in
canonical constants, docs, JSON Schema files, and tests that assert serialized
public contracts.
Prompt Size
Small enough for one implementation prompt after prior stages are complete.
Deferred Or Explicitly Avoided Work
Do not implement these as part of the pre-1.0 cleanup sequence unless a later audit identifies a concrete bug or larger duplication pattern:
- A generic workflow engine for merge, trim, and normalize.
- Dynamic plugins or runtime-extensible schemas.
- A manifest, checkpoint, resume, dry-run, force, or progress framework.
- Atomic output writes or fsync semantics.
- A broad filesystem adapter layer.
- A combined backchannel/filler classifier framework.
- A generic registry resolver that erases stage-specific error messages.
- A broad public API for internals.
- Destructive changes to
samples/.
Backchannel and filler share mechanics, but the cleanup decision is to defer shared category-tagging extraction before 1.0. The current duplication is small, domain-specific, and well isolated.
Full Cleanup Validation
After all implemented stages:
go test ./...
go run ./cmd/seriatim --help
go run ./cmd/seriatim merge --help
go run ./cmd/seriatim trim --help
go run ./cmd/seriatim normalize --help
Manual review checklist:
- Public CLI behavior is unchanged.
- Public output schemas and JSON shapes are unchanged.
- Report event ordering and text remain stable unless tests were deliberately updated.
- Trim, normalize, and merge boundaries match
docs/policy/architecture.md. - No planned or aspirational behavior was added to non-roadmap docs.
- Cleanup reduced duplicated policy without introducing broad abstractions.
Open Questions
No blocking questions are required to implement this plan.
If a future implementation stage discovers that preserving current behavior conflicts with one of the structural decisions above, stop that stage and ask for direction before changing public CLI behavior, public JSON contracts, or report semantics.