milpa/governance

Governance-as-contract: immutable ADRs compile into a Governance Profile and an inspectable, honestly-enforced Governance Plan — an enforced gate is only honest if its mechanism actually guards the subject.

Maintainers

Package info

github.com/getmilpa/governance

pkg:composer/milpa/governance

Transparency log

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-07-18 17:00 UTC

This package is auto-updated.

Last update: 2026-07-18 21:52:28 UTC


README

Milpa

Milpa Governance

Governance-as-contract for the Milpa PHP framework: immutable ADRs compile into a Governance Profile and an inspectable Governance Plan. The core law: an enforced gate is only honest if its mechanism actually guards the subject — the validator refuses a profile that claims enforced without a real, allow-listed mechanism behind it.

CI Packagist PHP License Docs

milpa/governance answers one question honestly: does this system's declared law actually enforce itself, or is enforced just a label? It takes the full corpus of immutable, ADR-backed GovernanceDecision objects plus the GovernanceProfile that names which of them currently govern, and produces two things: a verdict (GovernanceValidator::validate() throws a GovernanceException the instant a claim is unproven — a duplicate decision id, a dangling supersedes, a supersession cycle, a reference to a decision that is rejected or superseded (declared or derived), or an enforced gate — the profile's own or one declared inside an ADR's policies — with no allow-listed boundTo) and a plan (GovernanceCompiler::compile() derives the honest GovernancePlan — decisions, rules, gates, and required artifacts — from that same corpus). Supersession is derived, never mutated: an ADR file stays accepted forever; ending up superseded is a calculated effect of the plan, so the audit trail never lies about what a decision's file actually said.

Install

composer require milpa/governance

What it is (and isn't)

Milpa Governance is the engine: it validates and compiles a governance contract you give it — an array of GovernanceDecision value objects plus a GovernanceProfile. It is pure: no I/O, no wall clock, deterministic (the same input compiles to the same GovernancePlan, byte for byte). It does not read files: a host implements GovernanceRepositoryInterface (decisions(): array, profile(): GovernanceProfile) to load a .milpa/governance/ directory — ADRs plus a profile.json — and hands the resulting objects to the engine.

Quick example

use Milpa\Governance\GovernanceCompiler;
use Milpa\Governance\GovernanceValidator;

// $repository implements GovernanceRepositoryInterface — the host already loaded
// .milpa/governance/ (ADRs + profile.json) and handed the engine objects, not files.
$decisions = $repository->decisions();   // array<GovernanceDecision>
$profile = $repository->profile();       // GovernanceProfile

$validator = new GovernanceValidator(GovernanceValidator::DEFAULT_BOUND_TO);
$validator->validate($decisions, $profile); // throws GovernanceException if the profile lies

$plan = (new GovernanceCompiler($validator))->compile($decisions, $profile);

$plan->profileName;    // the profile's name
$plan->gates;          // array<GovernanceGate> — this contract's honest gates
$plan->toArray();      // deterministic, serializable shape

compile() calls validate() first — a plan is never built from an unproven contract — then derives, from the full decision corpus (including superseded and historical lineage, so the plan can show it honestly): one GovernanceRule per active decision, the merged GovernanceGates and RequiredArtifacts (from the profile itself and from kind: 'gate' / kind: 'artifact' policies declared inside each ADR), and a planHash that binds this exact compilation. A rejected, proposed, or deprecated decision never enters the plan — not as a decision entry, and none of its policies govern anything.

The value objects

Value object Role
GovernanceDecision An immutable ADR: id, title, DecisionStatus (proposed|accepted|rejected|superseded|deprecated), what it supersedes, what it governs, its derived policies, and a contentHash that seals its content.
GovernanceProfile The law's table of contents: the decisionIds that currently govern, the typed GovernanceGates and RequiredArtifacts, and the profile's own schemaVersion/name/version.
GovernanceGate A checkpoint: an id, a description, an EnforcementTier (enforced|advisory|convention|deferred), and the boundTo mechanism an enforced gate must name to be honest.
GovernancePlan The compiled, inspectable law: decisions (with derived effectiveStatus/supersededBy), rules, gates, artifacts, actors, assumptions, and the planHash that binds the whole compilation. toArray() is the deterministic, serializable shape.
GovernanceManifest The {id: contentHash} immutability seal, ksorted and hashed — verifyIntegrity() detects drift against the current hashes, immutabilityViolations() detects a decision that changed or disappeared since a prior base manifest.
GovernanceException Every contract break, one named factory per cause (profileMissing, decisionDuplicate, supersessionCycle, enforcementUnproven, …) — each message names what broke, why, and the fix.

Anatomy of a governance contract

A contract lives in .milpa/governance/: immutable ADRs (adr/*.md) plus a profile.json. The engine consumes them through the host's GovernanceRepositoryInterface — it never reads the directory itself. This is the minimal shape (generic — an example service, not any real repo's law):

.milpa/governance/adr/0001-tests-are-the-contract.md

---
id: ADR-0001
title: Tests are the contract
status: accepted
date: 2026-01-15
supersedes: null
governs: [quality]
policies: []
---

## Context
A green suite is the cheapest proof that a change didn't break the promise.

## Decision
Every change ships with the test suite green. The `tests` gate is `enforced`, bound to the CI job
that actually runs it.

## Consequences
An `enforced` gate with no real job behind it would be a lie the validator rejects.

.milpa/governance/profile.json

{
  "schemaVersion": "0.1",
  "name": "example-service",
  "version": "2026.01.1",
  "decisionIds": ["ADR-0001"],
  "gates": [
    { "id": "tests", "description": "The test suite passes.", "tier": "enforced", "boundTo": "ci:test:unit" }
  ],
  "artifacts": [],
  "actors": []
}

Compiling this contract yields a GovernancePlan whose tests gate is honestly enforced because ci:test:unit is an allow-listed mechanism (GovernanceValidator::DEFAULT_BOUND_TO). Change boundTo to something not on the allow-list and validate() throws — the wall that stops an aspirational gate.

Requirements

  • PHP ≥ 8.3
  • No runtime dependencies — the engine is dependency-free by design (milpa/core is a dev-only dependency, used solely to generate the API reference).

Documentation

Full API reference: getmilpa.github.io/governance — generated straight from the source DocBlocks and dressed with the Milpa design system.

Contributing

Contributions are welcome — see CONTRIBUTING.md. Please report security issues via SECURITY.md, and note that this project follows a Code of Conduct.

License

Apache-2.0 © Rodrigo Vicente - TeamX Agency.

Milpa is designed, built, and maintained by Rodrigo Vicente - TeamX Agency.