Skip to content

Planning

The Planning module converts discovery output into a deterministic execution plan. It is where intent is expressed explicitly as ordered actions with clear reasons and traceable origins.

Planning does not mutate files or execute transformations. It analyzes structure, applies planning steps, and emits a plan that can be reviewed, validated, versioned, and replayed.

This separation allows complex transformations to be reasoned about and tested independently of execution, and it makes the system explainable by design.

dita_package_processor.planning.executor

Planning execution adapter.

Planning never performs execution directly. It delegates execution to the execution layer.

This adapter exists for backward compatibility with callers that still invoke execution through the planning namespace.

PlanningExecutor

Backwards-compatible execution adapter.

This class does not perform execution itself. It delegates to DryRunExecutor in accordance with the execution contract.

Responsibilities
  • Normalize incoming plan objects
  • Delegate to execution layer
  • Emit ExecutionReport
execute(plan, *, execution_id)

Execute a plan in dry-run mode.

Parameters

plan : Any Plan object or dictionary. execution_id : str Unique execution identifier.

Returns

ExecutionReport

dita_package_processor.planning.graph_planner

Graph-based planner for dependency resolution.

This module operates strictly on the PlanningInput contract layer.

It does NOT depend on discovery types or DependencyGraph.

Responsibilities
  • Determine root artifact
  • Walk relationships deterministically
  • Produce stable execution ordering
  • No filesystem
  • No mutation
  • No discovery coupling
Input

nodes : list[str] relationships : list[PlanningRelationship | dict]

Output

list[str] Deterministic artifact order suitable for action emission.

Design rules
  • deterministic
  • pure
  • contract-only
  • fail fast

GraphPlanner

Deterministic dependency planner operating on contract relationships.

Guarantees
  • stable ordering
  • no duplicates
  • dependencies visited before dependents
  • no discovery layer coupling
__init__(*, nodes, relationships)

Initialize planner.

Parameters

nodes Artifact identifiers. relationships Contract relationships containing: source, target, type, pattern_id

plan()

Produce deterministic traversal order.

Returns

List[str]

Raises

GraphPlannerError If graph is cyclic or ambiguous.

GraphPlannerError

Bases: RuntimeError

Raised when dependency planning cannot be resolved safely.

dita_package_processor.planning.hydrator

Plan hydration logic.

This module converts a JSON-loaded dictionary into strongly-typed planning domain models.

Hydration is a strict boundary:

  • JSON is untrusted input
  • Models are validated, typed contracts
  • Errors are explicit and never silent

No filesystem or execution semantics exist here.

PlanHydrationError

Bases: ValueError

Raised when a plan cannot be hydrated into a Plan model.

This signals malformed, missing, or structurally invalid input.

hydrate_plan(payload)

Hydrate a :class:Plan model from a parsed JSON payload.

This function is a strict deserialization boundary. It assumes:

  • Input is untrusted
  • All required fields must be present
  • Types must be valid or convertible
  • Failures must be explicit and actionable

Parameters:

Name Type Description Default
payload Dict[str, Any]

Parsed JSON dictionary.

required

Returns:

Type Description
Plan

Hydrated :class:Plan instance.

Raises:

Type Description
PlanHydrationError

If required fields are missing or invalid.

dita_package_processor.planning.input_normalizer

Discovery input normalizer (legacy compatibility layer).

This module exists to normalize older discovery JSON formats that still expose graph structures. It is NOT part of the new contract boundary.

The new, canonical boundary is:

discovery.json
    → planning/contracts/discovery_to_planning.py
    → PlanningInput

This module remains only for historical pipeline compatibility and migration support. Planning MUST NOT depend on this module.

Responsibilities: - Canonicalize paths - Remove media artifacts from graph topology - Remove edges involving media - Ensure graph nodes reference known artifacts - Perform no inference and no guessing

PlanningInputNormalizer

Normalize legacy discovery graph output into planner-compatible structure.

WARNING: This is NOT the new contract bridge. The canonical bridge is planning/contracts/discovery_to_planning.py.

This class only exists to stabilize older pipeline stages that still expect graph-shaped discovery output.

normalize(discovery) staticmethod

Normalize legacy discovery input.

Parameters:

Name Type Description Default
discovery Dict[str, Any]

Parsed discovery JSON.

required

Returns:

Type Description
Dict[str, Any]

Normalized structure with semantic-only artifacts and graph.

Raises:

Type Description
ValueError

On structural inconsistency.

normalize(discovery)

Functional wrapper for PlanningInputNormalizer.normalize.

Exists only for backward pipeline compatibility.

dita_package_processor.planning.invariants

Invariant enforcement for DITA execution plans.

This module defines and enforces global execution invariants that must hold for a plan to be considered logically safe to execute.

Invariants differ from schema validation:

  • Validation ensures structural correctness.
  • Invariants ensure semantic and operational safety.

Any invariant violation is fatal and must halt execution.

InvariantViolationError

Bases: ValueError

Raised when an execution invariant is violated.

This signals that a plan is unsafe to execute regardless of syntactic correctness.

validate_invariants(plan)

Validate all execution invariants for a plan.

This function is the single public entry point for invariant enforcement. It must be called after schema validation and before execution.

Parameters:

Name Type Description Default
plan Dict[str, Any]

Execution plan dictionary.

required

Raises:

Type Description
InvariantViolationError

If any invariant is violated.

dita_package_processor.planning.layout_rules

Target layout rules for planned artifacts.

This module defines deterministic rules that map an artifact type to its target location within the output package.

