From 08a2b51a9cc29190f4254ead4be0eb6908347002 Mon Sep 17 00:00:00 2001 From: DLANSAMA <258674612+DLANSAMA@users.noreply.github.com> Date: Fri, 14 Aug 2026 15:27:31 -0400 Subject: [PATCH] docs: retire stale plan claims and fix mutation-baseline contradictions Re-verified the 2026-07-31 post-audit residual table against current main rather than trusting the audit snapshot. Four residuals it lists as open are closed, and one the follow-up notes recorded as closed is not: - S5 closed: utils.py:63-67 is now a shim delegating to jsonio; both spellings redact the audit's own repro identically. No weaker second implementation. - A4 closed: mqtt_port is wired end to end (context -> printer -> mqtt_tls _mqtt_port -> client.connect). Doctor no longer reports a port it ignores. - T1 closed: textual is pinned >=8.0,<9.0 (#115), not <2.0. - A3/Q1/S1/B1/R1/R3 closed by #119, the camera opt-in work, and the TUI merge. - Q4 NOT closed: #116 removed 81 setdefault stubs across 33 files but tests/bambu_test_base.py:26-29 still assigns MagicMock to sys.modules for paho at import time and never restores it, which is also order-dependent. - P1, Q5, Q6, A1, A2, S3, R2, T2/WS4, W3.4 re-confirmed open with current file:line evidence; A1's target moved to bambu_cli.cliparse. Plan headers carried numbers from before #119/#120 and read as live state. Both gameplans now lead with measured current values (1499 non-live, 90.99% Linux / 90.68% Windows over 8368 statements, floor 86, Python 3.10/3.12/3.14) and keep the old figures marked as historical, including the a-plus W1 sizing that was derived from the 89.4% baseline. mutation-baseline.md claimed its per-module table reconciles to the 50.7% headline. It does not: the seven rows are the whole only_mutate scope yet sum to 2010 mutants against a headline 2091, leaving 81 unattributed (37 survived, 41 killed, 3 timeout). Also corrected the "residual survivors should be cosmetic" prediction, which the same file already disproves at 21.8% with 269 survivors, and the stale "re-run mutmut on slicer/output.py" note, which was already done on 2026-08-04. tui-plan.md kept imperative branch instructions after its own "implemented" banner; folded into the historical framing. Also drops AI-vendor attribution from these two plan docs: the audience and source lines, the "pasteable brief for a new session" heading, and the trailer rule, which now reads "AI/session attribution trailers" instead of naming a product. References to the local-only agent notes filename are left intact where removing the name would void the rule. Docs only, no code changes. Gates: 1499 passed / 1 deselected, 91.0% total; test_docs_consistency + test_docs_links 25 passed. --- docs/mutation-baseline.md | 14 +++++-- docs/plans/a-plus-gameplan.md | 14 ++++--- docs/plans/post-audit-gameplan.md | 61 ++++++++++++++++++++++++++----- docs/plans/tui-plan.md | 13 ++++--- 4 files changed, 77 insertions(+), 25 deletions(-) diff --git a/docs/mutation-baseline.md b/docs/mutation-baseline.md index fc2cddc..365430d 100644 --- a/docs/mutation-baseline.md +++ b/docs/mutation-baseline.md @@ -72,8 +72,14 @@ Focused tests (also listed in `[tool.mutmut].pytest_add_cli_args_test_selection` | Timeout | 3 | | **Score** | **1061 / 2091 = 50.7%** | -Per-module, derived from the mutant sources and survivor list (these reconcile -to the 50.7% total, so they are measured rather than estimated): +Per-module, derived from the mutant sources and survivor list. **These rows do +not reconcile to the headline totals and should not be read as if they do:** the +seven rows below are the whole `only_mutate` scope, yet they sum to **2010** +mutants (990 survived + 1020 killed) against a headline of **2091** (1027 + 1061 ++ 3 timeouts). **81 mutants — 37 survived, 41 killed, 3 timeout — are +unattributed.** Per-module scores are therefore measured but incomplete; the +headline 50.7% is the number to quote. Re-derive the split before using any row +as a target. | Module | Total | Survived | Killed | Score | vs 2026-07-09 | |--------|------:|---------:|-------:|------:|--------------:| @@ -125,7 +131,7 @@ Enforced by `./scripts/run_mutation_baseline.sh` after `mutmut export-cicd-stats Categories (not an exhaustive dump of the 1027 survivors): 1. **Equivalent / cosmetic** — error-message string literals, log format, `getattr` default when tests always set the attribute, `ZipFile(..., "r")` vs default mode. -2. **`_finalize_slice` (output.py)** — subprocess exit interpretation, JSON emit, path display. **Addressed (C.4):** `tests/fakes/orca_stub` + `tests/test_slice_stub_integration.py` now run these branches against a real fake-slicer subprocess. Residual survivors here should be cosmetic (log strings / path display); re-measure before treating any as "accepted". +2. **`_finalize_slice` (output.py)** — subprocess exit interpretation, JSON emit, path display. **Addressed (C.4), but the prediction failed:** `tests/fakes/orca_stub` + `tests/test_slice_stub_integration.py` run these branches against a real fake-slicer subprocess, and the re-run still scored **21.8%** — **269 survivors, the single largest pocket (26% of all survivors)**. They are *not* cosmetic: the bulk are unconstrained `_finalize_slice` exit-code interpretation, JSON emit, and path display (see the C.4 correction above). Extract those decisions into assertable units or stop counting the module — do not treat these as accepted. 3. **DNS cache / hop bookkeeping (netsafety)** — TTL, cache size clear, attribute names on redirect requests. Core `is_global` refuse path is well killed. 4. **URL normalize / Content-Disposition edges (validation/naming)** — ambiguous scheme-less inputs and RFC2231 header tuples; behavior partially covered; full combinatorial matrix deferred. 5. **Dry-run prediction (predict.py)** — Printables/archive/extension branches that return `None` early; many mutants are observationally equivalent under the focused suite. @@ -154,4 +160,4 @@ Artifacts (`mutants/`, `.mutmut-cache`, `.hypothesis/`) are gitignored. - mutmut 3.x needs Python ≥ 3.10 (CI mutation job uses 3.12). - Hypothesis property tests live in `tests/test_properties_safety.py` and are part of the focused mutmut suite. -- Raising the score further: the hermetic `_finalize_slice` tests now exist (C.4, `tests/test_slice_stub_integration.py`); re-run `mutmut` on `slicer/output.py` and update the per-module row. Optionally still move pure 3mf validation to a tiny module so mutmut does not spend budget on I/O. +- Raising the score further: the `slicer/output.py` re-run is **done** (2026-08-04, 21.8% — the row above is current); adding hermetic tests moved line coverage but not the mutation score, so the next lever is extracting `_finalize_slice`'s decisions into assertable units, or dropping the module from `only_mutate` rather than carrying a 21.8% row. Separately, attribute the 81 unaccounted mutants (see §Measured 2026-08-04) before trusting per-module targets. Optionally still move pure 3mf validation to a tiny module so mutmut does not spend budget on I/O. diff --git a/docs/plans/a-plus-gameplan.md b/docs/plans/a-plus-gameplan.md index 8587bb0..48b4e45 100644 --- a/docs/plans/a-plus-gameplan.md +++ b/docs/plans/a-plus-gameplan.md @@ -1,9 +1,11 @@ # Gameplan: A+ across the board **Date:** 2026-08-13 -**Baseline (this checkout, dirty tree, Linux py3.12):** 1459 passed / 1 live deselected, **89.4%** branch over 8413 statements. Current scoreboard: overall **A**, none below A−. Product **A−**. A+ is not earned. +**Current measured (2026-08-14, post-#119/#120):** **1499** passed / 1 live deselected, **90.99%** branch on Linux (**90.68%** Windows — the binding leg, macOS passing) over **8368** statements, CI floor **86**. Scoreboard: overall **A**, none below A−. Product **A−**. **A+ is not earned.** -**Progress 2026-08-13:** W1 moved coverage **89.4 → 91.0%** (1499 passed, 8368 stmts) via transport/session/camera/ftps tests + dead `abort` tails after `emit_json_error`. W2/W3 landed (residual acceptance, facade freeze, scoreboard refresh). **Did not raise the CI floor** (91.0 < 92.5 margin). **Not A+ across the board.** Remaining: 91.0→92, floor 92, `mypy --strict`, `v1.0.0` tag. +*Historical baseline (2026-08-13, dirty tree, Linux py3.12): 1459 passed, 89.4% over 8413 statements — superseded by the line above; kept because the W1 sizing below was computed from it.* + +**Progress 2026-08-13:** W1 moved coverage **89.4 → 90.99%** (1499 passed, 8368 stmts) via transport/session/camera/ftps tests + dead `abort` tails after `emit_json_error`. W2/W3 landed (residual acceptance, facade freeze, scoreboard refresh). **Did not raise the CI floor** (90.99 < the 92.5 margin W4 requires). **Not A+ across the board.** Remaining: 90.99→92.5, floor 86→92, `mypy --strict`, `v1.0.0` tag. This is an execution plan, not another audit. Truth sources stay [quality-roadmap.md](../quality-roadmap.md) §2 / §3.1 / §5 and [test-backlog.md](../test-backlog.md). @@ -29,7 +31,7 @@ A+ across the board means **all three** of: | Error model | A | **A+** | Already entry-only `sys.exit`. | | Tests | A | **A+** | **92%** total on Linux. Per-module A+ floors only for §5's four. | | CI / release | A | **A+** | Raise `--cov-fail-under` to **92** only if Linux ≥ **92.5%** (Windows last trailed ~0.3 pt). | -| Docs | A | **A+** | Refresh 1448/88.9%/8419; fix stale camera-TCP sentence; accept residuals. | +| Docs | A | **A+** | Number refresh **done** (#120: 1499/90.99%/8368) and camera/`--confirm`/doctor accuracy **done** (#121/#122). Remaining: field-level `api.md` sync. | | Product | A− | **A−** until tag | Classifier + changelog can be prepared. **Do not tag `v1.0.0` without an explicit user “tag it”.** | **Consequence:** this session can make every *unblocked* row A+ and leave Typing A + Product A−. That is **not** “A+ across the board.” Do not write that phrase into the scoreboard until the tag exists and Typing is either strict or the A+ definition is deliberately changed in a separate, reviewed docs PR. @@ -38,7 +40,7 @@ A+ across the board means **all three** of: ### W1 — Coverage to ≥92.5% Linux (Tests A+) -Need ~280 extra statement/branch hits vs 89.4%. Hit the fattest *decision* holes first; do not pragma I/O loops just to move the number. +Sized when coverage was 89.4%; **as of 2026-08-14 Linux is 90.99%**, so the remaining gap to 92.5% is roughly **~125** statement/branch hits, not the ~280 originally scoped. The per-module misses below are pre-W1 and were not re-measured — re-run `--cov-report=term-missing` before picking targets. Hit the fattest *decision* holes first; do not pragma I/O loops just to move the number. | Priority | Module | Last miss | Approach | |----------|--------|-----------|----------| @@ -54,8 +56,8 @@ After W1: remeasure with `pytest -m "not live" --cov=bambu_cli`. Do not raise th ### W2 — Docs truth + residual acceptance (Docs A+, Security A+) -- Scoreboard / backlog: **1459** passing, **89.4%→measured**, **8413** stmts, **27** schemas, floor still 86 until W4. -- Delete the stale “TCP failure on 6000 still falls back to the streamer” sentence. +- Scoreboard / backlog: **done** in #120 — **1499** passing, **90.99%** Linux / **90.68%** Windows, **8368** stmts, floor still 86 until W4. (Schema count **27** was never re-verified; confirm before quoting.) +- Delete the stale “TCP failure on 6000 still falls back to the streamer” sentence. **Done** in #121/#122. - SECURITY.md: mark camera streamer / `insecure_tls` / leftover container / HTTP integrity / TOFU / Windows ACLs as **accepted 1.0 residuals** (not open P0s). That is what “close or explicitly accept” means. ### W3 — Architecture cheap A+ diff --git a/docs/plans/post-audit-gameplan.md b/docs/plans/post-audit-gameplan.md index 1351387..a54ac59 100644 --- a/docs/plans/post-audit-gameplan.md +++ b/docs/plans/post-audit-gameplan.md @@ -1,10 +1,19 @@ # Gameplan: post-audit hardening + `feat/tui` merge prep -**Audience:** coding agents (Claude / Grok / etc.) executing work in this repo. -**Source:** 2026-07-31 deep read-only audit (parent session + 3× explore agents on Grok 4.5). -**Branch at audit:** `feat/tui` @ `0d63378` (12 commits ahead of `main`). -**Measured suite (Linux, that session):** `1314 passed`, `1 deselected` (live), **88.6%** branch coverage. -**Version:** `0.5.0.dev0` — pre-1.0 Beta. +**Audience:** coding agents executing work in this repo. +**Source:** 2026-07-31 deep read-only audit (one parent pass + 3× parallel exploration passes). +**Branch at audit:** `feat/tui` @ `0d63378` (12 commits ahead of `main`) — **since merged** (#97, #104). +**Measured suite at audit (Linux, that session):** `1314 passed`, `1 deselected` (live), **88.6%** branch coverage. +**Version at audit:** `0.5.0.dev0`. + +> **Every number and file path above is a 2026-07-31 snapshot, not current state.** +> Current: **1499** non-live tests, **90.99%** Linux / **90.68%** Windows over **8368** +> statements, CI floor **86**, version **0.5.0** (Beta), Python **3.10/3.12/3.14** +> (3.9 dropped in #115). `protocols/mqtt.py` is no longer an ~885 LOC hotspot — it +> was split into `mqtt_tls` / `mqtt_cmd` / `mqtt_print` / `mqtt_monitor` / +> `mqtt_session` in #119. Camera code lives under `protocols/`, not +> `bambu_cli/camera.py`. **See the re-verified status block below the residual +> table before acting on any row.** > **Revision 2026-07-31 (cross-check pass).** A second independent audit re-verified this plan's premises and found the plan **missing a confirmed merge blocker** plus eight other findings; the new items are folded into the residual table and into **WS-B** below. S1/S2/S3 were re-verified in code and hold — **S2 is a genuine catch this plan surfaced that the other audit missed**. **Q2's diagnosis was wrong and is corrected below.** Post-fix suite is `1321 passed`, **88.58%**. @@ -22,7 +31,7 @@ Copy these into every work session: 4. **`sys.exit` only in `bambu_cli/cli.py`.** Domain raises `BambuError` / `abort`. CI greps this. 5. **No `@mockable`**, no `isinstance(..., Mock)` / test-awareness branches in production. 6. **Do not** hand-maintain package / py_compile / help-command lists (setuptools + smokes auto-discover). -7. **Do not** add Claude-Session or similar trailers to commits/PRs. +7. **Do not** add AI/session attribution trailers to commits/PRs. 8. **LOCAL-ONLY:** `CLAUDE.md` is gitignored via `.git/info/exclude` — never commit it. 9. **TUI is human-only:** no machine contract; never try to drive `tui`/`go` via `--json`. 10. **Confirm choke point:** under `bambu_cli/tui/`, `confirm=True` must appear only in `screens/confirm.py` (the Start print path). Preserve this invariant; add a CI grep if you touch confirm. @@ -86,6 +95,40 @@ A green pytest alone is **not** evidence ruff/mypy/bandit passed. | R2 | `privacy_smoke` is a crying-wolf gate: exit 1 locally on correctly-gitignored files (it walks the filesystem without consulting git), and on CI runners the account-name patterns resolve to the filtered generic `runner`, so its two best checks never build. Red locally, disarmed remotely | Medium | | R3 | `CLAUDE.md:16` prints `--cov-fail-under=81`; CI is **83** (`ci.yml:77`, `CONTRIBUTING.md:25`, `AGENTS.md:104`). An agent following CLAUDE.md runs a weaker gate than CI | Medium | +### Status re-verified 2026-08-14 — read this before working any row above + +The table is the **2026-07-31 audit snapshot**, kept as the record. Each row below +was re-checked against current `main` (`85c831e`); cited paths and line numbers are +from that check, not from the audit. + +**Closed — do not re-open:** + +| ID | Evidence | +|----|----------| +| S1 | Camera is fail-closed/opt-in: a failed direct grab does not start the streamer without `camera_allow_streamer` / `--allow-camera-streamer`; pin mismatch or `ssl.SSLError` with a pin hard-aborts; `camera_direct_only` forbids it entirely | +| S5 | `utils.py:63-67` is now a 4-line shim delegating to `jsonio.redact_url_credentials`. There is no longer a weaker second implementation — both spellings return `https://192.168.1.5/a.stl` for the audit's own `bob:secret123@…` repro | +| A3 | `protocols/mqtt.py` split into five siblings (#119) | +| A4 | `mqtt_port` is live, not dead: `context.py:89,137` → `printer.py:339` → `mqtt_tls.py:143` `_mqtt_port()` → `mqtt_tls.py:162` `client.connect(resolved_ip, _mqtt_port(printer), …)`. Doctor no longer lies | +| Q1 | CI floor is **86** (#119) against ~91% measured | +| T1 | Textual pin is `>=8.0,<9.0` (#115) — the `<2.0` cap is gone and the 8.x break is fixed | +| B1 | Rich markup injection fixed before the TUI merged (#97) | +| R1 / R3 | Media hygiene handled; `CLAUDE.md` cites floor 86 | + +**Still open — re-verified as real:** + +| ID | Evidence | +|----|----------| +| P1 | **Confirmed printer-safety bug.** `_numeric_values("400abc")` returns `[]` while `_numeric_values("400")` returns `[400.0]` (`slicer/options.py:162-175`), so a non-numeric nozzle/bed override contributes no values and temp validation passes vacuously where plain `400` correctly errors | +| Q4 | **Partly closed, and less closed than #116's message implies.** The 81 `sys.modules.setdefault("paho…")` calls are gone (0 remain), but `tests/bambu_test_base.py:26-29` still *unconditionally* assigns `sys.modules["paho"|"paho.mqtt"|"paho.mqtt.client"] = MagicMock()` at import time and never restores them. Real paho is installed and importable. This is also an order-dependence hazard of exactly the kind `AGENTS.md` forbids | +| Q5 | Both halves hold. `test_cmd_print_dry_run_success` (`tests/test_doctor_and_safety.py:198`) asserts `nlst` and `get_status` but never that the print publish did **not** happen; `test_cmd_stop_with_confirm` (`tests/test_cmd_device.py:107`) ends at `mock_send_command.assert_called_once()` with no payload check | +| Q6 | `tests/contracts/test_schema_validation.py:~299` still hand-builds `{"status": "print_started", …}` and validates it against the schema — no `bambu_cli` code runs, so emitter and schema can still drift together | +| A1 | Open, but the path moved: `interactive/core.py:558` and `interactive/presets.py:81` now import `build_parser` from `bambu_cli.cliparse` (not `bambu_cli.cli`). Domain→parser coupling remains | +| A2 | `utils.py:58` `_JSON_EMITTED` (with `_LAST_ERROR_PAYLOAD` / `_LAST_DOWNLOAD_PAYLOAD`) are still process globals mutated via `global` at `utils.py:133-134` | +| S3 | `commands/snapshot.py:399` is still an unbounded `resp.read()`. Severity is lower than the audit's Medium: the streamer path now requires explicit opt-in, so it is reachable only after the user enables it | +| R2 | Still crying wolf — `uv run python tests/privacy_smoke.py` exits **1** on a clean checkout | +| T2 / WS4 | Confirm-modal ergonomics and quit-during-prepare — unchanged | +| W3.4 | Never implemented: `ci.yml` contains no grep asserting `confirm=True` under `bambu_cli/tui` is confined to `screens/confirm.py` | + --- ## 1. Outcome goals @@ -281,7 +324,7 @@ Before merging to `main`: - [ ] `ci_workflow_smoke` if parser/CI touched - [ ] Multi-OS GitHub Actions green on tip (not only an older SHA like `cc6f78c`) - [ ] Human smoke: `plate tui --sim` on a real TTY (not agent-driven) -- [ ] No Claude-Session trailers; no force-push of shared history without ask +- [ ] No AI/session attribution trailers; no force-push of shared history without ask - [ ] Version remains `.dev0` until release tag process ([docs/releasing.md](../releasing.md)) **Do not auto-merge.** Human review required (plan precedent). @@ -348,7 +391,7 @@ Before merging to `main`: --- -## 8. Pasteable brief for a new Claude session +## 8. Pasteable brief for a new agent session ```text You are working in your local platecli checkout on branch feat/tui (or main if TUI already merged). @@ -359,7 +402,7 @@ Priority order: WS-B (TUI markup blocker — already fixed in the working tree, Also open and unassigned: S5 (utils redactor weaker + 0% covered), P1 (temperature override fails open on non-numeric values), Q4 (paho never exercised against the real library), Q5 (dry-run and cmd_stop tests cannot fail as intended). See the residual table. -Constraints: AGENTS.md + SECURITY.md; no --confirm / BAMBU_LIVE without asking; sys.exit only in cli.py; no @mockable; no Claude-Session trailers; do not commit CLAUDE.md. +Constraints: AGENTS.md + SECURITY.md; no --confirm / BAMBU_LIVE without asking; sys.exit only in cli.py; no @mockable; no AI/session attribution trailers; do not commit the local-only agent notes file. Verify with ruff + ruff format + mypy + bandit + pytest -m "not live" (ResourceWarning error) at the stated cov floor. After ci.yml changes, run tests/ci_workflow_smoke.py. diff --git a/docs/plans/tui-plan.md b/docs/plans/tui-plan.md index 6d753ff..aaffdab 100644 --- a/docs/plans/tui-plan.md +++ b/docs/plans/tui-plan.md @@ -7,11 +7,12 @@ built, documented in [the manual](../manual.md#full-screen-mode-plate-tui), and covered by pilot tests. What the plan describes as future work has either shipped or is recorded as an accepted quirk in the PR — read the manual and [quality-roadmap](../quality-roadmap.md) for current behaviour, not this file. -**Branch:** all implementation work goes on **`feat/tui`** (branched from `main`). -Each phase below is sized for one ~1–2 h autonomous session and MUST leave the -repo green (every gate in §8 passing) so the session can commit safely. -**Prerequisite reading:** `AGENTS.md`, `docs/plans/interactive-mode-plan.md` -(house conventions, printer-safety rules), `docs/quality-roadmap.md`. +*(Historical, as written:)* **Branch:** all implementation work goes on +**`feat/tui`** (branched from `main`) — that branch merged via #97/#104 and no +longer exists. Each phase below was sized for one ~1–2 h autonomous session and +had to leave the repo green (every gate in §8 passing). **Prerequisite reading +at the time:** `AGENTS.md`, `docs/plans/interactive-mode-plan.md` (house +conventions, printer-safety rules), `docs/quality-roadmap.md`. ## 1. Goal @@ -361,7 +362,7 @@ Never run against a live printer or set `BAMBU_LIVE=1` without asking Dylan. **Acceptance criteria:** all §8 gates green from a clean checkout of `feat/tui`; `uv build` + package smoke green (styles.tcss present in wheel); docs updated. Final commit to `feat/tui`; branch ready for PR review — do not -merge to `main`, do not add Claude-Session trailers. +merge to `main`, do not add AI/session attribution trailers. ## 9. Quality gates (run at the end of EVERY phase)