Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f921562
feat(governance): honest unconfigured-governance seams (N3/N4) + C3 d…
tachyon-beep Jun 7, 2026
7b15c11
test(governance): pin N3 keyless reachability end-to-end via build_ru…
tachyon-beep Jun 7, 2026
fbdf949
fix(wardline): adopt Wardline's suppression_state key (W3, weft-ef793…
tachyon-beep Jun 7, 2026
4a254f2
docs(doctor): clarify check_policy_cells mirrors precedence, not root…
tachyon-beep Jun 7, 2026
18c3a11
feat(wardline): echo scan-level artifact_status posture at the scan_r…
tachyon-beep Jun 8, 2026
0dabc8b
feat(governance): reject disabled evidence tests (POLICY-1) + doctor …
tachyon-beep Jun 8, 2026
41e0b20
fix(governance): surface lineage divergence at status root (GOV-1)
tachyon-beep Jun 8, 2026
acdbff0
feat(store): close delete-and-rechain forgery — v3 seq-binding + head…
tachyon-beep Jun 8, 2026
691e838
fix(store): fsync audit commits — synchronous=FULL closes power-cut t…
tachyon-beep Jun 8, 2026
cf42727
docs(store): correct HeadAnchor over-claim — replay is a known unclos…
tachyon-beep Jun 8, 2026
0a9cfe9
fix(doctor): detect split-brain instruction block — freshness was fir…
tachyon-beep Jun 8, 2026
98c9f5c
fix(identity): sign the SEI capability probe when keyed (ID-3)
tachyon-beep Jun 8, 2026
b36939d
feat(judge): cap the agent-controlled judge request — prompt-stuffing…
tachyon-beep Jun 8, 2026
5076170
fix(audit): close final three low risk-audit findings (AUTH-1, POLICY…
tachyon-beep Jun 8, 2026
7a054a6
style(tests): clear pre-existing ruff errors in test_doctor/test_install
tachyon-beep Jun 8, 2026
01382d5
fix(security): close JUDGE-3/GOV-2/F1 + honesty hygiene for 1.0
tachyon-beep Jun 8, 2026
84a8047
feat(doctor): canonical --fix flag + repairability tagging; 1.0 tidy
tachyon-beep Jun 8, 2026
d5a7580
docs(guide): add operator configuration + output-interpretation guides
tachyon-beep Jun 8, 2026
b975567
docs(guide): add a worked end-to-end example to the output guide
tachyon-beep Jun 8, 2026
a11378e
fix(doctor): split-brain is [operator] not [auto-fixable]; match fili…
tachyon-beep Jun 8, 2026
f5f5a8b
feat(mcp): close dogfood LEG-1/2/3 — policy discoverability, scan_rou…
tachyon-beep Jun 9, 2026
64208dd
release: cut 1.0.0 final — drop the rc
tachyon-beep Jun 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ coverage.json

# Local tooling config (machine-specific, never commit)
.mcp.json
# Claude Code scheduled-tasks runtime lock (transient; never commit)
.claude/*.lock

# Agent instruction files — filigree-generated, regenerated each session
AGENTS.md
Expand Down
122 changes: 109 additions & 13 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,113 @@ All notable changes to Legis are documented here. The format follows
versions per [PEP 440](https://peps.python.org/pep-0440/) /
[SemVer](https://semver.org/) (pre-release: `1.0.0rc1`).

## [1.0.0] — 2026-06-09

### Security / honesty (second pre-1.0 adversarial review, 2026-06-09)

A second independent adversarial review re-attacked the first audit's (self-verified)
fixes. The crypto-threshold assumption held; these gaps it surfaced are now closed:

- **JUDGE-3 — protected cell is now fail-closed unconditionally.** A judge `ACCEPTED`
in the protected cell is advisory and is downgraded to `BLOCKED` (escalate to
operator sign-off) unless a deterministic, non-LLM validator confirms it — a policy
is protected by virtue of being *routed* to the cell, no longer by separate
membership in `LEGIS_PROTECTED_POLICIES`. Previously the Q-H3 downgrade was gated on
that exact-match set, which diverges from the glob-capable cell routing, so a
protected-cell policy outside the set (including any glob route, and the empty-set
default) had its `ACCEPTED` signed as authoritative on the model's word — a silent
fail-open. **Behavior change:** in the default config (no validator wired), all
protected overrides now require operator sign-off. `protected_policies` now drives
only a config-hygiene warning (an undeclared protected-cell policy) and the
read-side signature requirement.
- **GOV-2 — `/governance/identity-gaps` no longer reports a false all-clear.** It now
returns a `{status, gaps}` envelope (`status: "unavailable"` when the Loomweave
client is unwired vs `"checked"`), so "could not check" is distinguishable from
"checked, zero orphan gaps" — the same false-green shape GOV-1 fixed on the sibling
lineage-integrity endpoint. *Response-shape change for this endpoint* (was a bare
list).
- **F1 — `TrailVerifier` docstring corrected.** It no longer claims that flipping an
in-record flag cannot downgrade a protected record to "unsigned, skip"; the
modify-to-unsigned and tail-truncation residuals of the raw-file-write tier are now
documented honestly (code hardening tracked post-1.0).
- **POLICY-1 — aliased-marker / fixture-skip residuals documented.** The evidence-
liveness gate's `_disabling_marker` now honestly documents that an aliased disabling
marker (`skipper = pytest.mark.skip; @skipper`) and a fixture-mediated `pytest.skip()`
are not caught (zero shipped `@policy_boundary` sites today; name-heuristic hardening
tracked post-1.0).
- **ID-SEI-1 — `LEGIS_ALLOW_INSECURE_REMOTE_HTTP` now warns.** Permitting plaintext to
a remote Loomweave/Filigree voids the SEI/binding TLS custody seal (responses are not
HMAC-signed); the bypass now logs a warning and is documented as dev/loopback-only.
- **ID-SEI-2 — `alive` is now strict-bool.** A non-bool truthy `alive` from a
buggy/hostile Loomweave (e.g. the string `"false"`, or `1`) no longer promotes to a
stable SEI identity; it degrades fail-closed.

Dogfood-#2 governance honesty (convention C-10) — branch-local; merge/release
gated on the filigree-first propagation. Capability confinement (proposed C-8) is
preserved: operator signing keys stay out of agent reach, no key is auto-provisioned
or relocated, and no MCP tool enables a cell or self-grants authority (pinned by
`test_c8_no_agent_reachable_enablement_or_signing_surface`).

### Changed
- **Adopt Wardline's `suppression_state` key (W3, weft-ef79348eb2).** Wardline
renamed the per-finding output key `suppressed` → `suppression_state` across all
surfaces, including the **signed** legis scan artifact — which changed the
canonical signed bytes and broke the Wardline→legis hop (`legis_e2e` red). legis
ingest (`WardlineFinding.from_wire` + `active_defects`) now reads the new key; the
values (active/waived/suppressed/baselined/judged) are unchanged. Clean break: a
finding carrying only the legacy `suppressed` key reads as `active` and **over**-gates
(fail-safe — never silently drops a defect). No signing/canonical change was needed
(legis's signer already reproduces Wardline's rekeyed golden byte-for-byte). Added the
**legis-side cross-impl golden mirror** legis was missing — `sign(_GOLDEN_FIELDS,
_GOLDEN_KEY) == hmac-sha256:v2:2b2cf09…` over `suppression_state` — so the signed hop
is self-verifying on both ends, not only in Wardline's opt-in oracle.
- **Honest, actionable unconfigured-governance errors (N3, weft-df8d2ef454 — C-10(c)).**
legis no longer "ships dark and quiet": the two inert axes now name their concrete
enablement path. `INVALID_CELL_SPEC` (scan_route, server-owned routing unset) names
`LEGIS_WARDLINE_CELL` / `LEGIS_WARDLINE_CELL_BY_SEVERITY`; `CELL_NOT_ENABLED` is split
into the keyless simple tier (map the policy via `policy/cells.toml` /
`LEGIS_POLICY_CELLS`, `LEGIS_DEV_DEFAULT_CELLS=1` for the chill dev default) and the
complex tier (`LEGIS_HMAC_KEY`, operator-held, out-of-band + relaunch). Subsumes Le1.
Fail-closed is preserved — the errors become honest, nothing auto-opens.
- **Honest `SKIPPED_DIRTY_TREE` skip payload (N4, weft-a7a92a40dd — C-10(d)).** The
dirty-tree skip is no longer a prose-only blob: `WardlineDirtyTreeError.to_payload()`
is the single source both transports (MCP `structuredContent` + HTTP body) serialize,
carrying machine-switchable `reason` / `posture` / `cause` / `remediation` (commit for
a signed artifact, or the `LEGIS_WARDLINE_ALLOW_DIRTY=1` operator opt-in) while still
governing nothing. The dirty-snapshot opt-in stays an env-only operator switch — no
`scan_route` call argument was added. (Compounds with sibling finding C1: loomweave's
tracked runtime DB perpetually dirties the tree; that fix is loomweave-side.)
- **`install.filigree_scope` doctor check is gated on filigree being installed.** The
report-only unscoped-binding warning only fires when filigree is actually set up in
the project (file-existence probe: `.filigree.conf` AND a resolved store config — no
import of filigree, staying decoupled from its schema). An unscoped binding only
fail-closes against a server-mode filigree daemon, so the warning is noise when
filigree is absent. When it does fire, the message now names it as operator-owned (the
`--filigree-url` is operator-pinned in wardline's `.mcp.json` entry; legis never writes
it), so the check stays `repairable=False` and names the operator action instead of
implying `--fix` can resolve it.
- **`legis doctor --format json` checks now carry a `repairable` field** (bool). Additive
— every check object gains the key; no existing key changed.

### Added
- **Two report-only `legis doctor` checks (N3).** `runtime.policy_cells` and
`runtime.wardline_routing` report whether the governance surface is wired and, when
not, name the exact enablement keys (warn, never auto-fixed; presence-only — they
write nothing and never render a key value).
- **`legis doctor --fix`** — canonical spelling of the repair flag (`--repair` stays a
working alias, no break for scripts). Each check now carries a `repairable` bit, and
the text view tags every problem `[fixed]` / `[auto-fixable]` / `[operator]` with a
footer that points auto-fixable items at `legis doctor --fix` and tells the operator
that `[operator]` items need out-of-band config + a relaunch. Distinguishes "doctor
can repair this" from "only you can" at a glance.

### Docs
- **Charter: self-asserted write actor (C3, weft-f506e5f845).** `legis-charter.md`'s
known-gaps note now also covers legis's *own* audit records — `agent_id` / `operator_id`
are self-asserted (launch-bound + HMAC-tamper-evident, but not authenticated); the
narrative `verified_author: null` maps to these stored fields. The governed subject's
SEI is still resolved; only the actor is unauthenticated.

## [1.0.0rc4] — 2026-06-08

### Added
Expand Down Expand Up @@ -310,19 +417,8 @@ WP-M1 service-layer extraction, consolidated behind a stable version.
`HTTPException`, so both HTTP and the forthcoming MCP adapter drive one code
path. Behavior-preserving; FastAPI handlers are now thin adapters.

### Known limitations
- The agent-facing **MCP surface** is designed and decomposed
(`docs/superpowers/specs/2026-06-03-legis-mcp-surface-design.md`) with WP-M1
landed; WP-M2..M6 (registry + `legis_explain`, the MCP stdio server, the
write/governance tools, safety hardening, judge reason-classification) are not
yet built.
- The git-rename provider to Loomweave is contract-locked but operatively gated on
Loomweave driving a committed rev-range.
- `HttpLoomweave` runs loopback-unauthenticated; sibling-gated work packages
(Filigree signature column, live-Loomweave oracle + HMAC auth, operative
git-rename feed) remain.

[1.0.0rc4]: https://github.com/foundryside-dev/legis/compare/v1.0.0rc3...HEAD
[1.0.0]: https://github.com/foundryside-dev/legis/compare/v1.0.0rc4...v1.0.0
[1.0.0rc4]: https://github.com/foundryside-dev/legis/compare/v1.0.0rc3...v1.0.0rc4
[1.0.0rc3]: https://github.com/foundryside-dev/legis/compare/v1.0.0rc2...v1.0.0rc3
[1.0.0rc2]: https://github.com/foundryside-dev/legis/releases/tag/v1.0.0rc2
[1.0.0rc1]: https://github.com/foundryside-dev/legis/releases/tag/v1.0.0rc1
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Legis is the fourth Weft product: the git/CI and governance side of the suite's

## Status

Legis is at **`1.0.0rc4`** — the fourth release candidate. The standalone git/CI surfaces, the graded 2×2 enforcement engine, the agent-programmable policy grammar, SEI-keyed attestations, and the Wardline/Filigree suite combinations are all built and tested; the git-rename provider to Loomweave is contract-locked, operative pending Loomweave's committed-range driving. The transport-agnostic service layer (WP-M1) and the agent-facing MCP surface on top of it have landed (`legis mcp`), and Legis now stands itself up via `legis install` (instruction block + `legis-workflow` skill pack + SessionStart hook + `.mcp.json` registration). `legis doctor [--repair]` provides an operator health view and safe repair for the install + config layer. See the combination matrix below for per-pairing status and `CHANGELOG.md` for the release notes.
Legis is at **`1.0.0`**. The standalone git/CI surfaces, the graded 2×2 enforcement engine, the agent-programmable policy grammar, SEI-keyed attestations, and the Wardline/Filigree suite combinations are all built and tested; the git-rename provider to Loomweave is contract-locked, operative pending Loomweave's committed-range driving. The transport-agnostic service layer (WP-M1) and the agent-facing MCP surface on top of it have landed (`legis mcp`), and Legis now stands itself up via `legis install` (instruction block + `legis-workflow` skill pack + SessionStart hook + `.mcp.json` registration). `legis doctor [--fix]` provides an operator health view and safe repair for the install + config layer, tagging each problem `[auto-fixable]` or `[operator]` so it is clear what `--fix` will and will not touch, including report-only checks that name the enablement path when the governance surface is unwired (policy cells, Wardline routing) — it reports, it never auto-enables or touches a signing key. See the combination matrix below for per-pairing status and `CHANGELOG.md` for the release notes.

## The Weft suite

Expand Down Expand Up @@ -92,8 +92,26 @@ Legis's enforcement surface is a **2×2**, and the base always stays weightless.
- **Block + escalate** is also available here, with the added constraint that even a human sign-off produces a tamper-bound record.
- **Audit lineage keyed on SEI.** Every verdict, override, and sign-off is recorded in an append-only trail keyed on Stable Entity Identity so the record survives rename/move.

> **What "cryptographic layer" means here.** The HMAC signing is intra-suite *tamper-evidence* — it binds a governance record to SEI-stable code identity and detects after-the-fact edits by an actor who cannot recompute the keyed signature (e.g. a holder of raw DB-file access). The recorded actor is *self-asserted* (not a third-party-authenticated identity), and verification today is same-process Python over v1 canonical JSON. It is **not** a third-party-verifiable, cross-party authenticated cryptographic proof. RFC-8785 canonicalization is the named one-file upgrade for the day a non-Python verifier of a Legis attestation lands.

The elspeth CI judge (`/home/john/elspeth`) is the working design ancestor of the protected cell — it is the "thick version" shipped inside elspeth's own codebase. Legis is where the same mechanisms land as a suite-level, opt-in layer.

### Known security limitations

Legis is a governance-*honesty* tool, so it states its own residual limits plainly rather than leaving them in source comments:

- **The coached cell is a model-robustness wall, not a cryptographic one.** A blocked agent clears the coached gate by convincing the LLM judge; a *malicious prompt injection* that persuades the model will likewise clear it. Structural injection (forging a verdict key) is closed and any transport/parse failure is fail-closed to `BLOCKED`, but the coached cell has no defense-in-depth against a model that is genuinely fooled. For verdicts that must not rest on the model's word, use the **protected** cell, where a judge `ACCEPTED` is advisory only and is downgraded to require operator sign-off (unless a deterministic, non-LLM validator confirms it).
- **Tamper-evidence assumes the signing key is out of the attacker's reach, and is not absolute against raw DB-file writes.** v3 signing binds each record's chain position, so in-place edits, reordering, and renumbering are detected. A holder of raw write access to the governance `.db` can still *delete* a record and re-chain, or rewrite a record's policy to a non-protected value and strip its protected markers ("modify-to-unsigned"), or truncate the tail — these are residuals of the conceded raw-file-write threat tier. The opt-in `HeadAnchor` mitigates truncation/rewind (with a documented anchor-replay caveat). Keep the governance store on storage only the operator controls.
- **Durability tier.** The audit store runs `synchronous=FULL`, but a power loss can still drop the most recent un-checkpointed appends; the trail stays internally consistent (a shortened-but-valid tail), it does not corrupt.
- **SEI binding integrity rests on TLS by design.** The Weft request HMAC authenticates legis's *requests* to Loomweave/Filigree; it does not sign their *responses*. Response integrity is TLS's job. `LEGIS_ALLOW_INSECURE_REMOTE_HTTP=1` permits plaintext to a remote sibling and therefore **voids that custody seal** (an on-path attacker could forge a stable identity binding) — it now logs a warning and is for dev/loopback use only.

**The full adversarial threat model is published — attack recipes and all.** Legis holds itself to the honesty bar it enforces, so both pre-1.0 adversarial reviews ship in the open, including the *reproduced* attack recipes for every residual above:

- [`docs/release-1.0-risk-audit.md`](docs/release-1.0-risk-audit.md) — the multi-lane pre-release risk audit.
- [`docs/release-1.0-pre-ship-review.md`](docs/release-1.0-pre-ship-review.md) — the independent second pass that re-attacked the audit's own fixes (and caught a real fail-open the self-verified pass had missed).

This is deliberate. Legis is a *"forced me to do the right thing"* discipline, not a hardened security boundary — its worth is the effort the threat model forces and the residual tiers it names honestly (raw DB-file write, model-robustness, response-integrity-rests-on-TLS), not a claim to withstand an attacker who already holds those capabilities. **The system is only as load-bearing as the effort put into it.**

### Graded enforcement

Across all four cells, one underlying primitive: when a policy fires, the *cell* decides who answers and what is recorded.
Expand Down Expand Up @@ -164,18 +182,27 @@ Legis is complete when:

## Repository layout

- `docs/guide/` — operator guides: configuration reference and output interpretation
- `docs/federation/` — Weft-facing contracts and participation notes
- `docs/design/` — product intent and design notes
- `docs/superpowers/specs/` — approved design specs
- `docs/superpowers/plans/` — implementation plans

## Documents

**Operator guides (how to configure and read Legis):**
- `docs/guide/configuration.md` — what to set, what each cell costs to enable, the full env-var/flag reference, and the dev-only escape hatches
- `docs/guide/reading-legis-output.md` — what you're seeing when an agent acts: the verdict/outcome/status vocabulary and which signals need a human

**Design and federation:**
- `docs/design/legis-charter.md` — authority boundary, operating modes, near-term scope
- `docs/federation/README.md` — Weft participation overview
- `docs/federation/sei-conformance.md` — Legis-specific SEI posture and obligations

**Security & threat model (published in full, by design):**
- `docs/release-1.0-risk-audit.md` — the pre-1.0 adversarial risk audit (multi-lane), with reproduced attack recipes for every residual
- `docs/release-1.0-pre-ship-review.md` — the independent second-pass review that re-attacked the audit's own fixes

**Planning:**
- `docs/superpowers/specs/2026-06-01-legis-federation-repo-design.md` — federation repo design spec
- `docs/superpowers/specs/2026-06-01-legis-roadmap-to-first-class.md` — final-form roadmap (the two halves, the 2×2, dependency gates, SEI conformance)
Expand Down
Loading
Loading