I2129 — The lease timings the policy declares are the ones the mcp and serve elections use
Load the policy before mcp and serve elect, make every reader of a lease timing read the declared value, and declare the idle timeout beside the other server timings.
DONE wave 0 · p2 · implementation profile · runs alone
Objective
Load the policy before mcp and serve elect, make every reader of a lease timing read the declared value, and declare the idle timeout beside the other server timings.
Why
Timings (apps/majordomus-cli/src/lease.rs:89-191) is a OnceLock filled with compiled defaults on first read, and declare_timings is called only from LoadedPolicy::load (apps/majordomus-cli/src/policy.rs:364). In mcp and serve, Lease::keep_alive (apps/majordomus-cli/src/lease.rs:1043) and inspect read the timings before App::load, which does not load the policy (apps/majordomus-cli/src/commands/mcp.rs:173-174, apps/majordomus-cli/src/commands/serve.rs:72,89-90), so the later declaration is a silent no-op. Only env enter loads the policy first (apps/majordomus-cli/src/commands/env.rs:548). standing_of (apps/majordomus-cli/src/capability/builtin/server.rs:370,386) and the spawn claim (apps/majordomus-cli/src/commands/serve.rs:330,358) use the compiled BIND_GRACE whatever is declared. docs/MCP.md:284-303 and the comment at .ai/repo/policy.yaml:335-339 say the contest is judged by the declaration. apps/majordomus-cli/tests/lease_declared.rs calls declare_timings directly and case 354_a_lease_contest_is_judged_by_a_declaration.sh checks the schema only. Latent today because the declared values equal the defaults. The idle timeout DEFAULT_IDLE_SECONDS (apps/majordomus-cli/src/cli.rs:3427) is a compiled constant absent from policy.yaml server:. Baseline audit of 2026-10-09 at master 1fb8d6be44, lifecycle gap 6 and drift 1.
Current state
Declared lease timings reach env enter only; mcp, serve, standing_of and the spawn claim use compiled values.
Desired state
mcp and serve read the policy before their first election, so the declared timings are the ones the election uses; standing_of and the spawn-claim bound read lease::timings() rather than the compiled BIND_GRACE and JOIN_TIMEOUT. The deferred part, the idle timeout as a declared server key, is I2701.
Scope
- apps/majordomus-cli/src/lease.rs
- apps/majordomus-cli/src/policy.rs
- apps/majordomus-cli/src/commands/mcp.rs
- apps/majordomus-cli/src/commands/serve.rs
- apps/majordomus-cli/src/capability/builtin/server.rs
- apps/majordomus-cli/src/cli.rs
- .ai/repo/policy.yaml
- share/schemas/majordomus/policy
- apps/majordomus-cli/tests/lease_declared.rs
- test/cases/354_a_lease_contest_is_judged_by_a_declaration.sh
- docs/MCP.md
Dependencies
None. This issue is a root of the graph.
What waits on this
Acceptance criteria
- A real-process test that declares a busy grace different from the default observes the election wait the declared value, in both mcp and serve
- No reader of a lease timing names a compiled constant directly
Validation
- bin/majordomus plan validate
- cargo test --test lease_declared
- MJ_TEST_JOBS=4 bash test/run.sh 354_a_lease_contest_is_judged_by_a_declaration
Evidence required
- declared_timings_reach_the_election_test
Evidence
| covers | type | command | result | at commit |
|---|---|---|---|---|
| declared_timings_reach_the_election_test | test | cargo test --locked --manifest-path apps/majordomus-cli/Cargo.toml --test lease_policy_process | test result: ok. 3 passed; 0 failed: serve and mcp each wait the declared busy grace; peers.rs and server.rs read lease::timings() (run 2026-10-14 12:00-12:15Z on master 2ca25bb9cf plus this change) | 2ca25bb |
Timeline
- started
- 2026-10-14T12:15:47Z
- verified
- —
- completed
- 2026-10-14T12:16:27Z
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/I2129.yaml. Read it back with majordomus plan show I2129.