β RETIRED β kept as HISTORY, not as guidance¶
Retired 2026-08-16. This is the record of how documentation was run from roughly 2026-06 to 2026-08. Do not follow it. The live standard is
../00-documentation-standard.md.Why it was retired¶
The Disaster-Recovery framing asked one question of every document: could someone rebuild this subsystem from the doc alone if the source were lost? That produced real gains β the precision rules below are genuinely good and have been carried forward β but the frame itself turned out to be wrong in three ways.
The premise stopped holding. DR grading assumes total source loss is the risk worth designing against. It is not the risk this project actually runs. The source is on GitHub, in HACS, and on every user's disk. Meanwhile the real failure mode β a doc confidently describing behaviour the code no longer has β is one DR grading does not address, because a doc can be beautifully reconstruction-grade and still be describing last month's code.
It made every doc a diff against a baseline that was never maintained. The companion epoch model (also retired,
documentation-epoch-lifecycle.md) declared the numbered guides to be a diff against a canonical DR baseline, and "not another canonical specification". They were never written that way β most open with "Complete implementation reference". The declaration was the part that was false, and maintaining the fiction cost real effort at every reconciliation pass.Grading created an obligation instead of a tool. Per-subsystem DR status (Β§7) meant docs accrued a score, and a score invites work that raises the score rather than work that helps a reader. The replacement has no grades.
What was carried forward¶
Into
../00-documentation-standard.md: the Β§5 meta-rules (never be confidently wrong; no normative collisions; canonical text is not attention-proof; a doc no index reaches does not exist), the Β§4 rubric of what a doc must specify, and the Β§6 acceptance test including the blind-agent rebuild.Β§5.2 β a reconstruction that disagrees is a bug signal β was generalised rather than copied. It is now the adjudication rule for design docs: when doc and code disagree, establish which way the drift went before assuming the doc is stale.
What died with it¶
The Β§0 availability contract, the Β§1βΒ§3 grade scale and rubric-shape rationale, and the Β§7 per-subsystem DR status. Also the epoch/baseline machinery in its companion doc.
00 β Disaster-Recovery Doc Standard¶
Scope: The bar every
docs/dev/subsystem doc is measured against. A doc is disaster-recovery grade when someone with only the docs β no source β can rebuild the subsystem with correct behaviour, API, and data shapes (its own section for the subsystem, the neighbouring sections for their interfaces β Β§0). This file defines what that requires, why, and how to check it. It is grounded in two measured reconstruction runs, not vibes.
This standard governs the numbered dev docs; 12-battery-system.md and
10-learning-system.md are the worked exemplars.
0. The availability contract (ruled 2026-08-07)¶
What "disaster" means, exactly β the scenario every DR doc is sized against:
- The disaster is TOTAL loss of source code. The DR corpus (all numbered docs, together) must suffice to rebuild the integration with no original source at all.
- The target is FUNCTIONAL identity, never byte identity. A rebuild is correct when it satisfies every documented contract and behaviour; private names, internal structure, and incidental choices are explicitly NOT part of the contract. (Corollary, proven by CAL-23: only behavioural tests can examine a rebuild β a test that asserts private internals demands byte identity the contract never promised.)
- The corpus is SELF-HOSTING β neighbours are supplied as documentation, not code.
One section is sufficient when a competent blind implementer can rebuild its
subsystem from that section plus the interfaces the OTHER sections' docs state
(and public framework knowledge). No source appears anywhere in this contract β that
is what gives the recovery a start point. Naive "rebuilt-neighbours" induction has no
base case and the dependency graph is not even acyclic (
error_tracker β active_job β learning β error_trackeris real); doc-stated interfaces dissolve both: mutual dependents each publish their surface on paper. The bootstrap is TWO-PHASE, not topological β measured reality: only 5 of 26 sections are dependency-free and the rest form one mutually-recursive cluster, so no topological order exists. Phase 1: declare every doc-stated interface as a skeleton, corpus-wide. Phase 2: implement each section against the skeletons, in any deterministic order. Composition rides the interface statements, not the sequence. Hub modules rebuild by ACCRETION (ruled 2026-08-07):core/manageris not a build-order node β it grows as each subsystem comes up, that section implementing its own slice of the manager's wiring against the skeleton. The reading order doubles as an existence-proof growth sequence: the doc numbers are landing order, i.e. the sequence in which this system demonstrably grew once, manager accreting alongside. Reference edges are not requirement edges (Chris's check, 2026-08-07): the import graph's mutual-recursion cluster mostly measures OPTIONAL consumption. The architecture is atom + rings (01 Β§2, the model; core-minimality, the measured map): the ATOM (adapter + dispatch + rooms + spine + active_job) fires a clean with everything else ABSENT β most rings attach through absence-tolerant seams (if learning is None: β¦, the model to copy), which is stronger than skeletons: the core tolerates a ring's nonexistence, not merely its stubbing. Recovery therefore runs ATOM-OUT: rebuild the atom's sections first (a system that CLEANS exists at that point), then attach rings in any order, each restoring its feature slice; phase-1 skeletons are only genuinely needed across the WELDED edges core-minimality maps as refactor debt. Consequence for authors: every doc MUST state the interfaces it PROVIDES and the neighbour interfaces it CONSUMES β and whether each consumed seam is absence-tolerant or welded (the rubric's integration-contract row is load-bearing for the whole corpus, not local hygiene). -
Test-harness honesty: blind-reconstruction experiments (the ablation loop) give builders the real surrounding source as a practical stand-in for rebuilt neighbours. That is an OVER-approximation β call-site bodies teach more than documented interfaces would β so a sandbox pass is necessary but not sufficient evidence of the corpus contract. The corpus-level acceptance test is the periodic full docs-only rebuild drill (the doc-as-spec cold run β measured ~90% at last execution; the ablation campaign exists to close that gap section by section).
-
Why any of this can work at all (Chris, 2026-08-07): recovery is verifiable because the architecture is incrementally testable. Stand up the atom β test: it cleans. Attach one ring β test: the ring works AND the atom still cleans. Every attachment is a small falsifiable proof against a green baseline, so a full rebuild is N bounded verifications instead of one unboundable act of faith. This property comes from the architecture, not the prose: the absence-tolerant seams are what let a partial system RUN, and a running partial system is what makes each increment testable. DR-grade documentation presupposes DR-grade architecture β a welded monolith cannot be made recoverable by writing better docs, because nothing in it is testable until all of it exists. (This is the extractability-proves-the-layer principle read in reverse: the same seams that let a subsystem leave cleanly are the ones that let it return.) The rebuild drill's procedure follows directly: atom first, its behavioural suite green; then per ring β the ring's own behavioural tests plus a re-run of the atom's suite as the did-I-break-the-core regression gate.
A doc's scope line states this contract's per-section form; it never claims document-alone sufficiency and never treats the original source as a legitimate dependency β its dependencies are the corpus and the framework, full stop.
1. What "disaster-recovery grade" means¶
The test is literal: a blind reader (or agent) rebuilds the module from the doc
alone; the rebuild is diffed against the real source; the diff measures the doc.
Every behavioural/API/shape difference that traces to a doc omission is a doc defect
(DOC_GAP), separate from reader mistakes (AGENT_MISS). The goal is that if the
code vanished, the docs are enough to bring it back.
This is a stricter bar than "reference." A reference doc explains what and why. A disaster-recovery doc is an executable specification β precise enough to re-derive the exact bytes on disk and the exact edge behaviour, not just the idea.
2. What we measured (why the rubric is shaped this way)¶
Two runs, blind reconstruction β adversarial diff vs source:
- Battery docs β ~90% cold. Strong; the ~10% miss was underspecified edges.
- Learning docs β ~55% (broke the 90% bar). A sharp, informative split:
- Algorithm / math reconstructs well β estimator ~75%, utils ~68%; most misses were reader errors (rounding, indexing), not doc faults.
- Persistence / serialization / HA-integration COLLAPSES β
stats_rebuilder~35%,history_store~35%. Whole surfaces were simply absent: exact CSV columns (19 of 27 missing),job_statsJSON nesting,jobs_indexschema,hass.datacache keys, atomic writes, HA-bound constructors, the ~14-kwargbuild_completed_job_payloadsignature, water metrics everywhere. - 7 statements were CONFIDENTLY WRONG β worse than silence, because they actively misled the rebuild (0-vs-1 indexing, drift source, a formula weight that under-weighted area ~100Γ, a function's module-vs-method location, a default).
Conclusion the standard encodes: docs are naturally good at algorithm and weak at serialization + integration + edges. So this rubric front-loads the collapse zones (Β§2 data shapes, Β§3 edges, Β§4 integration) and bans confident wrongness (Β§meta). Don't spend the effort where docs already succeed; spend it where they fail.
3. The grade scale¶
| Grade | Answers | Can you rebuild from it? |
|---|---|---|
| Reference | what & why | no |
| Interface | + API / signatures | partially |
| Reconstruction (DR-grade) | + exact shapes, edges, integration, provenance | yes |
DR-grade = Reconstruction. Everything below is what pushes a doc up to it.
4. The rubric β a DR-grade subsystem doc MUST specify¶
Grouped by the failure modes above; use as a checklist.
4.1 Algorithm & rules (docs already do this β hold the line)¶
- The exact computation: every formula with all terms, coefficients, and weights β not "weighted by area" but the actual weight (area was ~100Γ under-weighted once).
- Precedence / tie-breaking / ordering rules, stated explicitly.
- Behaviour at the boundaries of each input range.
4.2 Data shapes & serialization (the #1 collapse zone β spell it out)¶
- Every persisted structure's exact schema: field names, types, and nesting.
- Exact file formats: CSV columns in order and count; JSON key layout; store keys.
- In-memory layout that survives restarts (
hass.datacache keys + shape). - Per-field rounding precision (e.g. 2 dp vs 4 dp).
- What is persisted vs computed-on-read, and when each field is set (e.g. a value added by a finalizer after the main compute).
4.3 Edge behaviour β clamps, coercion, indexing (silently glossed = silently wrong)¶
- Every clamp with its exact condition β result ("negative β
None"; ">0 gate"; int/float coercion). "β¦and then it's clamped" is not enough β state the clamp. - Indexing conventions: 0- vs 1-based, per structure (a confidently-wrong source).
- Null / empty / missing handling, per field.
4.4 Integration & host contract (HA-bound = collapses)¶
- Entry-point / constructor signatures with all arguments (the ~14-kwarg payload builder is a rebuild-blocker if absent).
- The host contract: what is injected vs imported β the seam a rebuild must
reproduce. Write it as an explicit contract (cf. learning's
LearningHost/BrandFactsProtocols). - Persistence mechanics: atomic write (temp-file β rename), cache lifecycle.
4.5 Brand / variant dependence (the leak that hides bugs)¶
- Flag explicitly any behaviour that differs by brand / model / variant. The
clean_timesclamp (Eufy caps 2 passes, Roborock allows 1β3) was undocumented β and was a real bug, not just a doc gap. - Mark which values are observed-data passthrough vs capability-branched.
4.6 Provenance & location¶
- Where each field originates and when it is set (post-compute additions).
- A function/method's location when it matters (module-level vs method β a confidently-wrong source once).
5. Meta-rules (as important as the checklist)¶
- Never be confidently wrong. A precise-but-unverified statement is worse than silence β it misleads the rebuild. If you have not verified it against source, mark it unverified or omit it. Hedge, don't harden.
- A reconstruction that disagrees with the doc/code is a BUG SIGNAL. When a
reasonable rebuild contradicts what's written, the code may be wrong β the
clean_timesEufy-ism surfaced exactly this way (the "no clamp" guess was closer to correct than the buggy clamp). Investigate the disagreement; don't just patch the doc. - This depth is intentional β do not trim it as "over-documentation." The clamps, exact columns, and kwarg lists are the load-bearing parts; a normal doc glosses precisely what reconstruction needs. Guard them against future cleanup passes.
- No normative collisions β amendments edit the superseded text in place, never merely append an override. Two authoritative statements, each individually followable and jointly unsatisfiable, are a defect class of their own. This corpus's readers are AGENTS: a human resolves "newer/specific wins" by common sense, but a retrieval-based reader may load either statement without its sibling and follow it faithfully β the collision becomes nondeterministic behaviour, not ambiguity. One live statement per rule; history lives in git and the audit record, exactly as with code. "Readable" and "agent-executable" are different standards; a discovered collision is fixed before the doc is consumed by an agent.
- Canonical text is never evidence that the author paid attention. Names, dates, attributions, "as discussed", discovery stories, and callbacks to a conversation are metadata about the author, not information about the system β the same distinction the coupling rule draws for implementations. Attention is demonstrated in the commit message, the manifest, the review response, and the audit record; the artifact demonstrates attention by being correct. The filter for any phrase: would it still serve a competent reader who has never seen the project's conversations and does not care who discovered the rule? If not, it belongs in the audit record.
- A doc that no index reaches is a doc the corpus does not have β check the
index when you commit docs. An index fails by OMISSION, and omission is
invisible in prose: a missing table row leaves a hole you can see, a missing
clause reads as a complete sentence. The whole of
design/was unreachable from the reading order until 2026-08-15 β five files named in backticks inside one bullet, none of them linked, two of them created that same week. Under the Β§0 availability contract an unreachable doc is worse than a missing one: the corpus claims completeness it does not have. Runpython scripts/check_docs_index.py(naming a file in backticks does not count as reaching it).
This is a doc-commit rule, NOT a CI gate β deliberately. Wiring it into
tests.ymlwould fail every push that adds a doc before the doc pass runs, which is exactly the friction the 2026-06-12 ruling keepsscripts/check_legend_drift.pymanual to avoid. It fires where the cost is already being paid: when docs are committed, and at the release doc pass.
6. Acceptance test¶
- Full (measured): a blind agent rebuilds the module from the doc alone; diff vs
source; classify each miss
DOC_GAPvsAGENT_MISS; theDOC_GAPs are the doc's failures. Re-verify each proposed doc fix against source before applying (an eval's "codeTruth" is ~90% reliable, not 100% β and the mismatch may be a code bug, per Β§5.2). - Pragmatic (per-doc walk): run Β§4 against the doc. Audit Β§4.2βΒ§4.4 hardest β that's where docs collapse; Β§4.1 usually survives. A doc passes when nothing in the collapse zones is missing, hand-wavy, or unverified.
7. Subsystem status¶
DR-grade is earned per subsystem, one at a time. Track it here.
| Subsystem | Doc | DR-grade? |
|---|---|---|
| Battery | 12-battery-system.md |
β ~90% (exemplar) |
| Learning | 10-learning-system.md |
β
re-audited 2026-07-28 against this rubric β 12 collapse-zone gaps closed (full completed_job schema Β§2.0, room-stats/baseline field sets, jobs_index fields, hass.data caches, idle-wall blocker Β§3.2b, finalizer/host-contract drift). The audit also surfaced a real code bug (cancel reason dropped from the persisted learning_blockers) β fixed + regression-tested, per Β§5.2. |
| Job Lifecycle | 06-job-lifecycle.md |
β
audited 2026-07-28 (2 waves, ~20 findings) β added the active_job + incomplete_run.json + phase-record schemas and the full lifecycle signatures / return shapes; fixed a confidently-wrong cancel threshold and the last_* live keys; stripped 25 stale line-refs. Also surfaced a behavior asymmetry (EVENT_RUN_INCOMPLETE fires only on the service finalize path) β documented + flagged for a product decision, per Β§5.2. |
| Queue Engine | 07-queue-engine.md |
β
audited 2026-07-28 (14 findings) β fixed confidently-wrong clean_intensity values (Quick/Narrow/Deep; Standard is dead) + profile-resolution precedence (per-room fields always win) + the zone-in-gate drift + zone-phase timing; added the exact wire-payload / resolved_rooms schemas, dispatch-engine registry + signatures, capability-gate mapping, and map_id cast/omission. Two code smells flagged for a decision (unclamped clean_times on the Eufy path; stale queue_engine TypedDicts). |
| External-Run Ingestion | 28-external-run-ingestion.md |
β
audited 2026-07-28 (9 findings + 2 code flags) β added the full external-ingestion adapter contract (Β§9: external_mid_run_statuses, job_segmenter, room_attribution, capture prereqs), corrected Β§10 file layout (ExternalRunManager in learning/external_run.py, not core/manager.py), documented the pose-only pending-record variant (Β§4), the sanitized filename chain, the <4-sample cold-start gate, and the exact service returns. Β§5b reconcile + Β§6 gate verified consistent with 10/06. Two low-confidence code smells flagged (resegment return lacks resegmentable; pose-only records lack attribution_confidence). |
| Phase Runner | 30-phase-runner.md |
β
audited 2026-07-28 (10 findings) β the room_group/stop machine was already solid; fixed the zone-phase cluster: the undocumented zone verify branch (Β§6.4 β sustained cleaning-state, else the phase locks ACTIVE forever), the zone dispatch early-return (Β§6.3), the zones-rects-at-build-time serialization (Β§3.1 β was "at dispatch time"), the zone_timing wall-start basis (Β§5.5 β prev phase, not job start), + zone-forces-strict-order and the queue-breaks step source. One code-behavior asymmetry documented + flagged (charge-timeout deadline restarts fresh on re-arm vs wait recomputes). |
| Room Rules | 09-room-rules-system.md |
β
audited 2026-07-28 (12 findings + 3 code flags) β operators/categories/fan-out/wire-format were solid; fixed the serialized-output collapse: full preflight schema + blocked/modified entry schemas, the _build_effective_start_plan return shape (payload = phases[0], NOT build_room_clean_payload), the mid-job report schema + its last_path_block_signature mutation (doc said "does not mutate"), the Β§9 operator-allowlist step (a new op is coerced to equals without it), protected_room_config nullifying modifier water/edge, Β§6 rounding/guards/reason ladder. Flagged: stale TypedDicts, unguarded int(room_id), backend clean_passes unclamped (mitigated by the 07 wire clamp). |
| Rooms | 08-rooms-system.md |
β
audited 2026-07-28 (10 findings + 3 code bugs) β the serialization collapse: documented the THREE divergent room-record writers (save / rebuild / load-time backfill) + the load-time normalization (setdefaults + carpetβcarpet_<pile> migration) that reconciles them, the exact defaults, the reconciliation output shapes, the 9-key summary entry (vs rebuild's reduced 4-key), the source_refresh cache key/shape, the discovery cache key, and the discovery skip filters. Fixed confidently-wrong clean_intensity/fan_speed examples (Quick/Max, not Standard). Flagged BUG-A (HIGH): rebuild_map permanently strips is_configured/configured_at β rebuilt rooms read unconfigured and are filtered from entity creation. |
| Error Tracker | 23-error-tracker.md |
β
ABLATION-CLOSED 2026-08-07 β the first section to earn its size experimentally (CAL-23; the DR prose-ablation protocol and its ledger live in the repo-local audit record, not on this site). 475β415 lines (β13%) with one invariant ADDED, and the replacement is the doc a 2-of-2 fresh blind builders rebuilt the module from β 8/8 behavioral tests + 9/9 certified pins, twice. This row's grade now means demonstrated sufficient, not reviewed and believed. Earned invariant #1 (error_label_key returns a label only for a non-empty string entry; any other stored value resolves to None exactly as an absent one does β ledger entry 1). Pre-build, the trim also caught a confidently-wrong claim that had survived two truth passes: Β§7.2 documented the deprecated harvest_active_run as the live finalizer wiring, when the real design is the peek/commit pair. Apparatus caveat, per the protocol's own suspension rule: the legacy suite was 81% white-box (35/43 tests assert private names and reject any correct reimplementation), so certification rests on 8 behavioral tests + 9 tester-authored public-contract pins; the 35 are handed to test-hardening as over-pinned. Earlier: β
audited 2026-07-28 (12 findings + 1 code bug) β record shapes / extraction / API were solid; fixed confidently-wrong: the not-error sentinel set is replaced by the adapter's (not merged), the secondary predicate strip().lower()s ("Error"β"error"), the rising edge fires on any error value (not not_errorβerror). Added the harvestβfinalizer injection + extra_outcome/total_error_seconds contract, unregister_vacuum, persistence mechanics. Flagged Gap 7 (code bug): _on_grace_expired uses the generic sentinel set not the adapter's β a stuck event whose error_message reads a brand sentinel (Eufy "none"/"normal") is silently dropped. |
| Setup System | 15-setup-system.md |
β
audited 2026-07-28 (17 findings + 4 code flags, docs-only) β the algorithm/rule surface (step ids, thresholds, protection-level derivation, delete steps) was DR-grade; the collapse was serialization + provenance + service wiring. Added the 5-field drift-history entry schema (Β§4.5/Β§8 β first_seen_at was entirely absent), compute_room_drift's history-only branch (the one the panel actually runs, Β§4.5), the full update_drift_history mutations (seen_passes resets to 0 on a miss; first_missed_at clears on sighting), Β§4.7 the is_configured/configured_at provenance + _migrate_setup_progress + active_map_configured sticky save_rooms re-open, the migrated_at divergent record, Β§9 the service-layer step-advance gate (status β {success, already_done} + the async_reload side-effect + setup_set_map_camera), the import/discovery/delete literal shapes + code enum, and the cadence coercion asymmetry (or loses a 0, is not None keeps it). Fixed confidently-wrong Β§3.2: import calls save_managed_roomsβbuild_managed_rooms, never rebuild_map_bucket. FIXED CS-2: get_discovery_cadence now honors an explicit low confirmation-pass count (is not None guard, not or) and floors 0/negative to 1 β a literal 0 would make missing_passes >= n_remove a tautology (every configured room flagged removed); +regression test DR-15, full suite 2891 green. Still flagged (not fixed): CS-1 (migrated_at write-only/dead), CS-3 (load-path is_configured setdefault gap β mitigated by the BUG-A fix), CS-4 (4-key vs 5-key divergent setup_progress writers). |
| Architecture Overview | 01 | β
audited 2026-07-29 (aggregator tier; SHAPE doc β lighter treatment) β the layer diagram, the 14-subsystem inventory with all post-extraction bundled-subsystem paths (dispatch/, live_refresh/, external_run, phase_runner, map_source), and the startup order were verified current. Fixed one HIGH confidently-wrong structural defect spanning Β§3/Β§4/Β§10 (5 platforms β 6 β the live select debug-target platform, which Β§2's own diagram already listed β the doc self-contradicted; same defect as 02), added debug.py to the Β§9 services table, dropped a ~90-line-stale battery line-ref, and filled the rooms//setup/ package descriptions. No code flags. |
| Listeners | 04-listeners.md |
β
audited 2026-07-29 (aggregator tier; 5 HIGH + 7 MED + 4 LOW + 3 code flags, docs-only) β the ordering-plan bet held for lifecycle/path-blockers/discovery/job-progress but broke for the two modules that absorbed real feature growth: pose_sampler (dual-source attribution + dispatched-run sampling) and pause_timeout (the FN-1 stranded reaper). Fixed confidently-wrong: Β§6 was missing the entire second reap (stranded-started β async_finalize_stranded_job, fires job_finished+run_incomplete β the landed B1 "if it strands it is incomplete"); Β§10 "external runs only" (also samples started) and "single live_pose source" (also native_current_room/Roborock); Β§9 vacuum_docked watches the vacuum entity's literal "docked" (edge-guarded), not dock_status vocab; Β§7 progress-tick payload is {ids} only, not the snapshot (matches 02). Added the run_incomplete_event_data helper + 11-key job_finished shape, the path-blocker any-change trigger + report-dict payload (09), the dock dedup+save, the job-metrics unit conversions (60Γ guard, ftΒ²βmΒ²), the completed_finalize_signals 4-key subset, and cadence/vocab edge fixes. Flagged CS-1 (job_metrics stale 3-tuple comment), CS-2 (dead "int" branch), CS-3 (always-on 6h ticker). |
| HA Integration | 02 | β
audited 2026-07-29 (opens the AGGREGATOR tier; 10 findings + 3 code flags, docs-only + 1 comment fix) β the ordering-plan bet held: 8 of 10 gaps were in the Β§7 event table, 6 of them 02-vs-06 drifts where 06 (hardened this campaign) already had the source-correct payload β so the fix was mostly "align Β§7 to 06 Β§10". Fixed confidently-wrong: 5 platforms β 6 (the live select debug-target platform was omitted), and "nine events / all in const.py" β ten (EVENT_ROOM_COMPLETED, defined in mapping/tracker.py, was undocumented). Aligned the event payloads to 06 (EVENT_ROOM_STARTED +completed_room_ids, EVENT_ROOM_FINISHED native-signal omits confidence, EVENT_JOB_FINISHED two shapes, EVENT_RUN_INCOMPLETE 5 fire sites, EVENT_PATH_BLOCKED = report-dict+augments), fixed EVENT_EXTERNAL_RUN_PENDING's fire site (learning/external_run.py not core/manager.py), added the async_remove_config_entry_device host hook, and the STORAGE_KEY provenance. Fixed CS-3 (the stale __init__.py "select platform was dropped" comment that seeded the doc error). Flagged CS-1 (DATA_SERVICES_REGISTERED dead const), CS-2 (EVENT_JOB_FINISHED payload asymmetry β same known 06 divergence). |
| Core Manager | 05-core-manager.md |
β
audited 2026-07-29 (aggregator tier; 1 HIGH + 3 MED + 4 LOW, docs-only, no code flags) β surfaced UNAUDITED during the docs/dev reorg. The owns-vs-delegates map (the #1 risk for an orchestrator doc) verified fully current: all six extraction delegators (dispatch/phase_runner/run_plan/external_run/map_source/live_room_refresh) correctly labeled delegators, every method the doc calls "owned" genuinely owned, Β§2 construction order + Β§3 constructor table byte-exact. The collapse was one manager-OWNED return shape + provenance rot. Fixed confidently-wrong Β§6 get_dashboard_snapshot "aggregates" list (claimed a managed-vacuums list + payload state + a top-level dock key β the first two are the SEPARATE get_managed_vacuums/get_payload_state; it is really a per-vacuum ~36-key read-model) and update_room_fields "and persists" (it's sync + in-memory, no async_save β the save is the service layer's, contradicting the doc's own Β§3). Added the Β§2 _phase_dispatch_pending guard-clear step, the awaiting_bounds_exit path-optimizing-brand suppression (honors_clean_order is False β Roborock), cross-refs to frontend/backend-contract-and-data-shapes for both snapshot shapes, fixed 3 stale color-sentinel line-refs (1233β1261 / 55β53 / 1291-1295β1319-1323) + the resolve_active_map_id typo, and qualified Β§7 "never reads disk" (the room-history cache still lazy-loads learning-history files). No code bugs β the code is correct, 05 was the stale party. |
| Data Model | 03 | β
audited 2026-07-29 (aggregator tier; 4 HIGH + 3 MED + 2 LOW, docs-only, no code flags) β the LAST backend doc; closes the AGGREGATOR TIER + the whole backend. The inlined OBJECT shapes were largely byte-faithful (hold-the-line: the default active-job state Β§5, the payload/ResolvedRoom capability-gating Β§4, the learning record Β§9), so the collapse was 03's #1 job as an INDEX. Added FOUR genuinely-persisted top-level keys the inventory omitted β battery ({"vacuums":{β¦}}, battery/manager.py:284), adapters (config_loader.py:78), learning_processing_enabled + learning_pending_runs (manager.py:319-320, also missing from the key-seeding prose) β plus analytics for completeness; THREE co-resident MapBucket keys (saved_zones/learned_zones/queue_breaks); and the _pending_run_steps transient staging key. Fixed confidently-wrong: Β§2a clean_intensity ("Standard"/"Intense" β Quick/Narrow/Deep, Standard dead β the same 07/08 fix reaching the data-model doc) and Β§1 RoomSelectionSummary (documented only {enabled_count,disabled_count} β the real 4-key shape with the 9-key SummaryEntry lists + the rebuild_map_bucket 4-key REDUCED divergence = doc-17 BUG-B). Β§6a room-profile record β 9 keys (added mop_required, path_type always-present str, cross-ref 16 Β§3.1); MapMetadata gained reconciled_at/reconciliation_dismissed_at/last_rebuild. No NEW code flags (surfaced smells β 17 BUG-B divergent summary writer, 15's write-only migrated_at, _pending_run_steps living in the durable dict β are all already tracked). Deferred: L3 migrated_at in Β§12 (LOW, owned by 15's CS). BACKEND COMPLETE β only FRONTEND remains. |
| Adapter Config Reference | 22-adapter-config-reference.md |
β
audited 2026-07-29 (2099-line schema diff; docs-only + 2 schema-desc fixes) β all confidently-wrong fixed (the worst category): Β§13a.2/Β§13a.3 "Roborock omits map_state_source/map_render" (it declares both β memory backend + roborock_raw_map_v1), live_transition "no schema entry" Γ3 (it IS in the schema), Β§17 water_rates absent-behavior (flat 4.0, not the Eufy table; Eufy declares it), Β§17a wash_frequency_bounds absent (1.0/1440.0, not Eufy 15/25), Β§6 cancel_detection_states type (str \| list). Added require_job_active_clear + job_active/mop_active, params_as_list/passes_is_global + the passes_max 2-vs-3. Fixed 2 SCHEMA bugs (config_schema.py, doc was right): CS-A (charging desc claimed a substring fallback core/charging.py removed), CS-B (stale canonical wash-mode keys β by_room/by_time/off); 335 schema/adapter tests green. Additive fills also completed (Β§14c external_mid_run_statuses, Β§14d cleaning_time_unit, the Β§14 zone caps, the full Roborock dispatch field set, guide_translations, maintenance_only/remaining_is_state, implicit_map_id, room_attribution.source, Β§3 glance) + fixed a recurring stale get_dashboard_snapshot line-ref. ADAPTERS TIER COMPLETE. Still flagged: CS-C (passes_max split 2-vs-3 defaults), CS-D (schema-vs-runtime drift). |
| Eufy Adapter | 25-eufy-adapter.md |
β
audited 2026-07-29 (adapters swarm; docs-only) β value-level blocks were largely DR-grade; collapse in Β§3 provenance + unenumerated Eufy value-sets. Fixed confidently-wrong Β§3 model source (device-registry primary, detected_model attribute fallback β the scalar/Tuya fix, same as 21). Added the full capability_hints family-sets + has_attribute_rooms, the model-family catalog (7 hints + 22 T-codes), the 5 alias maps (incl. standard/normalβquick), the verbatim blocked_* sets, the Eufy per-side zone caps, room_attribution.source/tuning, guide_translations/upkeep_guides_i18n, and several scalar fills. Flagged CS-1 (HA_ACTIVE_VACUUM_STATES dead import + core hardcodes the value it should read from the adapter). |
| Eufy Segmentor | 26-eufy-segmentor.md |
β
audited 2026-07-29 (adapters swarm; docs-only) β companion to the DR-hardened 11 Β§2; its own scope (engine Protocol, degraded taxonomy, envelope, porting) was near-DR-grade. Added the undocumented emit-time keep/drop gate cascade (Β§5.1 β undocumented in both 26 and 11; the reject-reason list + _component_should_keep thresholds), fixed the raw-detector-vs-SegmentationResult conflation (Β§3, cross-ref 11 Β§2.12), the stale "trace-based tracking" β native current-room (Β§1/Β§10.5), the no_image_path degraded reason, and Β§8/Β§6 cross-refs. Cross-doc: corrected 11 Β§2.6 "7-level" β "8-level (0β7)" bins (the swarm caught 11 was the wrong one). Flagged CS-1 (_split_suspicious_component annotated 2-tuple but returns 3-tuple), CS-3 (NoopSegmenter docstring stale), CS-4 (dead slices). |
| Roborock Adapter | 29-roborock-adapter.md |
β
audited 2026-07-29 (adapters swarm; docs-only) β dispatch/map-render/completion/settable-mop were DR-grade. Fixed confidently-wrong Β§2 ("no upkeep modules" β they exist, fully wired with a 13-language guide library) and the get_dashboard_snapshot line ref (3376-3389β3949-3963) + stale Map-Bounds-tab framing (derived-but-unconsumed since the mapping split). Added the load-bearing cleaning_time_unit: "min" (a silent 60Γ learning-corruption fix), rooms_unique_per_job: False (the revisit guard), 4 missing entity keys, the error_tracking/room_attribution/charging/map_state_source blocks, the exact discovery keys, the 12-entry maintenance count, and the zone min. Flagged CS-1 (remaining_is_state unconsumed β confirms 13's finding), CS-2 (guide_translations "empty today" comment stale), CS-3 (supports_map_bounds unconsumed). |
| Adapter System | 21-adapter-system.md |
β
audited 2026-07-28 (opens the ADAPTERS tier; 10 findings + 2 code flags, docs-only) β the registry/seam/loader half was DR-grade; collapse in the capability model (Β§3.5) + assembly provenance (Β§5.2), plus subset-presented-as-complete claims. Fixed confidently-wrong: the 9 supports_* flags are not pure entity-probes β 5 are hint-OR-presence (model-family hints β True even with the entity absent), supports_water_control is never probed (= supports_mop_features), supports_edge_mopping/passes/custom_room_config/room_clean are hardcoded True; and the model source is the device-registry model (primary), the detected_model attribute only a fallback (the scalar/Tuya fix). Added: framework-read entity keys beyond the schema's 18 (esp. job_active, referenced by the completion schema), the 4 required blocks, the schema-absent model_family/capability_hints, maintenance_only, the 5 omitted Roborock dispatch fields + map_id_type default "str", the per-side-vs-per-area zone caps, build_entity_id's strategy kwarg, and fixed ~300-line-stale refs. Flagged CS-1 (validator enforces no required fields β doc already noted), CS-2 (schema entity-keyset drift = root of the job_active gap). Chris's "adapters won't be that bad" β borne out: lighter than the managers, docs-only, no code fixes needed. |
| Maintenance Manager | 13-maintenance-manager.md |
β
audited 2026-07-28 (parallel swarm; 4 HIGH + 5 MED + 5 LOW + 5 code flags, docs-only) β fixed confidently-wrong replacement_status (real signature remaining_percent not state_value, thresholds β€5/β€10/β€15 not β€5/β€15/β€30, input is derived %-of-life not raw state β the issue-#38 refactor the doc never absorbed), max_interval_hours "enforced at write" (it's card-side only; the service enforces min=0 no max, the Number entity clamps to framework 1.0β500.0), and sensor_suffix "None when proxy_for" (they coexist). Added the maintenance_only + family-gate render rules (Β§4.3), the Eufy-vs-Roborock brand divergence (Β§4.4 β Roborock replacement rows read "unknown", remaining_is_state declared-but-unconsumed), the full 24-key replacement-item / 26-key maintenance-item schemas + model_meta/guide shapes, set_maintenance_interval + Number/Sensor entry points, and the service-layer persistence. FIXED CS-1 (reset now preserves the user's interval_hours override instead of wiping it; +regression MNT-7b, full suite 2892 green). Still flagged: CS-2 (remaining_is_state dormant), CS-3 (no backend max enforcement). |
| Dock Manager | 14-dock-manager.md |
β
audited 2026-07-28 (parallel swarm; 1 HIGH + 4 MED + 4 LOW + 3 code flags, docs-only) β the gating/storage core was DR-grade. Fixed confidently-wrong Β§5.2 token fallback (code scans button.{object_id}_-prefixed registry entities and matches by substring, not "all button.*" + split-on-_ β the doc described safer behavior the code doesn't implement, CS-1). Added the 4 missing *_label/lifecycle_message return keys (Β§6.1), the exact 9-key blocked / 10-key success dispatch shapes (Β§7), the service-layer map_id auto-resolution + ServiceValidationError + supports_response (Β§7.1), the undocumented get_dock_action_entities (Β§5.3), the full capabilityβaction map, and the dock_event/learning/diagnostics consumers. Correctly excludes the dock anchor (owned by 11/17). |
| Onboarding Manager | 18-onboarding-manager.md |
β
audited 2026-07-28 (parallel swarm; 2 HIGH + 2 MED + 2 LOW + 4 code flags, docs-only) β the storage/return/predicate surface was already DR-grade; the gaps were confidently-wrong integration claims. Fixed: confirm_floor_type has no panel/service path β it's a bulk auto-confirm over every room on every save_managed_rooms (not per-room, not initial-import-only); the Β§6 "Panel β get_onboarding_state" seam doesn't exist (real seams: the get_start_status embedding + the onboarding diagnostic sensor). Documented the start gate keys on floor_types_complete alone (not full onboarding_complete; zero-room maps pass vacuously). Flagged CS-3: the floor-type "review gate" is self-satisfying β the code never enforces a human floor-type review (product decision; ties to the deconstruction B3 onboarding-as-gate work). Plus CS-1/CS-2 (dead *_notified + write-only room_count_at_last_check), CS-4 (enabled-vs-is_configured cross-gate mismatch). |
| Profile Manager | 16-profile-manager.md |
β
audited 2026-07-28 (9 findings + 6 code flags) β built-ins/ID/CRUD/capability-gate were DR-grade; the shaper + run-profile serialization had gaps. Added Β§6.2 the full 12-key get_effective_room_details output (with the renamed default_clean_passes/default_edge_mopping + selected_vsresolved_profile_name traps), Β§6.3 the resolution precedence ladder (carpet overrides room fan/water; "room always wins" is only true for unconstrained hard-floor fields), Β§3.1 the 9-key stored room-profile record (path_type/mop_required always-defaulted), Β§5.5 apply_room_profile, Β§7.7 the start_run_profile signature/returns, Β§7.2 snapshot coercions + the -1 sentinel, and the from_room return shapes/reasons. Fixed confidently-wrong: "steps written only by set_run_profile_steps" (also written by save_run_profile when the queue has_breaks), and _match_profile_from_fields "exactly match" (it's a normalized match vs a bare-{profile_name} candidate). FIXED B1 (HIGH, real bug): a plain vacuum room never matched its vacuum preset β always profile_name="custom" (protected room forces water Off; candidate resolved to hardwood water default Low) β corroborated by the PM-10 test comment. Fix: _match_profile_from_fields now resolves + protects each candidate under the room's floor_type (symmetric pipeline); +regression test PM-10b, full suite 2889 green. B2βB6 also resolved: B2 (custom profiles now derive path_type Deepβnarrow + mop_required from mode, +test PM-2b), B3 (ProfileRecord TypedDict corrected to the real 9 keys), B4 (apply_room_profile_to_config now threads catalog into alias resolution), B5 (apply_room_profile id filter cleaned via _safe_int; the *_from_room int(room_id) is service-Coerced so unreachable), B6 (documented benign: dispatch reads the persisted path_type, no re-sync needed). Full suite 2890 green. |
| Map Manager | 17-map-manager.md |
β
audited 2026-07-28 (8 findings + 4 code flags) β the pure-function signatures/return shapes (Β§3.1β3.5) were DR-grade; the persisted room-record + summary serialization collapsed. Added the full 22-field rebuilt room-record schema to Β§3.4 (exact types/defaults/coercions: profile_name="vacuum_quick", fan_speed="Max", floor_type="hardwood", path_type no-coercion, list-guarded grants_access_to/rules) + the discovered_rooms input contract (room_id/name required, per-element map_id ignored β param wins) + both summary entry shapes. Flagged BUG-B in-doc: rebuild_map_bucket is the lone writer of the reduced 4-key summary entry vs the canonical 9-key build_room_selection_summary (drops profile_name/floor_type/clean_passes/edge_mopping/carpet until a non-rebuild write repopulates). Fixed confidently-wrong get_managed_maps_summary() β get_vacuum_maps() (Β§5). Added the is_configured/configured_at provenance (entity-creation gate, BUG-A carry-forward), the co-resident queue_breaks/learned_zones keys + the reconciled_at/reconciliation_dismissed_at metadata keys, and the stale image_variants set (per-layout custom_<id> + furnished-art). Code flags: CS-2 (BUG-B fix = call the 9-key builder), CS-3 (is_transition not in RoomConfig), CS-4 (configured_at None-vs-_iso_now() asymmetry), CS-5 (possibly-dead core/manager.py imports). |
| Mapping | 11-mapping-system.md |
β
audited 2026-07-28 (11 findings + 3 code flags) β algorithm (Β§2.2β2.9 masks/clustering/splitting) was pristine; the CV/custom serialization collapsed. Added Β§2.12: the full ~27-field per-segment dict (exact rounding, segment_id="segment_{N}", area_percent 0β1/4dp, value sets for structural_role/segmentation_state/variant_support/edit_readiness with thresholds) + the stored image_segments envelope (_reshape hoists segmentation/runtime under engine_diagnostics, adds engine/analyzed_at) + the keyword-only detector signature. Fixed confidently-wrong: custom segments are not the identical CV shape β _build_custom_segment is reduced (omits ~10 metrics, area_percent 0β100/2dp, center_pixel 1dp, adds source), and the stored custom_segments envelope is reduced too (Β§10.4). Pinned the polygon_pct divisor to the store's own dims (Β§2.10), the adjust-time bbox/center_pixel rewrites + the map-bucket-vs-active-scope scope caveat (Β§5.2), the dead "primary" path clause (Β§6.1), and the two extra coordinator readers (Β§11.2). Flagged 3 code smells: stale canonical SegmentationState/EditReadiness Literals (don't match runtime values), the updated.get("id") adjust-log that always reads "unknown", and the latent cross-scope adjustment collision. |
| (all others) | β | β¬ reference-grade β pending audit against this standard |
Harden one subsystem per pass: walk Β§4/Β§5, fix the collapse zones, update this row.