Why run Exists and execute Stays Strict¶
This document explains a deliberate design choice in the DITA Package Processor CLI:
Why orchestration lives in
run, andexecuterefuses to be helpful.
This is not accidental.
It is not unfinished.
It is a hard boundary.
The Problem We Are Avoiding¶
Most content-processing tools collapse these concerns into one command:
- inspect input
- infer intent
- mutate files
- hope nothing goes wrong
That approach optimizes for convenience and quietly trades away:
- debuggability
- auditability
- repeatability
- trust
The DITA Package Processor explicitly does not do that.
The Core Contract¶
The system is built around a single invariant:
Every mutation must be justified by an explicit, inspectable artifact.
That artifact chain is fixed:
package
→ discovery.json
→ planning_input.json
→ plan.json
→ execution report
Each artifact exists for a reason.
Each boundary is enforced.
No phase skips ahead.
No phase repairs the mistakes of the previous one.
Why execute Is Strict¶
The execute command exists to do exactly one thing:
Apply a validated execution plan inside a bounded filesystem sandbox.
Nothing more.
What execute requires¶
- A concrete, schema-valid
plan.json - An explicit
--source-root - An explicit output directory
- An explicit decision to mutate (
--apply)
If any of those are missing, execute fails.
That failure is intentional.
What execute refuses to do¶
execute will not:
- Discover content
- Normalize structure
- Infer intent
- Choose a “main” map
- Invent paths
- Guess defaults
- Repair a broken plan
If the plan is wrong, execution is wrong.
That is how responsibility stays visible.
Why This Matters¶
If execute were allowed to:
- accept a package directory
- silently run discovery
- silently generate a plan
- silently choose paths
- then mutate files
you would immediately lose:
- the ability to review decisions
- the ability to diff intent across runs
- the ability to stop before damage
- the ability to explain why something happened
At that point, you no longer have a system.
You have a script with ambition.
Why run Exists¶
run exists to solve a different problem:
Orchestrating the full pipeline without weakening its contracts.
Humans do not want to manually wire:
discover → normalize → plan → execute
Machines do.
So run performs orchestration internally, while preserving every boundary.
What run does¶
- Invokes
discoverwith a package root - Feeds its output into
normalize - Feeds normalized output into
plan - Invokes
executewith the generated plan - Passes
source-rootandoutputexplicitly - Preserves all intermediate artifacts
No shortcuts.
No shared mutable state.
What run does not do¶
run does not:
- Bypass validation
- Skip artifacts
- Relax schema requirements
- Change execution semantics
- Make
executemore permissive
It is orchestration glue.
It is not intelligence.
Why run Is a Separate Command¶
Because conflating orchestration with execution is how systems rot.
Keeping them separate ensures:
executeis testable in isolationexecutecan be safely reused by other tools- CI systems can inspect plans before applying them
- Execution logic remains boring and predictable
- Orchestration can evolve without destabilizing mutation
This mirrors well-understood boundaries:
- plan vs apply
- compile vs link
- dry-run vs mutate
Implicit run Invocation (And Why It Exists)¶
The CLI supports shorthand invocation:
dita_package_processor --package ./dita
Internally, this is treated as:
dita_package_processor run --package ./dita
This exists for:
- production ergonomics
- end-to-end testing
- backward compatibility
It does not change the underlying model.
All guarantees remain intact.
A Useful Mental Shortcut¶
If you are deciding which command to use:
- Use
discoverto observe - Use
normalizeto enforce structure - Use
planto decide - Use
executeto apply - Use
runto orchestrate the whole pipeline
If you feel tempted to make execute smarter, stop.
That work belongs upstream.
Design Principle (Non-Negotiable)¶
Orchestration may be convenient.
Execution must be boring.
Boring execution is how this tool avoids destroying real documentation at scale.
That constraint is the feature.
Where This Leads¶
This design naturally supports future extensions:
- alternative executors
- preview-only execution
- policy-driven mutation
- CI approval gates
- plan diffs and audits
All without ever weakening execute.
The discipline here compounds.
If you want a follow-up document, the natural next ones are:
- Why discovery is read-only forever
- Why plans are artifacts, not objects
- Why no executor may mutate without
--apply
They all rhyme, and they all protect the same boundary.