14 KiB
Catalog State And Workflow Implementation Roadmap
This is the active staged implementation plan for
docs/roadmap/catalog.md. The feature roadmap defines the target state model
and policy decisions; this document defines the implementation sequence for an
LLM coding agent to follow stage by stage.
Future behavior must remain under docs/roadmap/ until implemented. Update
current-behavior docs only in the stage that implements the corresponding
behavior.
Current Baseline
distributor currently writes destination .distributor.json using
single-owner schema version 2 or shared-root schema version 3. Destination
behavior is selected through several low-level knobs: state.mode,
reconciliation.mode, takeover.mode, and transfer.
The target behavior is a clean alpha break:
- all newly written destination state uses schema version
4; - all destinations use
state.mode: cataloginternally; - user-facing destination behavior is selected by
workflow: additiveorworkflow: replacement; workflowdefaults toadditive;- legacy destination policy fields are rejected, not aliased;
- legacy state schema versions
1,2, and3are not migrated.
Implementation Principles
- Keep the source manifest contract unchanged.
- Keep backend adapters unaware of catalog and workflow policy.
- Treat
workflowas runtime config, not persisted state. - Keep unmanaged content protected after catalog state exists.
- Preserve output
created_atwhen a path changes owner; update onlyupdated_at. - Prefer removing legacy state/config paths over compatibility shims.
- Preserve current public APIs outside destination state/config unless the catalog roadmap explicitly changes them.
Active Implementation Stages
Stage 1: Destination Workflow Config Clean Break
Goal: replace low-level destination policy config with the user-facing workflow switch.
Implementation scope:
- Add destination
workflowconfig with accepted valuesadditiveandreplacement. - Default omitted
workflowtoadditive. - Remove or make unsupported the destination-level config fields
state,reconciliation,takeover, andtransfer. - Ensure configs containing those legacy fields fail clearly. Prefer strict YAML unknown-field failure by removing struct fields; add explicit validation only if clearer errors are needed without weakening strict decoding.
- Preserve backend, publish, transform, path mapping, links, retention, and other non-policy destination fields.
- Update example configs only when the implementation stage also updates current docs; otherwise keep this stage focused on config code and tests.
Tests:
go test ./internal/config- Omitted workflow defaults to
additive. workflow: additiveandworkflow: replacementvalidate.- Unknown workflow values fail.
- Legacy
state,reconciliation,takeover, andtransferfields fail. - Existing valid examples are updated or tests are adjusted in the same change if examples currently use legacy fields.
Completion criteria:
- New configs express destination replacement/additive intent with one field.
- No normal config path accepts legacy destination policy knobs.
Stage 2: Catalog State Schema Version 4
Goal: implement the schema version 4 catalog state model in internal/state.
Implementation scope:
- Add catalog state types for top-level schema version
4,state.mode: catalog, andoutputs. - Add compact per-output source identity with
id,digest, andcreated. - Add catalog output records with required
path,pipeline_id,destination_id,source,kind,sha256,size,created_at, andupdated_at. - Add optional
source_path,transform, andurl, present only where allowed by the catalog roadmap. - Validate duplicate paths, invalid owner ids, invalid source identities, invalid output paths, invalid digests, negative sizes, invalid timestamps, invalid kind/transform combinations, and invalid URLs.
- Marshal catalog state deterministically with the existing JSON formatting conventions.
- Update
ParseDocumentso schema version4returns catalog state. - Treat schema versions lower than
4as superseded legacy state for publish planning, not as readable/migrated active state. Keep enough detection to identify legacy state and avoid treating it as arbitrary invalid JSON. - Reject unsupported future schema versions.
Tests:
go test ./internal/state- Parse/validate/marshal valid catalog state.
- Reject malformed catalog state and invalid output records.
- Detect schema versions
1,2, and3as superseded legacy state. - Reject future schema versions.
- Prove no top-level
owners,sources, workflow, source manifest, pipeline id, destination id, or published timestamp is accepted for catalog state.
Completion criteria:
- State package has one canonical schema version
4catalog contract for new writes. - Legacy state is detected but not migrated.
Stage 3: Catalog Publish Planning
Goal: plan publish actions against catalog state and destination workflow.
Implementation scope:
- Replace publish request inputs that consume state/reconciliation/takeover and transfer policy with destination workflow.
- Map workflow to catalog actions:
workflow: additive: upsert planned outputs and retain all other managed catalog outputs.workflow: replacement: upsert planned outputs and delete catalog outputs owned by the current pipeline/destination that are omitted from the plan.
- Planned paths that already exist as catalog-managed outputs may be overwritten and become owned by the current pipeline/destination.
- Planned path collisions with storage content not recorded in catalog state fail as unmanaged content.
- Matching planned outputs may skip writes when source identity and output digest already match, if that optimization can be implemented without changing externally visible results; otherwise writing idempotently is acceptable.
- Existing schema
< 4state is superseded:- replacement workflow may plan a bounded destination-root clear before writing planned outputs;
- additive workflow may plan overwrites for planned paths only and leave unplanned files unmanaged.
- Invalid JSON or future schema state remains a conflict, not a superseded legacy state.
- Preserve path mapping, publish policy, transforms, links, fixed-path selection, and output collision checks.
Tests:
go test ./internal/publish- Additive workflow publishes new catalog state.
- Additive workflow overwrites an existing managed output and retains unrelated outputs.
- Replacement workflow removes omitted outputs owned by the current pipeline/destination.
- Replacement workflow preserves unrelated catalog outputs owned by other pipeline/destination pairs.
- Managed output ownership moves to the current pipeline/destination when a planned path is overwritten.
created_atis preserved when an existing path changes owner;updated_atchanges.- Unmanaged path collisions fail.
- Legacy schema
< 4state follows the superseded-state rules above. - Invalid/future state fails.
Completion criteria:
- Publish planning no longer depends on single-owner/shared-root comparison semantics.
- Workflow behavior is fully driven by
workflow.
Stage 4: Catalog Publish Execution
Goal: execute catalog publish plans and write schema version 4 state.
Implementation scope:
- Write only schema version
4catalog.distributor.json. - Implement additive execution as managed upsert of planned outputs plus catalog state update.
- Implement replacement execution as managed upsert plus deletion of omitted current-owner catalog outputs.
- For replacement over superseded legacy state, clear the bounded destination root before writing planned outputs and catalog state.
- For additive over superseded legacy state, overwrite planned paths and write a catalog containing only planned outputs; leave unplanned files unmanaged.
- Preserve cleanup behavior on failed writes:
- additive cleanup removes outputs newly created by the failed attempt where practical;
- replacement cleanup follows existing managed-replacement safety where practical;
- state is written only after selected outputs are written.
- Preserve link metadata, generated output digests, source output digests, content type behavior, and backend-safe writes.
Tests:
go test ./internal/publish ./internal/app- Additive execution writes planned outputs, overwrites managed planned paths, retains unrelated managed outputs, and writes catalog state.
- Replacement execution deletes omitted current-owner outputs and preserves unrelated owner outputs.
- Superseded legacy replacement clears bounded destination root only.
- Superseded legacy additive leaves unplanned files on disk but out of catalog state.
- Failed writes do not leave misleading catalog state.
- Local, fake-backed SSH, and fake-backed S3 app paths exercise the same publish behavior.
Completion criteria:
- Successful
runwrites only v4 catalog state. - Destination outputs match additive/replacement workflow semantics.
Stage 5: Run Reporting, Notifications, And CLI Surface
Goal: update user-visible run behavior to describe workflow/catalog actions instead of legacy replacement/takeover actions.
Implementation scope:
- Replace legacy action labels tied to state/reconciliation/takeover/transfer
with these stable workflow-oriented labels:
publish_newupsert_additivereplace_catalogskip_sameforce_replacefail_unmanagedfail_conflict
- Ensure text and JSON run summaries include workflow-relevant counters.
- Include
workflowin run action records where useful. - Update fixed-path dry-run warnings to describe additive upsert or replacement clearly.
- Ensure notifications use the new action labels.
- Remove reporting assumptions that depend on
replace_older,replace_newer,replace_conflict, orreplace_takeover.
Tests:
go test ./internal/app ./internal/cli- Text dry-run output distinguishes additive from replacement workflow.
- JSON output includes workflow and stable action labels.
- Summary counters are deterministic.
- Notifications fire for additive and replacement writes.
- Existing CLI commands still parse and execute with the new config shape.
Completion criteria:
- Operators can understand from dry-run output whether a destination will upsert or replace managed catalog outputs.
Stage 6: Catalog Prune And Reconcile-State
Goal: update maintenance commands to operate on catalog state with the agreed selector model.
Implementation scope:
- Prune:
- selected
--pipelineand--destinationprune only outputs currently owned by that pipeline/destination; - no
--all-ownersprune mode in the initial catalog implementation; - do not add
--source-id,--path-prefix, or--kindselectors.
- selected
- Reconcile-state:
- selected
--pipelineand--destinationrepair only outputs currently owned by that pipeline/destination; --all-ownersrepairs all catalog outputs;- unmanaged reporting compares storage entries to all catalog output paths, not just selected owner paths.
- selected
- Rewrite repaired/pruned state as schema version
4. - Remove legacy single-owner/shared-root maintenance branches.
Tests:
go test ./internal/state ./internal/app ./internal/cli- Prune selects only current-owner catalog outputs.
- Prune deletes selected outputs and removes their catalog records.
- Reconcile-state selected owner removes only missing outputs for that owner.
reconcile-state --all-ownersremoves missing outputs for all owners.- Unmanaged reporting excludes all catalog-managed paths and reports unrecorded storage entries.
- JSON/text output remains stable and clear.
Completion criteria:
- Maintenance commands operate only on catalog state and respect the locked selector rules.
Stage 7: Clean Break Removal And Documentation
Goal: remove legacy state/config behavior and document the implemented catalog workflow model.
Implementation scope:
- Remove dead code for writing schema version
2single-owner and schema version3shared-root state. - Remove legacy config structs/constants/defaults/validation for destination
state,reconciliation,takeover, andtransferwhere no longer used. - Remove or rewrite tests that only assert legacy state/config behavior.
- Update current-behavior docs:
docs/config.mddocs/cli.mddocs/operations.mddocs/troubleshooting.mddocs/integrations/destination-state.md- relevant
docs/internal/docs docs/policy/architecture.mddocs/policy/development.md
- Update examples to use
workflowand remove legacy fields. - Keep roadmap-only material out of current docs.
- Once implemented and documented, remove or rewrite
docs/roadmap/catalog.mdso completed behavior is not described only as future work.
Tests and checks:
go test ./...rg -n "state:|reconciliation:|takeover:|transfer:" examples docs --glob '!docs/roadmap/**'rg -n 'single_owner|shared_root|schema version2|schema version3' docs internalrg -n "workflow: additive|workflow: replacement|schema_version.*4" docs examples
Completion criteria:
- Current docs and examples describe the catalog workflow model.
- Legacy destination policy fields and legacy write paths are gone.
- Full test suite passes.
Refactors To Avoid
- Do not add top-level
ownersorsourcescatalogs. - Do not persist workflow in
.distributor.json. - Do not keep deprecated config aliases for legacy destination policy fields.
- Do not implement state migration from schema versions
1,2, or3. - Do not add catalog-specific prune/reconcile selectors beyond the agreed initial ownership scope.
- Do not move catalog policy into storage adapters.
Open Questions
No open questions are known. The catalog roadmap locks the state shape, workflow
values, default workflow, clean-break policy, legacy-state behavior, output
field selection, created_at preservation, and maintenance command scope.