Make command line references pipeline scoped
This commit is contained in:
258
docs/roadmap/cli-pipeline-references.md
Normal file
258
docs/roadmap/cli-pipeline-references.md
Normal file
@@ -0,0 +1,258 @@
|
||||
# Pipeline-Scoped CLI References
|
||||
|
||||
## Purpose
|
||||
|
||||
Make command-line reference binding match the pipeline-level mental model used
|
||||
by configuration and by subprocess callers. A caller should be able to supply
|
||||
each shared external reference once, while retaining explicit syntax for the
|
||||
less common case in which one lane or one stage binding needs a different
|
||||
source.
|
||||
|
||||
## Motivation And Current Problem
|
||||
|
||||
Notarius already accepts repeatable `--reference selector=path` and
|
||||
`--without-reference selector` flags. The current unqualified form,
|
||||
`--reference slot=path`, succeeds only when exactly one selected chunk, extract,
|
||||
merge, or normalize target declares that slot. If several targets consume a
|
||||
shared reference such as `party`, `glossary`, or `spell_catalog`, the command is
|
||||
rejected as ambiguous and the caller must repeat stage-qualified bindings.
|
||||
|
||||
That behavior is internally precise but does not match the public configuration
|
||||
model. A reference declared at pipeline scope is shared with every compatible
|
||||
target, while target-local configuration provides the exceptional override.
|
||||
It is therefore surprising for the least-qualified CLI syntax to mean "find one
|
||||
unique target" rather than "supply this reference to the pipeline."
|
||||
|
||||
The mismatch is especially costly for subprocess use. The primary complete D&D
|
||||
workflow expects an orchestrator to provide one transcript, one output root,
|
||||
and a small collection of campaign reference files. Requiring the orchestrator
|
||||
to know and enumerate every internal consumer of those shared files couples it
|
||||
to lane composition, makes commands needlessly long, and creates maintenance
|
||||
work whenever another compatible D&D module is added.
|
||||
|
||||
## Target CLI Contract
|
||||
|
||||
`--reference` remains repeatable, but selector qualification expresses scope:
|
||||
|
||||
| Form | Target scope |
|
||||
| --- | --- |
|
||||
| `slot=path` | Every selected pipeline target that declares `slot`. |
|
||||
| `chunk.slot=path` | The selected chunk binding. |
|
||||
| `lane.slot=path` | Every extract, merge, or normalize binding in `lane` that declares `slot`. |
|
||||
| `lane.extract.slot=path` | The extract binding in `lane`. |
|
||||
| `lane.merge.slot=path` | The merge binding in `lane`. |
|
||||
| `lane.normalize.slot=path` | The normalize binding in `lane`. |
|
||||
|
||||
The current stage-wide `merge.slot=path` shorthand is removed. A merge-specific
|
||||
override must name its lane as `lane.merge.slot=path`; this keeps the grammar
|
||||
hierarchical and avoids another uniqueness-dependent selector.
|
||||
|
||||
`--without-reference` uses the same selector forms without `=path`. It removes
|
||||
matching external bindings only. It never removes a generated artifact handoff,
|
||||
and resolution continues to reject a missing required reference.
|
||||
|
||||
Broad selectors are expected to match multiple compatible targets. They fail
|
||||
when they match no selected target, when the named lane is not selected, or
|
||||
when a stage-qualified target does not declare the slot. Errors should identify
|
||||
the selector and relevant scope without requiring callers to understand private
|
||||
resolver structures.
|
||||
|
||||
## Precedence And Conflict Policy
|
||||
|
||||
Command-line references are operational overrides and take precedence over
|
||||
external file references supplied at pipeline, step, lane, or binding scope in
|
||||
configuration. Among CLI selectors that affect the same concrete target:
|
||||
|
||||
1. an exact `lane.stage.slot` or `chunk.slot` selector wins over a lane-scoped
|
||||
selector;
|
||||
2. a lane-scoped selector wins over a pipeline-scoped selector; and
|
||||
3. the last occurrence wins among selectors with equal scope and action.
|
||||
|
||||
This ordering lets an orchestrator provide shared defaults once and express
|
||||
only genuine exceptions:
|
||||
|
||||
```sh
|
||||
--reference party=/refs/party.txt \
|
||||
--reference npc-registry.party=/refs/npc-party-context.txt
|
||||
```
|
||||
|
||||
Binding and unbinding the same concrete target at the same specificity is a
|
||||
configuration error rather than an argument-order-dependent result. A
|
||||
more-specific unbind may carve an exception out of a broader binding, and a
|
||||
more-specific binding may restore an exception to a broader unbind.
|
||||
|
||||
Generated artifact references remain a distinct source form. A CLI file
|
||||
reference must not silently replace, remove, or coexist with a generated
|
||||
handoff for the same concrete target and slot. Resolution fails with a
|
||||
target-specific conflict and directs the caller to narrow or remove the CLI
|
||||
selector. Same-run generated registry and eligibility handoffs in the complete
|
||||
D&D pipeline therefore remain controlled by pipeline composition.
|
||||
|
||||
CLI reference paths continue to resolve relative to the process working
|
||||
directory and retain CLI provenance. Subprocess guidance must recommend
|
||||
absolute paths. The reference source is auxiliary context and does not affect
|
||||
the generated prompt session identifier.
|
||||
|
||||
## Subprocess Target State
|
||||
|
||||
Passing shared references is a first-class part of the documented subprocess
|
||||
workflow. The complete D&D invocation should have this shape:
|
||||
|
||||
```sh
|
||||
notarius run dnd-session \
|
||||
--config /absolute/path/to/notarius.yml \
|
||||
--input /absolute/path/to/transcripts/final.trimmed.json \
|
||||
--output-dir /absolute/path/to/notarius-output \
|
||||
--reference party=/absolute/path/to/references/party.txt \
|
||||
--reference players=/absolute/path/to/references/players.txt \
|
||||
--reference glossary=/absolute/path/to/references/glossary.txt \
|
||||
--reference spell_catalog=/absolute/path/to/references/spells.json \
|
||||
--json
|
||||
```
|
||||
|
||||
Only references actually available to and desired by the deployment need to be
|
||||
passed. The selected modules continue to determine accepted slot names, media
|
||||
types, requiredness, and size limits. The orchestrator passes references as
|
||||
separate argument-vector elements rather than building a shell command, captures
|
||||
standard output and standard error separately, checks exit status, and uses the
|
||||
JSON receipt's `output_directory` to locate the generated run bundle.
|
||||
|
||||
The documentation must clearly distinguish:
|
||||
|
||||
- the input file supplied by `--input`;
|
||||
- the output *root* supplied by `--output-dir` and the run-specific output path
|
||||
returned in the receipt;
|
||||
- external file references supplied by `--reference`; and
|
||||
- generated references produced and consumed within the configured ordered
|
||||
pipeline.
|
||||
|
||||
## Required Implementation Changes
|
||||
|
||||
### CLI selector model and parsing
|
||||
|
||||
- Replace the unique-target interpretation of unqualified and lane-qualified
|
||||
selectors with explicit pipeline, lane, chunk, and exact-binding scopes.
|
||||
- Remove parsing and help text for the stage-wide `merge.slot` form.
|
||||
- Preserve repeatable flag handling, non-empty selector/path checks, supported
|
||||
stage names, and syntax-error exit classification.
|
||||
- Represent selector scope explicitly enough that precedence and diagnostics do
|
||||
not depend on inferring intent from empty fields.
|
||||
|
||||
### Target expansion and precedence
|
||||
|
||||
- Refactor CLI reference resolution to return every compatible selected target
|
||||
for a broad selector rather than requiring uniqueness.
|
||||
- Resolve overlapping bind and unbind requests into one deterministic action
|
||||
per concrete target and slot using the precedence policy above.
|
||||
- Produce exact `pipeline.ReferenceBinding` and `pipeline.ReferenceUnbind`
|
||||
values after CLI scoping is resolved. Keep broad-selector policy at the CLI
|
||||
boundary rather than adding CLI grammar or D&D knowledge to the generic
|
||||
pipeline framework.
|
||||
- Preserve CLI provenance and the existing rule that command-line paths are
|
||||
materialized relative to the working directory.
|
||||
- Detect generated-reference conflicts before file access or stage execution
|
||||
and retain required-slot validation after effective bindings are known.
|
||||
- Ensure lane selection is honored for compact pipelines. Explicit ordered
|
||||
pipelines continue to use their complete configured lane set because they do
|
||||
not support `--only`.
|
||||
|
||||
### Public and internal documentation
|
||||
|
||||
- Rewrite the reference-selector section of `docs/cli.md`, which is the
|
||||
canonical owner of flag syntax, selector semantics, precedence, and command
|
||||
errors.
|
||||
- Update `docs/consumers/subprocess.md` so reference-bearing invocation is part
|
||||
of the primary subprocess workflow rather than an unillustrated aside.
|
||||
- Update `docs/consumers/dnd-pipeline.md` with the complete D&D subprocess
|
||||
invocation and explain which references are external versus generated.
|
||||
- Lightly update `docs/config.md` to link the configuration hierarchy to the
|
||||
CLI override contract without duplicating CLI syntax.
|
||||
- Update `docs/internal/cli.md` and, only where necessary, the reference
|
||||
resolution discussion in `docs/internal/pipeline.md` to describe the
|
||||
implemented expansion and precedence boundary.
|
||||
- Do not expand the README quickstart or duplicate complete configuration
|
||||
files in prose. The maintained example configuration remains the canonical
|
||||
copyable pipeline definition.
|
||||
|
||||
No ADR is required. This is a deliberate public CLI usability correction that
|
||||
fits the existing architectural decisions: configuration remains centralized,
|
||||
operational overrides remain explicit, generated handoffs remain part of
|
||||
pipeline composition, and broad CLI syntax is translated into existing exact
|
||||
framework bindings at the application boundary. The CLI and configuration
|
||||
references are the durable owners of the resulting current behavior once it is
|
||||
implemented.
|
||||
|
||||
## Testing And Validation
|
||||
|
||||
Add lean offline behavioral coverage at the CLI contract boundary for:
|
||||
|
||||
- one pipeline-scoped reference reaching multiple compatible chunk and lane
|
||||
targets;
|
||||
- one lane-scoped reference reaching both extract and normalize bindings;
|
||||
- exact-binding and lane-scoped exceptions overriding broader values;
|
||||
- final-occurrence behavior at equal specificity;
|
||||
- broad selectors with no matches and selectors naming unselected lanes or
|
||||
undeclared slots;
|
||||
- bind/unbind specificity and same-specificity conflicts;
|
||||
- rejection of external/generated conflicts and protection of required
|
||||
generated handoffs;
|
||||
- working-directory resolution and CLI provenance for expanded bindings; and
|
||||
- removal of the `merge.slot` shorthand.
|
||||
|
||||
Prefer package-level resolution and representative `RunWithOptions` tests over
|
||||
duplicating the full selector matrix through end-to-end fixtures. Existing
|
||||
tests that encode unique-target ambiguity should be rewritten or removed rather
|
||||
than retained as historical change detectors. Do not assert complete error
|
||||
strings when stable classification and a concise semantic fragment provide
|
||||
sufficient confidence.
|
||||
|
||||
Validation for the completed change should include:
|
||||
|
||||
```sh
|
||||
go test ./internal/cli ./internal/core/config ./internal/framework/pipeline
|
||||
go test ./...
|
||||
go vet ./...
|
||||
go build ./cmd/notarius
|
||||
```
|
||||
|
||||
Also validate both maintained example configurations against their selected
|
||||
pipelines, verify every changed documentation link, and manually review the
|
||||
D&D subprocess command against the actual flag parser and complete example
|
||||
configuration.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Passing reference contents directly in command-line arguments or environment
|
||||
variables.
|
||||
- Adding a reference-manifest file, glob syntax, wildcard selectors, or a
|
||||
second pipeline-reference flag.
|
||||
- Allowing the CLI to define undeclared reference slots or bypass module media
|
||||
type, size, UTF-8, requiredness, or materialization validation.
|
||||
- Replacing generated same-run artifacts with external files or changing
|
||||
ordered pipeline dependencies.
|
||||
- Changing configuration-file reference path resolution, prompt-session
|
||||
derivation, output directory allocation, or the run-result receipt.
|
||||
- Adding D&D-specific selector behavior to generic CLI or pipeline packages.
|
||||
- Preserving uniqueness-dependent `slot` or `lane.slot` behavior as an alias.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- A subprocess caller can supply `party`, `players`, `glossary`, and
|
||||
`spell_catalog` once each and have every compatible selected D&D target
|
||||
receive the corresponding external file.
|
||||
- `slot=path` has pipeline scope and never fails merely because several selected
|
||||
targets declare the slot.
|
||||
- Lane and exact-binding selectors provide deterministic, documented
|
||||
exceptions with specificity-based precedence.
|
||||
- A broad selector that matches nothing is rejected before source parsing or
|
||||
LLM work.
|
||||
- CLI external references override configured external paths but never silently
|
||||
replace or remove generated artifact references.
|
||||
- All effective CLI bindings retain correct provenance and absolute-path
|
||||
materialization behavior.
|
||||
- `docs/cli.md` owns the exact public syntax, and both subprocess guides present
|
||||
shared CLI references as the primary orchestration workflow.
|
||||
- Current behavior outside `docs/roadmap/` is not documented until the code
|
||||
lands, and no complete example is duplicated outside `examples/`.
|
||||
- Focused tests, repository-wide tests, vetting, build, maintained configuration
|
||||
validation, and documentation-link review pass.
|
||||
Reference in New Issue
Block a user