Files
distributor/docs/roadmap/latest_paths.md

213 lines
7.7 KiB
Markdown

# Roadmap: Latest Path Destinations
## Purpose
Support stable "latest" publication paths as normal fan-out destinations.
A latest destination republishes the current winning source bundle to a fixed
destination path such as `/weather/latest/`, while archive destinations preserve
source-relative bundle paths such as `/weather/daily/brentwood/2026-06-01/`.
This feature should be implemented before link generation so URL construction
can use final destination path semantics. It is operationally destructive and
benefits from index-mode HTML, although it must not require index mode or link
generation.
## Current Implementation Grounding
Current run behavior computes the destination bundle path as the source bundle
path relative to the source root. Publication then writes selected outputs and
`.distributor.json` below that destination bundle path.
Backend roots differ by backend:
- local and SSH/SFTP use configured `path` as the backend root;
- S3 uses configured bucket plus optional `prefix` as the backend root.
Destination comparison uses `.distributor.json` at the destination bundle path.
Replacement and skip decisions are based on the normalized source manifest
recorded in destination state. Unmanaged non-empty destinations fail unless
explicit force behavior is requested.
## Goals
- Add destination-local path mapping.
- Preserve source-relative destination paths as the default.
- Add fixed path mapping for latest-style destinations.
- For fixed destinations, select only the newest discovered source bundle for
publication.
- Make fixed mapping work for local, SSH/SFTP, and S3 backends.
- Continue using normal destination state comparison and replacement rules.
- Keep link generation and index-mode HTML complementary, not required.
## Non-Goals
- Do not add symlink-based latest behavior.
- Do not add feed or index generation.
- Do not change source manifest schema.
- Do not make producers responsible for latest publication.
- Do not add domain-specific latest selection rules.
## Configuration Shape
Add destination-level path mapping:
```yaml
path_mapping:
mode: preserve_relative
```
Modes:
- `preserve_relative`: existing/default behavior.
- `fixed`: publish selected outputs directly at the destination backend root.
Example fixed destination:
```yaml
destinations:
- id: latest-html
backend: ssh
host: web.example.com
user: deploy
path: /srv/www/weather/latest
path_mapping:
mode: fixed
publish:
source: false
html: true
```
For S3, the fixed destination root is the configured bucket plus `prefix`.
## Path Mapping Semantics
`preserve_relative`:
```text
destination bundle path = source-root-relative bundle path
```
`fixed`:
```text
destination bundle path = ""
```
That empty logical destination bundle path means the destination backend root.
Storage writes still use normal backend-rooted logical paths for outputs and
state.
## Newest Bundle Selection
`preserve_relative` destinations should continue publishing every selected
source bundle independently.
`fixed` destinations should publish only one bundle per run: the newest
discovered source bundle selected for that destination. Newest selection should
be deterministic:
1. choose the bundle with the greatest source manifest `created` timestamp;
2. if multiple bundles have the same greatest `created` timestamp, use the
source-root-relative bundle path ascending as a tie-breaker.
The selected bundle then uses the existing destination comparison, skip,
replacement, cleanup, and force rules. Older discovered bundles should not be
planned or written to that fixed destination in the same run.
Dry-run output should identify the number of fixed-destination candidates and
the selected source bundle so operators can see which bundle would become
latest.
## Safety Rules
Fixed path mapping is more destructive than archive-style publication because
newer bundles replace prior contents at the same destination path.
Required behavior:
- dry-run output must identify fixed path mapping and planned replacement;
- dry-run output must include an extra fixed-path warning or summary count when
destructive replacement is possible;
- replacement should delete only managed outputs recorded in valid destination
state when possible;
- unmanaged non-empty fixed destinations must fail unless force is explicitly
requested;
- `--force` may be used when fixed mapping targets the backend root, but force
replacement must remain bounded to the configured destination backend root and
must never delete above it;
- fixed mapping to the backend root requires especially clear dry-run reporting.
## Relationship To Other Roadmaps
HTML index mode is useful for fixed web destinations because `index.html`
supports stable directory-style URLs, but sidecar HTML and source-only outputs
should still work.
Link generation should use fixed path semantics so latest URLs are based on the
fixed destination root, not the original source-relative archive path.
Producer manifest creation and remote validation are independent of destination
path mapping.
## Implementation Stages
1. Add destination-level `path_mapping` config validation with
`preserve_relative` as the default and `fixed` as the new mode.
2. Refactor run planning so destination bundle path mapping is destination-local
and can be evaluated before writes.
3. Add fixed-destination newest selection so each fixed destination receives
only the newest discovered source bundle.
4. Integrate fixed mapping with destination comparison, managed deletion, force
replacement, and dry-run reporting.
5. Update local, SSH, and S3 tests to verify fixed root behavior under each
backend root model.
6. Update current-behavior documentation after implementation.
## Testing Expectations
Suggested coverage:
- omitted `path_mapping` preserves existing behavior;
- `preserve_relative` explicitly matches existing behavior;
- `fixed` publishes outputs and `.distributor.json` at the backend root;
- fixed destinations select only the newest discovered bundle;
- fixed destination tie-break behavior is deterministic;
- older discovered bundles are not planned or written to fixed destinations;
- newer source replaces older managed fixed destination state;
- older source skips newer fixed destination state;
- unmanaged non-empty fixed destination fails without force;
- dry-run reports fixed path mapping, candidate count, selected bundle, and
destructive replacement warnings clearly;
- `--force` remains bounded when fixed mapping targets the backend root;
- S3 fixed mapping respects bucket plus prefix as backend root.
## Documentation Updates After Implementation
- Update `docs/config.md` with `path_mapping`.
- Update `docs/operations.md` with archive-plus-latest fan-out examples.
- Update `docs/cli.md` if dry-run output gains fixed-path indicators.
- Add an environment-safe example only after behavior is implemented.
Keep this roadmap under `docs/roadmap/` until implemented.
## Decisions
- `--force` is allowed when fixed mapping targets the backend root, but dry-run
and run output must make the fixed-root replacement explicit and force remains
bounded to the configured backend root.
- Fixed destinations publish only the newest discovered bundle, rather than
processing every discovered bundle and letting later writes replace earlier
writes.
- Dry-run includes an extra warning or summary count for fixed-path destructive
replacements.
## Future Work
- Add richer source selection policies if deployments need something other than
newest-by-`created` for fixed destinations.
- Consider stricter ambiguity handling for equal latest timestamps if real
producer workflows make timestamp ties common.
- Add higher-level status or approval workflows for fixed-root replacements if
dry-run output is not enough operational protection.