I2135 — The trust root is read from the default branch, not from whatever the checkout holds
Let a working tree narrow the mesh's trust and never widen it: obey the working tree's declaration with every widening of the trunk's committed copy removed, report each ignored line, and refuse a committed declaration that trusts on first use.
DONE wave 0 · p0 · implementation profile · runs alone
Objective
Let a working tree narrow the mesh's trust and never widen it: obey the working tree's declaration with every widening of the trunk's committed copy removed, report each ignored line, and refuse a committed declaration that trusts on first use.
Why
The server reads .ai/repo/mesh/ from the checkout's working tree once at start (apps/majordomus-cli/src/shared.rs:34-48,164-174). A branch, or an uncommitted edit, can add a key to trust.allow, add seeds or rendezvous.endpoints that make the runtime post its card and envelope to a host of the branch author's choosing, or set policy tofu, and that checkout's runtime obeys it (threat T12); peers judge by their own file, so the damage is local to that checkout, which is the one a reviewer runs a contributor's branch in. docs/MESH.md:365,382-399 and ADR 0050 rule 6 say the declaration is committed; ADR 0059 records this class of incident. No CI gate refuses a committed tofu declaration (enforcement table, 'no unknown remote auto-trust': unit tests only, apps/majordomus-cli/src/mesh/trust.rs:25-35, apps/majordomus-cli/src/mesh/config.rs:397). Baseline audit of 2026-10-09 at master 1fb8d6be44, mesh gap 10 and drift 12.
Current state
Delivered by 4816d7c8ad and 11472da40d: mesh::root::resolve narrows the working tree's declaration to the trunk's committed copy (the trunk is the local branch the repository names as its trunk; a stale local trunk is older trust, the safe direction), every reader asks it, mesh doctor names each ignored line, and scripts/ci/mesh-check refuses a committed tofu, which case 301 now drives.
Desired state
mesh::root::resolve holds the working tree's declaration against the trunk's committed copy: enabled only if both are, a policy no looser than the trunk's, keys, endpoints, seeds and networks only as the trunk lists them (loopback endpoints and seeds admitted); with no trunk copy, tofu is refused. Every reader of the declaration asks it. mesh doctor's trust-root check names every ignored line; mesh-check refuses a committed tofu. Reading only the trunk was rejected: it broke loopback-worktree discovery and every fixture that declares a mesh, for no security gain over narrowing.
Scope
- apps/majordomus-cli/src/shared.rs
- apps/majordomus-cli/src/mesh/config.rs
- apps/majordomus-cli/src/mesh/doctor.rs
- scripts/ci/mesh-check
- apps/majordomus-cli/src/mesh/root.rs
- apps/majordomus-cli/src/capability/builtin/mesh.rs
- apps/majordomus-cli/tests/mesh.rs
- apps/majordomus-cli/tests/mesh_trust_root.rs
- test/cases
- docs/MESH.md
Dependencies
None. This issue is a root of the graph.
Acceptance criteria
- A working-tree edit that adds a key to trust.allow does not admit that key, and mesh doctor names the difference
- A branch that adds a rendezvous endpoint does not make its runtime post to it until the change is on the default branch
- A committed declaration with policy tofu fails scripts/ci/mesh-check
Validation
- bin/majordomus plan validate
- cargo test --test mesh
- MJ_TEST_JOBS=4 bash test/run.sh 301_the_mesh_stays_one_subsystem 494_the_mesh_is_declared_and_held
Evidence required
- working_tree_trust_edit_ignored_test
- tofu_declaration_refused_case
Evidence
| covers | type | command | result | at commit |
|---|---|---|---|---|
| working_tree_trust_edit_ignored_test | ci | gh run view 38635619021 --json jobs (validate on master c0e3585dc8, push); job 116098148455 rust (3) runs tests/mesh_trust_root.rs | test result: ok. 4 passed; 0 failed at master c0e3585dc8 (Rust tree identical to e8505a431d): a_working_tree_edit_that_widens_trust_is_not_obeyed (an uncommitted edit adding key B, policy tofu and endpoint 198.51.100.7 leaves allow=[A], deny_unknown, no endpoint, 3 narrowed lines, and mesh doctor's trust-root check fails naming B, 198.51.100.7 and tofu), a_branch_that_commits_a_widening_is_held_to_the_trunk (a branch commit adding a rendezvous endpoint is not posted to until the trunk lists it), trust_on_first_use_is_honoured_only_from_a_committed_copy, a_working_tree_that_narrows_trust_is_obeyed_as_written; delivered by 4816d7c8ad and 11472da40d (mesh::root::resolve, the trunk being the local branch the repository names as its trunk) | c0e3585 |
| tofu_declaration_refused_case | test | MJ_TEST_JOBS=2 bash test/run.sh 301_the_mesh_stays_one_subsystem 494_the_mesh_is_declared_and_held 1023_a_branch_cannot_widen_the_mesh_trust | 3 passed on branch chore/the-trust-root-is-the-default-branch-is-done (master c0e3585dc8 + case 301 section 4): a committed .ai/repo/mesh/majordomus.yaml with trust.policy tofu makes scripts/ci/mesh-check exit 10 naming the file and the policy; the same declaration with deny_unknown passes; an untracked tofu is left to the runtime. Mutation: with the gate's tofu pattern disabled case 301 fails (expected exit 10, got 0). Case 1023 holds a branch's tofu, second key and hub out of force | c0e3585 |
Timeline
- started
- 2026-10-15T01:28:05Z
- verified
- 2026-10-15T01:28:17Z
- completed
- 2026-10-15T01:28:23Z
Those three fields, the evidence above and the state of the dependencies are all the status is made of. There is no status field to disagree with them.
Canonical record: .ai/repo/project/issues/I2135.yaml. Read it back with majordomus plan show I2135.