This layer is pure planning logic:

  • No filesystem access
  • No directory creation
  • No content inspection
  • No mutation

It is a pure mapping layer used by the planning system to determine where artifacts should be placed.

LayoutRuleError

Bases: ValueError

Raised when an unknown or unsupported artifact type is encountered.

This indicates a planning error: either discovery classification failed or a new artifact type has not yet been mapped.

resolve_target_path(*, artifact_type, source_path, target_root)

Resolve the target filesystem path for an artifact.

Layout rules:

  • map<target_root>/<filename>
  • topic<target_root>/topics/<filename>
  • media<target_root>/media/<filename>

Only the filename is preserved. All directory hierarchy from the source path is intentionally flattened per artifact class to ensure deterministic, collision-free layouts.

This function performs no I/O and no mutation. It only returns a computed path.

Parameters:

Name Type Description Default
artifact_type str

Artifact classification ("map", "topic", or "media").

required
source_path Path

Original artifact path.

required
target_root Path

Root directory of the output package.

required

Returns:

Type Description
Path

Target path for the artifact.

Raises:

Type Description
LayoutRuleError

If the artifact type is unsupported.

dita_package_processor.planning.loader

Plan loading utilities.

Loads a plan.json file from disk and hydrates it into a validated :class:~dita_package_processor.planning.models.Plan model.

Responsibilities: - Read JSON from disk - Parse JSON into a Python object - Delegate structural validation and typing to the hydrator

This module enforces a hard boundary between: Filesystem → Planning Domain

No semantic validation, execution logic, or fallback behavior is allowed here. All failures are fatal and must abort the pipeline.

PlanLoadError

Bases: ValueError

Raised when a plan file cannot be loaded or hydrated.

Indicates a contract boundary failure between the filesystem and the planning domain model.

These errors are non-recoverable by design.

load_plan(path)

Load and hydrate a plan.json file.

Strict process: 1. Read UTF-8 JSON text from disk 2. Parse JSON into a Python mapping 3. Hydrate mapping into a :class:Plan

No optional behavior, no guessing, no silent recovery.

Parameters:

Name Type Description Default
path Path

Path to the plan.json file.

required

Returns:

Type Description
Plan

Fully hydrated :class:Plan instance.

Raises:

Type Description
PlanLoadError

If any step fails.

dita_package_processor.planning.models

Planning data models.

This module defines the immutable data structures that represent a validated execution plan.

Design Principles
  • Declarative only.
  • No execution state.
  • No mutation.
  • No filesystem logic.
  • No runtime side effects.

Planning describes intent. Execution describes effects.

ActionType

Bases: str, Enum

Canonical set of allowed plan action types.

This enum is the single source of truth for action identity.

validate(value) classmethod

Validate and normalize an action type.

Parameters

value: String or ActionType.

Returns

str Canonical action type string.

Raises

ValueError If the action type is invalid.

Plan dataclass

Immutable execution plan.

A Plan is: - Deterministic - Declarative - Immutable - Execution-agnostic

to_dict()

Serialize plan to JSON-compatible dictionary.

Returns

Dict[str, Any]

PlanAction dataclass

Declarative execution action.

This structure represents intent only. It contains no execution state.

copy_map(*, id, source_path, target_path, reason) classmethod

Create COPY_MAP action.

copy_media(*, id, source_path, target_path, reason) classmethod

Create COPY_MEDIA action.

copy_topic(*, id, source_path, target_path, reason) classmethod

Create COPY_TOPIC action.

to_dict()

Serialize PlanAction.

Returns

Dict[str, Any]

PlanIntent dataclass

High-level intent of the plan.

Parameters

target: Logical execution target (e.g. "analysis_only"). description: Human-readable explanation of plan intent.

to_dict()

Serialize plan intent.

Returns

Dict[str, Any]

PlanSourceDiscovery dataclass

Metadata describing the discovery input that produced the plan.

Parameters

path: Root path of discovery input. schema_version: Schema version used during discovery. artifact_count: Total artifacts discovered.

to_dict()

Serialize discovery metadata.

Returns

Dict[str, Any]

dita_package_processor.planning.planner

planner.py

Deterministic execution planner.

Responsibilities
  • Accept ONLY a PlanningInput contract
  • Produce a deterministic Plan dictionary
  • Perform NO discovery
  • Perform NO normalization
  • Perform NO schema forgiveness

Planner is intentionally boring.

Planner

Deterministic planner.

Consumes only PlanningInput. Emits stable copy actions.

plan(planning_input)

Generate deterministic execution plan.

validate(plan)

Validate plan against schema + invariants.

dita_package_processor.planning.validation

Execution-time validation utilities.

These checks validate action parameters before execution. They do not perform filesystem writes or mutate any state.

This module exists to enforce the executor contract: - Plans may be structurally valid - Actions may be syntactically correct - But execution must still fail fast if parameters are unsafe

ActionValidationError

Bases: ValueError

Raised when action parameters are invalid or unsafe for execution.

validate_action(action)

Validate a PlanAction based on its type.

This is the main entry point used by executors. It dispatches to the appropriate validator based on action.type.

Parameters:

Name Type Description Default
action PlanAction

PlanAction to validate.

required

Raises:

Type Description
ActionValidationError

If validation fails.

validate_copy_map_parameters(action)

Validate parameters for the copy_map action.

Required parameters: - source_path - target_path

Validation rules: - source_path must exist - target_path must NOT already exist

Parameters:

Name Type Description Default
action PlanAction

PlanAction to validate.

required

Raises:

Type Description
ActionValidationError

If validation fails.