GovernanceCompiler
Compiles decisions + profile (already validated) into a deterministic GovernancePlan. Loading doctrine (Amendment 2 of ADR#14): supersession is DERIVED, the past is never mutated. ADR files are `accepted` byte-identical forever; a decision ending up `superseded` is a CALCULATED effect in the plan (`effectiveStatus`), never a change in the input decision (`declaredStatus`, which reflects the file as-is, immutable). The same Amendment 2 requires the plan to reflect ONLY accepted governance. That is enforced in code with two criteria (see `entersPlan()` / `isActive()`), a single source of truth used uniformly in `compileDecisions()`, `compileRules()`, `compileGates()`, `compileArtifacts()`, and `indexSupersededBy()`: - **Enters the plan** (`entersPlan`): `status === Accepted`. A `superseded` decision has `status === Accepted` in its file (the file never changes) — that's why it DOES enter, with its `effectiveStatus` derived to `superseded` to show the lineage honestly. `rejected`/`proposed`/`deprecated` NEVER enter: neither as a `decisions` entry, nor do their gates/artifacts declared via policies govern anything. - **Active** (`isActive`): enters the plan AND is not superseded. Only active decisions generate `rules`, and their `kind:gate`/`kind:artifact` policies flow into `gates`/`artifacts` — a superseded decision still appears in `plan.decisions` for traceability, but no longer governs (the successor replaced it). `$decisions` is the full corpus known to the compiler (includes historical lineage: already-superseded decisions, or even rejected/proposed ones that never governed, needed to derive `supersededBy` and so the plan can honestly show its history). `$profile->decisionIds` is, instead, the set of CURRENT decisions the profile declares — `validate()` (the T2 wall) rejects any profile that directly references an already-superseded decision, even when the one that supersedes it is also listed. That's why the plan's `decisions`/`gates`/`artifacts` sections are derived from ALL of `$decisions` (the full lineage, filtered by the criteria above), not just from `$profile->decisionIds` — so the plan can honestly show "ADR-0004 was superseded by ADR-0014" without the profile having to break the wall.
GovernanceCompiler::__construct()
public function __construct(Milpa\Governance\GovernanceValidator $validator):Parameters
| Name | Type | Description |
|---|---|---|
| $validator | Milpa\Governance\GovernanceValidator |
GovernanceCompiler::compile()
public function compile(array $decisions, Milpa\Governance\GovernanceProfile $profile): Milpa\Governance\GovernancePlanValidates and compiles a deterministic governance plan.
Parameters
| Name | Type | Description |
|---|---|---|
| $decisions | list<GovernanceDecision> | Full corpus of known decisions (includes historical superseded lineage and rejected/proposed decisions that never governed) |
| $profile | Milpa\Governance\GovernanceProfile |
Throws
GovernanceException if `$decisions`/`$profile` do not form a coherent
and honest contract (propagated unwrapped from `validate()`), or if
a `kind:gate`/`kind:artifact` policy declares an unrecognized
tier