232 lines
8.0 KiB
Markdown
232 lines
8.0 KiB
Markdown
# Roadmap: Campaign Registry
|
|
|
|
Status: Implemented
|
|
|
|
## Problem
|
|
|
|
Narratio currently treats campaign configuration as one selected
|
|
`campaign.yml` file:
|
|
|
|
- command flags use `--campaign <path>`;
|
|
- default discovery searches fixed system file locations;
|
|
- `campaign.yml` uses `campaign:` as the identity field.
|
|
|
|
That model works for a single campaign, but it is awkward for installations
|
|
that manage multiple campaigns. Operators need to pass file paths or maintain a
|
|
single global campaign config, while the newer session-oriented CLI already
|
|
uses concise positional session IDs and remote session lookup.
|
|
|
|
The campaign selection model should become ID-based and pipeline-owned.
|
|
Pipeline config should describe where campaigns live, commands should select a
|
|
campaign by ID, and each campaign directory should contain its stable campaign
|
|
materials.
|
|
|
|
## Target Model
|
|
|
|
`pipeline.yml` owns the campaign registry:
|
|
|
|
campaigns:
|
|
root: /usr/local/share/narratio/campaigns
|
|
default_campaign_id: dilfs
|
|
|
|
Campaign files live at the conventional path:
|
|
|
|
{campaigns.root}/{campaign_id}/campaign.yml
|
|
|
|
The first implementation should use only the conventional path. Recursive
|
|
discovery of every `campaign.yml` under `campaigns.root` is deferred to a
|
|
future stage.
|
|
|
|
Each campaign file uses `campaign_id` as the canonical identity field:
|
|
|
|
campaign_id: dilfs
|
|
session_template_file: ./session.template.yml
|
|
inputs:
|
|
speakers_file: ./speakers.yml
|
|
autocorrect_file: ./autocorrect.yml
|
|
glossary_file: ./glossary.yml
|
|
|
|
Campaign-relative files continue to resolve relative to the selected
|
|
`campaign.yml`, including stable input files and `session_template_file`.
|
|
|
|
The public CLI changes from path-based campaign selection to ID-based campaign
|
|
selection:
|
|
|
|
- `--campaign <id>` selects a campaign ID.
|
|
- `--campaign-file <path>` explicitly loads one campaign file for
|
|
development, tests, and unusual local workflows.
|
|
- `--campaign` and `--campaign-file` are mutually exclusive.
|
|
|
|
If neither `--campaign` nor `--campaign-file` is passed, Narratio uses
|
|
`pipeline.campaigns.default_campaign_id`. If no campaign can be selected,
|
|
commands fail clearly before session loading or stage execution.
|
|
|
|
Resolved campaign ID remains the campaign segment used for:
|
|
|
|
- workspace paths;
|
|
- spool paths;
|
|
- S3 session prefixes;
|
|
- remote `session.yml` lookup;
|
|
- archive locks and promoted output keys;
|
|
- session/campaign mismatch validation;
|
|
- status, plan, restore, and helper output.
|
|
|
|
## Compatibility Policy
|
|
|
|
This is a breaking public/config contract change.
|
|
|
|
After the cutover:
|
|
|
|
- `--campaign` no longer accepts a filesystem path;
|
|
- default fixed campaign file discovery is removed;
|
|
- `campaign:` is no longer accepted in `campaign.yml`;
|
|
- `campaign_id:` is required.
|
|
|
|
Keep `--campaign-file` as the only explicit file override. Do not retain hidden
|
|
aliases for the old `--campaign <path>` behavior.
|
|
|
|
## Implementation Stages
|
|
|
|
### Stage 1: Add Campaign Registry Selection
|
|
|
|
Status: Implemented
|
|
|
|
Add the registry model and switch command loading to resolve campaigns through
|
|
pipeline config.
|
|
|
|
Implementation requirements:
|
|
|
|
- Add `pipeline.campaigns.root`.
|
|
- Add `pipeline.campaigns.default_campaign_id`.
|
|
- Add `campaign_id` to campaign config and make it the canonical identity.
|
|
- Resolve pipeline config first, then campaign selection.
|
|
- Use this selection order:
|
|
1. explicit `--campaign-file <path>`;
|
|
2. explicit `--campaign <id>`;
|
|
3. `pipeline.campaigns.default_campaign_id`;
|
|
4. fail clearly.
|
|
- For ID selection, load `{campaigns.root}/{campaign_id}/campaign.yml`.
|
|
- Validate that the loaded `campaign_id` matches the selected ID.
|
|
- Reject `--campaign` with `--campaign-file`.
|
|
- Preserve strict YAML decoding.
|
|
- Preserve campaign-relative stable input and session template resolution.
|
|
- Keep storage details behind the existing storage adapter and object-store
|
|
helper.
|
|
- Keep remote session lookup and archive key construction based on the
|
|
resolved campaign ID.
|
|
|
|
Acceptance criteria:
|
|
|
|
- Commands can run with only a pipeline config and the pipeline default
|
|
campaign ID.
|
|
- Commands can select another campaign with `--campaign <id>`.
|
|
- Commands can load a specific file with `--campaign-file <path>`.
|
|
- Existing session loading, remote session fallback, prepare materialization,
|
|
restore, archive, locks, clean, analyze, and publish behavior continue to use
|
|
the same resolved campaign identity.
|
|
- No generic config registry framework is introduced.
|
|
|
|
### Stage 2: Remove Old Single-File Campaign Behavior
|
|
|
|
Status: Implemented
|
|
|
|
Remove the old public campaign file model after registry selection is in
|
|
place.
|
|
|
|
Implementation requirements:
|
|
|
|
- Remove fixed default campaign config discovery from command loading.
|
|
- Remove `DefaultCampaignConfigSearchPaths` and related path-only resolution if
|
|
no current tests or helpers still need them.
|
|
- Remove support for `campaign:` from `campaign.yml`.
|
|
- Update validation errors to refer to `campaign_id`.
|
|
- Update examples to use campaign directories and `campaign_id`.
|
|
- Update current-behavior docs to document:
|
|
- `pipeline.campaigns.root`;
|
|
- `pipeline.campaigns.default_campaign_id`;
|
|
- `campaign_id`;
|
|
- `--campaign <id>`;
|
|
- `--campaign-file <path>`.
|
|
- Update troubleshooting examples that currently pass `--campaign <path>`.
|
|
|
|
Acceptance criteria:
|
|
|
|
- `campaign.yml` files with `campaign:` fail strict decoding.
|
|
- `--campaign /path/to/campaign.yml` is treated as a campaign ID and fails
|
|
unless that ID exists under `campaigns.root`.
|
|
- `--campaign-file /path/to/campaign.yml` is the supported file override.
|
|
- User-facing docs no longer describe fixed campaign config discovery.
|
|
|
|
## Test Guidance
|
|
|
|
Focused tests:
|
|
|
|
- `go test ./internal/config -v`
|
|
- `go test ./internal/app -v`
|
|
- `go test ./internal/stage -run Prepare -v`
|
|
|
|
Full validation:
|
|
|
|
- `go test ./...`
|
|
|
|
Config tests to add or update:
|
|
|
|
- strict decode accepts `pipeline.campaigns.root`;
|
|
- strict decode accepts `pipeline.campaigns.default_campaign_id`;
|
|
- strict decode accepts `campaign_id`;
|
|
- selected campaign ID mismatch fails;
|
|
- missing campaign root fails when ID selection is needed;
|
|
- missing default campaign ID fails when no explicit campaign selector is
|
|
passed;
|
|
- old `campaign:` fails after Stage 2.
|
|
|
|
App tests to add or update:
|
|
|
|
- `--campaign <id>` resolves `{campaigns.root}/{id}/campaign.yml`;
|
|
- omitted `--campaign` uses `pipeline.campaigns.default_campaign_id`;
|
|
- `--campaign-file` loads an explicit campaign file;
|
|
- `--campaign` plus `--campaign-file` fails;
|
|
- remote session fallback uses the resolved campaign ID;
|
|
- `session init`, `run`, `run-stage`, `resume`, `analyze`, `publish`, `clean`,
|
|
and `session` subcommands all use the same campaign selection path;
|
|
- path-based `--campaign` examples and tests are removed after Stage 2.
|
|
|
|
## Documentation Guidance
|
|
|
|
Update current-behavior docs only after implementation lands:
|
|
|
|
- `docs/config.md`
|
|
- `docs/cli.md`
|
|
- `docs/operations.md`
|
|
- `docs/troubleshooting.md`
|
|
- relevant files under `docs/internal/`
|
|
- `examples/`
|
|
|
|
Planned campaign registry behavior belongs only in this roadmap until the code,
|
|
tests, examples, and current-behavior docs are updated.
|
|
|
|
## Architecture Guardrails
|
|
|
|
- Keep Narratio explicit and stage-driven.
|
|
- Do not introduce a generic configuration registry or workflow framework.
|
|
- Keep YAML decoding strict.
|
|
- Keep defaults centralized and testable.
|
|
- Keep campaign-relative path resolution centralized.
|
|
- Use centralized S3 and workspace path helpers.
|
|
- Keep storage details behind `storage.ObjectStore`.
|
|
- Keep secret-backed object-store construction in `internal/app`.
|
|
- Preserve manifest-driven resume and restore behavior.
|
|
- Do not store raw secrets in campaign configs, manifests, logs, generated
|
|
configs, or archive metadata.
|
|
|
|
## Assumptions
|
|
|
|
- The canonical pipeline schema is grouped under `campaigns`.
|
|
- The canonical campaign identity field is `campaign_id`.
|
|
- `--campaign` means campaign ID.
|
|
- `--campaign-file` is retained as an explicit override.
|
|
- Recursive discovery is planned but not part of the first implementation.
|
|
- Existing production configs can be migrated from `campaign:` to
|
|
`campaign_id:` and from `--campaign <path>` to `--campaign <id>` or
|
|
`--campaign-file <path>`.
|