I1992 — A rebuild that fails consumes no change: the next request retries it
When a long-lived server's rebuild of its repository picture fails, keep the change it noticed unconsumed, so that a later request retries the load (bounded, not on every request) instead of serving the previous generation until git happens to move again.
DONE wave 0 · p2 · implementation profile · parallel safe
Part of development-runtime — The runtime has a development surface.
Objective
When a long-lived server's rebuild of its repository picture fails, keep the change it noticed unconsumed, so that a later request retries the load (bounded, not on every request) instead of serving the previous generation until git happens to move again.
Why
Found while closing the case 545 flake (fix/ci-flakes-545-and-multicast 79ecdbb944, batch B3 #788): Live::reload (apps/majordomus-cli/src/live.rs, the Err arm of `match built`) records `state.stamp = taken` when App::load fails, so the stamp the failed load was taken under now reads as current. A transient failure — a git process caught mid-write, an index.lock, a manifest saved half-way — therefore makes the server miss the commit that triggered it, and every answer (HTTP, MCP, Cockpit) keeps the old head with a fresh-looking observed_at until some unrelated move. The comment there gives the reason (not to retry a failing load on every request); the cost of that choice is a commit silently not seen. Case 545 now polls for the new head and prints the server's 'would not load' line when it fails, so an occurrence names this path.
Current state
A failed App::load sets the generation's stamp to the stamp it was taken under: the change is consumed, and no request retries the load until the git control files move again.
Desired state
A failed load leaves the generation's stamp as it was and records the failure (stamp and moment) beside it; a request whose stamp equals the failed one retries the load once a bounded interval has passed since that failure, and is served the previous generation (with the failure said) inside it. A stamp that moves again retries at once. The warning names the retry.
Scope
- apps/majordomus-cli/src/live.rs
- apps/majordomus-cli/tests
- test/cases
Out of scope
- The stamp's watched files (WORKTREE_FILES, COMMON_FILES)
- The mid-rebuild rule: a request arriving during a rebuild is still served the existing generation
Dependencies
None. This issue is a root of the graph.
Acceptance criteria
- A load that fails and then would succeed, with no further repository move, is retried after the interval and the next answer carries the new head
- A load that keeps failing is attempted at most once per interval, not once per request
- Case 545 still passes; with the retry removed, the new test fails
Validation
- bin/majordomus plan validate
- cargo test --manifest-path apps/majordomus-cli/Cargo.toml --lib live::
- MJ_TEST_JOBS=4 bash test/run.sh 545_every_answer_says_when_it_was_observed
Evidence required
- failed_load_is_retried_test
Evidence
| covers | type | command | result | at commit |
|---|---|---|---|---|
| failed_load_is_retried_test | test | cargo test --locked --manifest-path apps/majordomus-cli/Cargo.toml --lib live:: | test result: ok. 15 passed; 0 failed (run 2026-10-14 between 07:40Z and 07:58Z on master ce5959db59) | ce5959d |
Timeline
- started
- 2026-10-14T07:59:03Z
- verified
- —
- completed
- 2026-10-14T07:59:09Z
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/I1992.yaml. Read it back with majordomus plan show I1992.