Architecture hardening, and the Predict delta
v0.8.0 was called "Predict" and its forecast never reached a surface. A blind review across
eight independent tracks found it after the fact β the headline model existed, was tested, and
was reachable by nothing a person could see. That gap became
PRD-0009,
and eight of its nine ratified requirements ship here.
The rest of the release went after the seams underneath β and kept finding the same defect, five
separate times: a layer correct about the question it asks, and silent about the one nobody wired.
Your plants now say one thing, in one place
Every surface used to reach its own conclusion about a plant. Now there is one composed state β
and the card, the queue, and the chart all read from it rather than re-deriving it.
- The attention model decides once what a plant needs. The harm level fires on plants trending
badly, not only on broken instruments; the calm signal is affirmative and time-bound; a plant that
was just watered is held rather than re-nagged. - An untrustworthy reading predicts nothing. A sensor we don't believe stops producing forecasts
instead of producing confident ones. - The queue is ordered by forecast, not by wetness β and it is the same ordering the card
shows, asserted in code so the two cannot drift apart. - The forecast appears on the history chart, with an unmistakable boundary between what was
measured and what is predicted, and a confidence vocabulary where the interval is the how-sure.
Adding a board no longer requires knowing how Sprout works
The whole path, in the app: describe your hardware β map probes to plants β flash β it reports.
No JSON, no config files, no command line. You declare the board before flashing it, and Sprout
keeps what you set up when the board first speaks. A board that answers but isn't registered is
offered for adoption rather than ignored.
Things that were quietly wrong, and aren't
- The health strip stopped crying wolf. Every sensor was drifting toward "inspect" β not because
the hardware was failing, but because three signals counted any single event, ever over unbounded
history. Older boards scored worse purely for being older. Now they are rates, dropout cadence is
measured locally instead of pooled across logging regimes, and an instrument with no readings gets
no verdict rather than a clean bill of health. - The zero state names the real state instead of waiting forever; the serial lock says who holds
the port and what to do about it; Ctrl-C exits clean;just doctorturned a wall of output into a
checklist. - Charts say when they're frozen rather than going silently stale, and the wide-window build is
twice as fast.
Under the hood
- The package flip:
toolsis importable by name and 227 lines ofsys.pathsurgery are gone,
with byte-identical output. - Release integrity, walked rather than assumed. A dry-run of the entire signing chain β draft,
sign, verify, publish, resolve, delete β on a throwaway tag found a cut-blocking defect on its
first run: the signer could not resolve a draft, and would have produced a second asset-less
release with an error blaming the operator for a draft that already existed. - Signed-pull OTA reached hardware, dark by construction. The feed fails closed.
The record, corrected
v0.8.0's entry was missing from the changelog entirely for four days while the file still
announced it as the next cycle. It is now written, honestly: that release shipped its foundations
and not its name, and it published with zero assets because the signer ran after publish and
GitHub refused the upload to an immutable release.
Releases now carry a theme-conformance gate β a themed release is not closed until someone
asks whether the name became true on the operator's surface.
Known scope, carried rather than hidden
- The health analysis still reads pre-production bench rows; lab sessions are unmarked on the
timeline (#1634,
#1635). - The version guard doesn't inspect
uv.lock, and doesn't check dates at all β both found during
this cut (#1633,
#1637).
Per-component detail is in
CHANGELOG.md.
Full generated commit list
What's Changed
β¨ Features
- feat(board): just-wrappers for the five board fields (#1443) by @OrangePeachPink in #1444
- feat(flash): the alpha channel from the CLI β just flash-alpha (#1334) by @OrangePeachPink in #1482
π Bug fixes
- fix(release): sign into the draft, publish seals it (#1438) by @OrangePeachPink in #1459
- dx(lint): epic-hygiene discovers epics structurally, not by the retiring label (#1446) by @OrangePeachPink in #1473
- fix(board): read renders empty fields as β and never crashes (#1447) by @OrangePeachPink in #1469
- fix(ci): experimental-board job is non-blocking on EVERY step, not just compile (#1439) by @OrangePeachPink in #1476
π Documentation
- docs(adr-0003): the v0.8.0 process standardization β the ratified retro package by @OrangePeachPink in #1441
- docs(team): the D pass β OPERATIONS + CONTRIBUTING sync to tonight's ratified process by @OrangePeachPink in #1450
π§ Other changes
- docs(team): the design lane is named Design β one name, board and relays alike by @OrangePeachPink in #1440
- docs(adr-0027): Β§6 calibration portability RESOLVED β portable on a per-board transfer (#621) by @OrangePeachPink in #1448
- data(identity): #1454 β the shared resolved-identity join, the S1-seam closure by @OrangePeachPink in #1461
- fix(logger): #1428 β a fleet-logger refusal is not a crash, and its words survive by @OrangePeachPink in #1465
- docs(adr-0003): the new-ADR justification gate + harvested antipatterns (#1462) by @OrangePeachPink in #1464
- feat(telemetry): #1434 AC0 β emit the signed classifier step so rate_spike is auditable from the wire by @OrangePeachPink in #1463
- fix(tier): #1466 β the launcher fills the store, not only compacts it by @OrangePeachPink in #1471
- docs(adr-0000): 0012/0013 are satellites of 0006, not a merge (#1462) by @OrangePeachPink in #1472
- docs(adr-0035): fold ten amendments into one band model + a changelog (#1462) by @OrangePeachPink in #1467
- feat(registry): #1188 β a location edit reads as the move it is by @OrangePeachPink in #1470
- fix(home): #1432 β one physical channel is one card across the v5 flash by @OrangePeachPink in #1475
- feat(ota): S3 pull transport orchestrator β fetch β parse β decide β gated apply (#1284) by @OrangePeachPink in #1474
- fix(trial): #1435 β the trial says WHY it is empty, never a false 'no readings' by @OrangePeachPink in #1477
- docs(bench): safety-gate run sheet β on-device proof for #93 watchdog + fail-safe (#93) by @OrangePeachPink in #1480
- perf(classic): #1457 β halve the wide-window build (classify once, not twice) by @OrangePeachPink in #1484
- fix(cal): land the C5's measured board-level rails so open_adc can fire (#1433) by @OrangePeachPink in #1490
- perf(classic): #1456 β RSS-per-request instrument + a session-stability guard by @OrangePeachPink in #1487
- fix(provenance): #1430 β label the host CSV schema for what it is by @OrangePeachPink in #1489
- fix(classic): #1455 β the charts say when they're frozen, never silently old by @OrangePeachPink in #1486
- feat(registry): #1027 Β§5.1 β the calm discovery set (answering-but-unregistered boards) by @OrangePeachPink in #1491
- feat(registry): #1188 AC2 β structured placement on the temporal move (ADR-0029) by @OrangePeachPink in #1492
- feat(ota): S3 pull hardware bindings β real fetch_feed/esp_ota, dark by construction (#1284 AC4) by @OrangePeachPink in #1488
- docs(security): correct the ArduinoOTA claim in SECURITY.md β public builds arm no OTA receiver (Refs #1478) by @OrangePeachPink in #1483
- docs(release): the sign-the-draft-before-publish guard β v0.8.0 asset-less, in the playbook (#1346, #1438) by @OrangePeachPink in #1493
- docs(fd-3): Sprout-side of the search deploy β Person
@idmirror + bench run sheet (Refs #1232) by @OrangePeachPink in #1495 - docs(brand): #1485 β SERP audit + search-cache blind-spot class by @OrangePeachPink in #1504
- docs(brand): #1496 β re-voice the discovery surfaces off the 'honest' wedge by @OrangePeachPink in #1499
- feat(starter): #1494 ch.1 β the sketch speaks Sprout's real mood words by @OrangePeachPink in #1505
- voice(vocabulary): #1506 β the collective is a greenhouse, not a fleet by @OrangePeachPink in #1509
- docs(bench): pump/relay, signed-pull, and yyvvpd-recovery run sheets (#1451 #1284 #680) by @OrangePeachPink in #1498
- docs(fd-6): front-door launch-session runbook for the v0.8.1 bench (#1069, #1076, #1232, #1508) by @OrangePeachPink in #1511
- docs(user): stop promising OTA that isn't shipped (#1334 item 5) by @OrangePeachPink in #1510
- feat(analytics): exception-taxonomy labels in the host classifier β direction + rebound + floor-vs-rails (#1434 host slice) by @OrangePeachPink in #1507
- feat(editor): #1500/#1501/#1502 β reassignment is deliberate, never a silent remap by @OrangePeachPink in #1514
- feat(registry): base_url optional at adoption β unblock the discovery adopt flow (#1027) by @OrangePeachPink in #1515
- data(fleet): #1431 β the reproducible C5-dropout comparison (before/after instrument) by @OrangePeachPink in #1503
- design(trial): #1148 β execute the hero verdict (Greenhouse B, Plant A, ledger stashed) by @OrangePeachPink in #1516
- docs(adr-0038): Β§2 permits same-layer imports below layer 4 (#1452) by @OrangePeachPink in #1513
- fix(guard): align import_layer_guard to ADR-0038 Β§2 as amended β same-layer below 4 (Refs #1513, unblocks #1512) by @OrangePeachPink in #1519
- feat(registry): #1188 part 2 β the structured side control (ADR-0029) by @OrangePeachPink in #1517
- docs(adr): 0011/0012/0013 ProposedβAccepted β status matches shipped reality (#1460 AC1) by @OrangePeachPink in #1521
- refactor(serve): extract the route table into serve_routes.py (#1336 rung) (#1452) by @OrangePeachPink in #1512
- fix(fw-comment): sampling authority, not sole sampler (#1460 AC2) by @OrangePeachPink in #1523
- feat(registry): #1027 β the adopt-a-board surface (Β§5.1 β Β§5.5 β Β§5.2) by @OrangePeachPink in #1518
- feat(starter): Serial-Plotter-friendly output β raw:NNN data line, voice on mood change (Refs #1494 ch.3) by @OrangePeachPink in #1526
- docs(adr-0015): scoped attribution exception β chosen byline is not leakage (#1460 AC3, V1) by @OrangePeachPink in #1525
- docs(bench): launch run sheet step 5 β post the #1128 write-up at end of session by @OrangePeachPink in #1520
- feat(dx): the OTA release feed β generator, guard, cut wiring (Refs #1524, #1284 AC5 [critpath]) by @OrangePeachPink in #1529
- arch(serve): control-plane hardening riders β cache lock + the localhost floor (#1468) by @OrangePeachPink in #1527
- refactor(arch): the package flip β tools importable by name, 227 surgery lines gone (#1528) by @OrangePeachPink in #1530
- docs(ops): the architecture review cadence β preserve #297's ruling before deleting it by @OrangePeachPink in #1531
- docs(process): fold rulings 3+4 β theme-conformance gate (OPERATIONS) + CHANGELOG missed-section check (RELEASE_CUT) by @OrangePeachPink in #1537
- fix(dx): board wrappers resolve PRs, not just issues (Refs #1522) by @OrangePeachPink in #1542
- feat(registry): A1 β declared devices: pending binding + reconcile-on-first-contact (#1544) by @OrangePeachPink in #1563
- chore(deps): Bump the python group with 4 updates by @dependabot[bot] in #1540
- chore(deps): Bump the actions group with 3 updates by @dependabot[bot] in #1539
- docs(prd): PRD-0009 Monitor + Predict β the contract of record the headline never had (#1536, V1) by @OrangePeachPink in #1538
- feat(registry): A5 β reconcile declared vs answering; strangers route to adoption (#1548) by @OrangePeachPink in #1567
- fix(serve): B5 β announce the port before it can fail; say who holds a busy one (#1555) by @OrangePeachPink in #1565
- feat(dx): B3 β just doctor: the wall turned into a checklist (Refs #1553) by @OrangePeachPink in #1572
- fix(ota): a duplicate board class rejects the whole feed at the parser (#1570) by @OrangePeachPink in #1575
- docs(dx): C5 β the VS Code open path + the two editor lists reconciled (Refs #1561) by @OrangePeachPink in #1576
- fix(ota): an empty feed offers nothing, it is not broken (#1284 desk re-verify) by @OrangePeachPink in #1577
- fix(cards): #1534 D2+D3 β the next-water line tells the truth per state by @OrangePeachPink in #1578
- feat(onboarding): A3 β the visual pin map: tap a channel, tap its pin (#1546) by @OrangePeachPink in #1564
- feat(onboarding): C1 β the Quick start installs everything it needs, all three OSes + scripts/bootstrap (Refs #1557) by @OrangePeachPink in #1566
- docs(prd): PRD-0009 ratification fold β dispositions + the three ruled questions (#1536) by @OrangePeachPink in #1569
- feat(serve): A4 β the zero state names the real state, not "waiting forever" (#1547) by @OrangePeachPink in #1571
- fix(registry): A5 identity class is not the cal-anchor classifier (Trellis hold, #1548) by @OrangePeachPink in #1568
- docs(onboarding): C3 + C4 β prerequisites, GitHub access, and where to clone (Refs #1559, #1560) by @OrangePeachPink in #1588
- docs(dx): unmangle two docstrings C5 shipped (Refs #1561) by @OrangePeachPink in #1589
- design(predict): R3 β the confidence vocabulary: the interval IS the how-sure (#1580) by @OrangePeachPink in #1590
- feat(onboarding): A2 β the guided Add-a-board flow (#1545) [stacked on #1564] by @OrangePeachPink in #1574
- feat(dx): D1 part 1 β assert bootstrap from a genuinely toolless shell (Refs #1562) by @OrangePeachPink in #1593
- feat(predict): R6 β the forecast on the history chart, with an unmistakable boundary (#1581) by @OrangePeachPink in #1591
- feat(onboarding): A7 β connected hardware, enumerated once and safe to render (#1550) by @OrangePeachPink in #1595
- feat(onboarding): A6 β the flash handoff, board-aware (Refs #1549) by @OrangePeachPink in #1596
- fix(pages): publish manifest-alpha.json β give the alpha channel a producer (Refs #1599) by @OrangePeachPink in #1600
- fix(just): B2 β Ctrl-C exits clean; swallow only SIGINT (Refs #1552) by @OrangePeachPink in #1601
- fix(workbench): partial sweeps are judged per BOARD, not against the greenhouse (#1597) by @OrangePeachPink in #1603
- fix(cards): R12 β an untrustworthy reading predicts nothing (#1583) by @OrangePeachPink in #1602
- feat(predict): G3 β the calm signal, affirmative and time-bound (#1585) by @OrangePeachPink in #1605
- fix(registry): the committed example is documentation, not a data fallback (#1594) by @OrangePeachPink in #1607
- feat(dx): D1 part 2 β the guard holds the Ctrl-C contract (Refs #1562) by @OrangePeachPink in #1609
- feat(predict): G5 β the post-watering hold, issued at the drink and checkable after (#1586) by @OrangePeachPink in #1608
- refactor(predict): the FIRM/ROUGH/HAZY classifier gets one home (#1598) by @OrangePeachPink in #1610
- feat(home): B1 β Stop server on Home, present from the first run (#1551) by @OrangePeachPink in #1612
- feat(monitor): R11 β the attention composer, one state per plant in one place (#1582) by @OrangePeachPink in #1611
- design(monitor): the attention model β one composed state for R11/R12/G2/G3 (#1582) by @OrangePeachPink in #1592
- feat(dx): D1 parts 3+4 β the zero state, and the whole journey walked (Refs #1562) by @OrangePeachPink in #1617
- fix(serve): B4 β the serial lock names its holder and says what to do (#1554) by @OrangePeachPink in #1618
- feat(predict): R2 β the ranked queue, ordered by forecast not by wetness (#1579) by @OrangePeachPink in #1620
- feat(home): B6 β hardware health on the default surface, as a roll-up (#1556) by @OrangePeachPink in #1613
- docs(onboarding): C2 β document the shipped Add-a-board flow, steps 9/10/12 (Refs #1558) by @OrangePeachPink in #1619
- feat(predict): G2 β surface the trajectory facts so the harm level can fire (#1584) by @OrangePeachPink in #1623
- docs(agents): the contributor-agent contract β attribution, credit, and bot-review provenance by @OrangePeachPink in #1621
- fix(ci): the signer could never resolve a draft β no repo context (Refs #1346) by @OrangePeachPink in #1627
- docs(process): the tag is the line β reconcile the milestone both directions at every cut (#1628) by @OrangePeachPink in #1629
- release(v0.8.1): the version bump β four declared sites + a fifth the guard doesn't cover (Refs #1346) by @OrangePeachPink in #1631
- fix(health): rate-gate the three bare signals; the cadence must be local (#1626) by @OrangePeachPink in #1632
- docs(changelog): the missing 0.8.0 section, and 0.8.1 (Refs #1615, #1346) by @OrangePeachPink in #1636
- docs(changelog): correct a false contributor claim in [0.8.1] by @OrangePeachPink in #1638
Full Changelog: v0.8.0...v0.8.1