diff --git a/.planning/PROJECT.md b/.planning/PROJECT.md index b5c4a28b..2cc38c4f 100644 --- a/.planning/PROJECT.md +++ b/.planning/PROJECT.md @@ -69,7 +69,7 @@ real files the user owns, and nothing is lost if Maru is uninstalled. feature landing mid-refactor makes every regression ambiguous. One recorded exception: the `hwped_*` hwp-editor bridge, written by a parallel track and adopted here rather than built (STATE.md "Scope Exceptions") -- **Converting all 1,118 `Result` signatures** - CONCERNS.md rejects +- **Converting every `Result` signature** - CONCERNS.md rejects it explicitly; only the errors the frontend actually branches on move - **Retrofitting all ~20 existing path-traversal validators** - the existing checks are individually sound; promoting the canonical helper is the goal @@ -181,7 +181,7 @@ ones this milestone can actually break are listed here. | Scope drawn from CONCERNS.md Tech Debt, not from the SPECs | 18 ingested docs describe shipped behavior; inventing forward work from them would be fabrication | - Pending | | Verification gates land before the decomposition (Phase 1) | Moving 68 `useState` / 50 `useEffect` without a hook-dependency gate reproduces #260/#262/#264 | ✓ Phase 1 — 7 gates live, deliberate-break proofs red-then-green, UAT 24/24 | | Continue the module-store precedent instead of adding a state library | `errorStore`/`workspaceStore`/`editorTabsStore` already prove the pattern here | - Pending | -| Typed error contract covers only branched-on errors | Converting 1,118 signatures is cost without benefit; display-only errors read fine as strings | - Pending | +| Typed error contract covers only branched-on errors | Converting all ~1,138 signatures is cost without benefit; display-only errors read fine as strings | - Pending | | Promote `ensure_within`, do not retrofit all ~20 callers | Existing checks are individually sound; the problem is that a new author has no canonical example | ✓ Phase 2 — promoted to `crate::paths`, doc + tests as the example, zero retrofits | | Phases 4-5 get no `UI hint` annotation | They refactor UI state plumbing with pixel-identical output as the success criterion; a UI design spec would be the wrong downstream suggestion | - Pending | | 64 SPEC constraints recorded as invariants, not decisions | 0 ADRs in the set - nothing is decision-locked, so a future ADR can override any of them | - Pending | diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index a6e02211..323fda92 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -34,7 +34,7 @@ milestone with no end-user-visible surface. - [ ] **ERR-01**: A frontend caller can read a stable machine-readable `code` from any error it needs to branch on, alongside the human-readable message - [ ] **ERR-02**: Renaming an error code fails the build on both the Rust and TypeScript side instead of silently breaking a recovery path - [ ] **ERR-03**: Every existing `message.includes("")` matcher branches on the typed code instead - starting with `evidence_binder_revision_conflict` at `src/components/evidence/EvidenceBinderPane.tsx:174` -- [ ] **ERR-04**: Display-only errors are untouched - the `Result` signature count stays within a few of today's 1,118 +- [ ] **ERR-04**: Display-only errors are untouched - the `Result` signature count stays within a few of the measured baseline of 1,138 (CONCERNS.md's 1,118 is stale; re-measure before Phase 3 executes) > **Note for Phase 3 planning, from Phase 1's verification (2026-08-22).** None of the > seven `make verify` gates can catch a serde mismatch at the Rust-TypeScript IPC @@ -103,7 +103,7 @@ not compete with the structural work. Not in the current roadmap. | Feature | Reason | |---------|--------| | Any new product feature | Behavior-preserving work is only verifiable if behavior is not also changing. **Exception, 2026-08-23:** the `hwped_*` hwp-editor bridge landed from a parallel track and is adopted, not built, by this milestone - see STATE.md "Scope Exceptions" | -| Converting all 1,118 `Result` signatures | Explicitly rejected in the CONCERNS.md fix approach; cost without benefit for display-only errors | +| Converting every `Result` signature | Explicitly rejected in the CONCERNS.md fix approach; cost without benefit for display-only errors | | Retrofitting all ~20 existing path-traversal validators | The existing checks are individually sound; the problem is the absence of a canonical example, not the callers | | Changing `.maruignore` defaults | It is a user-facing file format, not a scanner constant | | Any visible UI change during decomposition | A refactor that alters output cannot be verified against the existing e2e suite | diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index b9b97de5..9bbfcbae 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -117,7 +117,7 @@ Notes for planning: 1. A frontend caller can read a stable `code` and a human message from every error it branches on, without parsing the message 2. Renaming a code on the Rust side fails `make verify` on the TypeScript side, and vice versa 3. No `message.includes("")` matcher remains in `src/` for a code that moved to the contract - 4. The `Result` count in `src-tauri/src/` is essentially unchanged from today's 1,118 - display-only errors were not touched + 4. The `Result` count in `src-tauri/src/` is essentially unchanged from the measured baseline of 1,138 (CONCERNS.md's 1,118 is stale) - display-only errors were not touched **Plans**: 0/4 plans executed diff --git a/.planning/STATE.md b/.planning/STATE.md index b24fd7ba..e7188df3 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -136,7 +136,7 @@ None yet. ### Blockers/Concerns -- **Phase 3's ERR-04 count band is coupled to `hwped.rs`.** The pinned command reports 1,138 on the tree that adopts the bridge, and `hwped.rs` contributes 19 of those matches; without it the tree reads 1,119 and the post-migration count lands at 1,109, below `03-04-PLAN.md`'s `[1118, 1138]` band. Re-measure the baseline before Phase 3 executes and write the new number into `03-02-PLAN.md` and `03-04-PLAN.md` - continued hwp-editor work keeps moving it. +- **Phase 3's ERR-04 count band is coupled to `hwped.rs`.** The pinned command reports 1,138, and `hwped.rs` contributes 19 of those matches; without it the tree reads 1,119 and the post-migration count lands at 1,109, below `03-04-PLAN.md`'s `[1118, 1138]` band. The baseline is now anchored to a commit rather than a date - re-confirmed 2026-08-23 on the committed tree at 34f96ee - and `03-02`/`03-04` cite that provenance. Residual risk: the hwp-editor track is still active, so `03-04` now requires a re-measurement at the start of the plan rather than treating drift as exceptional. - `src/App.tsx` has no test of any kind. Phases 4-5 depend on Phase 1's hook-dependency gate plus the per-pane tests written during extraction; there is no existing safety net for the decomposition. - `make verify` runs on ubuntu-22.04 only and e2e runs Chromium against Vite with mocked IPC. Nothing in CI exercises WKWebView, the real PTY, IME input, or the macOS menu - macOS-affecting changes need a real-app run. diff --git a/.planning/phases/03-typed-ipc-error-contract/03-01-PLAN.md b/.planning/phases/03-typed-ipc-error-contract/03-01-PLAN.md index 0e6bc295..0bcc2928 100644 --- a/.planning/phases/03-typed-ipc-error-contract/03-01-PLAN.md +++ b/.planning/phases/03-typed-ipc-error-contract/03-01-PLAN.md @@ -105,6 +105,7 @@ Output: `src-tauri/src/ipc_error.rs`, `src/lib/ipcError.ts` + tests, the `IpcErr - TS: normalizeIpcError is idempotent — an already-normalized IpcError instance is returned by identity (same object, unchanged), so re-normalizing never produces a doubled "code: code: message" prefix (checker revision: the browser-mock path in this plan and the e2e-fixture paths in 03-03 route pre-thrown errors through the same normalizer). + ERR-04 BASELINE — do this FIRST, before editing any file. Run the pinned count command `grep -roE "Result<.*, String>" src-tauri/src --include="*.rs" | wc -l` and record the result in 03-01-SUMMARY.md as the phase's pre-migration baseline B. This plan flips `evidence_binder_mutate`, so the count stops being pristine the moment the tracer lands, and 03-04 runs far too late to establish one. The planning-time figure is 1,138 (re-confirmed on the committed tree at 34f96ee), but the measured B is what 03-02 and 03-04 assert against — do not substitute the planning-time constant if they differ. RUST — create src-tauri/src/ipc_error.rs per RESEARCH Pattern 1 and PATTERNS, with these exact decisions (Claude's-discretion items, decided here): - `#[derive(Debug, Clone, serde::Serialize)] pub struct IpcError { pub code: String, pub message: String }` — both fields are single lowercase words, so no serde rename attribute (per PATTERNS). `code` is an owned String, NOT &'static str: `impl From for IpcError` must exist so that every legacy `?`/map_err String path inside migrated commands converts without edits, and From puts the WHOLE original string into `message` with `code` set to the empty string (this keeps display-only text byte-identical — the behavior-preserving rule Phase 2 applied to ensure_within). - `impl std::fmt::Display for IpcError`: when `code` is empty, write only `message`; otherwise write code, ": ", message (hub_client/http.rs:25-35 idiom). @@ -198,5 +199,5 @@ A revision conflict in the evidence binder reaches the component as a typed `Ipc -Create `.planning/phases/03-typed-ipc-error-contract/03-01-SUMMARY.md` when done. Record: the checkpoint outcome (including the 7-command scope ratification), the exact Display/normalization semantics chosen, and any deviation from this plan. +Create `.planning/phases/03-typed-ipc-error-contract/03-01-SUMMARY.md` when done. Record: **the pre-migration ERR-04 baseline B measured before any edit** (03-02 and 03-04 assert against it), the checkpoint outcome (including the 7-command scope ratification), the exact Display/normalization semantics chosen, and any deviation from this plan. diff --git a/.planning/phases/03-typed-ipc-error-contract/03-02-PLAN.md b/.planning/phases/03-typed-ipc-error-contract/03-02-PLAN.md index 3be2c448..cba53acb 100644 --- a/.planning/phases/03-typed-ipc-error-contract/03-02-PLAN.md +++ b/.planning/phases/03-typed-ipc-error-contract/03-02-PLAN.md @@ -121,7 +121,7 @@ Output: the full Rust emit side of the contract; `today_apply_plan_result` adapt Per D-01/D-02: change `assert_expected_revision` (:122-132) to return `Result<(), IpcError>`, constructing `IpcError { code: DOCUMENT_CONFLICT.to_string(), message: format!("expected revision {expected}, found {actual}") }`. Flip `save_document` (:134-193) to `Result`. The two inline missing-file conflict returns (:150-152 and :185-188) construct IpcError with DOCUMENT_CONFLICT and message `format!("expected revision {expected}, file is missing")` (prefix removed, suffix verbatim). The "Cannot read document: {err}" map_err paths and every other String error path (resolve_inside_vault, assert_document_owner, assert_maru_can_write, validate_managed_write, snapshot writing, write_atomic, read_document tail-call) ride `From` UNCHANGED — do not reword them (prohibition: byte-identical text; PATTERNS explicitly sanctions non-contract paths keeping their strings via the conversion, and no non-contract code is added to the TS union). Migrate the test assertions at :738 and :1227 to the new shape in behavior above. Import from `crate::ipc_error`. - Then run the pinned ERR-04 count command — `grep -roE "Result<.*, String>" src-tauri/src --include="*.rs" | wc -l` — and record the number in the task commit message / SUMMARY for plan 03-04's guard (planning-time baseline: 1,138 measured 2026-08-23; expected after both this plan's tasks: roughly 1,128 — the 7 commands + 3 helpers flip — but the recorded measurement is what 03-04 asserts against). + Then run the pinned ERR-04 count command — `grep -roE "Result<.*, String>" src-tauri/src --include="*.rs" | wc -l` — and record the number in the task commit message / SUMMARY for plan 03-04's guard (the authoritative baseline is B, the pre-migration count 03-01 measured and recorded in its SUMMARY before touching any file — not a planning-time constant. The planning-time figure was 1,138, re-confirmed 2026-08-23 on the committed tree at 34f96ee; 19 of those matches live in hwped.rs and the hwp-editor track is still active, so B may legitimately differ. Expected here: B minus 10 — the 7 commands + 3 helpers flip, with 03-01's own evidence_binder flip already inside B's delta. Record both the measured number and B minus 10 so 03-04 can check them against each other). cd src-tauri && cargo test --lib document && cargo clippy --lib -- -D warnings && cd .. && grep -roE "Result<.*, String>" src-tauri/src --include="*.rs" | wc -l diff --git a/.planning/phases/03-typed-ipc-error-contract/03-04-PLAN.md b/.planning/phases/03-typed-ipc-error-contract/03-04-PLAN.md index 2613d0c7..7e07cfe8 100644 --- a/.planning/phases/03-typed-ipc-error-contract/03-04-PLAN.md +++ b/.planning/phases/03-typed-ipc-error-contract/03-04-PLAN.md @@ -17,7 +17,7 @@ estimate: must_haves: truths: - "[ERR-02] The rename drill is proven both directions: renaming a code constant fails cargo test on the Rust side, and renaming a union literal fails tsc on the TypeScript side — both observed red, then reverted to green" - - "[edge:boundary/ERR-04] The pinned count command (grep -roE over src-tauri/src for Result<*, String> signatures) reports within [1118, 1138] — planning baseline 1,138 measured 2026-08-23 minus the 10-item migration set, never above baseline, measured with the identical command before and after" + - "[edge:boundary/ERR-04] The pinned count command (grep -roE over src-tauri/src for Result<*, String> signatures) reports within [B-20, B] where B is the pre-migration baseline 03-01 measured before any edit — expected [1118, 1138] from the planning-time 1,138 re-confirmed 2026-08-23 on the committed tree at 34f96ee, minus the 10-item migration set, never above baseline, measured with the identical command before and after" - "[ERR-03] The residual-matcher grep over src/ for substring matchers on the four contract codes returns zero (re-run as a phase-gate, not just at 03-03)" - "Full make verify is green with the migrated contract in place" - "D-06: the local, non-IPC matchers stay out of the contract and untouched — `clipboard.ts:30`'s \"clipboard is empty\" match and the `RibbonTable.tsx:84-86` diagram string matches are not migrated and not covered by the ERR-03 residual grep" @@ -104,16 +104,17 @@ Output: drill evidence and the final count in the SUMMARY; full verify green. - Makefile (:321 — verify target) - - ERR-04 guard: re-run the pinned command — `grep -roE "Result<.*, String>" src-tauri/src --include="*.rs" | wc -l` — and assert the result is within [1118, 1138]: the planning baseline is 1,138 (measured 2026-08-23 with this exact command; RESEARCH Pitfall 6 — CONCERNS.md's 1,118 is stale), the migration set is 10 signatures (7 commands + 3 helpers), and the count must never exceed the baseline. If the measured value differs from 03-02's recorded number, investigate before proceeding — the delta must be explainable by the migration set alone (re-measure the baseline only if concurrent sessions touched src-tauri/src since 2026-08-23, per the RESEARCH valid-until note; if so, record the new baseline and apply the same ≤20 delta rule). + - ERR-04 guard: re-run the pinned command — `grep -roE "Result<.*, String>" src-tauri/src --include="*.rs" | wc -l` — and assert the result is within [1118, 1138]: the baseline is B from 03-01-SUMMARY.md, expected to be 1,138 (re-confirmed 2026-08-23 with this exact command on the committed tree at 34f96ee; RESEARCH Pitfall 6 — CONCERNS.md's 1,118 is stale), the migration set is 10 signatures (7 commands + 3 helpers), and the count must never exceed the baseline. If the measured value differs from 03-02's recorded number, investigate before proceeding — the delta must be explainable by the migration set alone (**do NOT re-measure a baseline here.** This plan runs after 03-01 and 03-02 have already flipped signatures, so a measurement taken now observes the post-migration count, not a baseline; recording it as one would let unintended conversions pass. The authoritative baseline is B, measured by 03-01 before any edit and recorded in 03-01-SUMMARY.md. If B differs from the planning-time 1,138 — 19 of those matches live in hwped.rs and the hwp-editor track is still active — derive the band as [B-20, B] and substitute it into this plan's automated check before asserting). - ERR-03 guard: re-run the residual-matcher grep — `grep -rnE '\.includes\("(today_conflict|task_conflict|document_conflict|evidence_binder_revision_conflict)"' src/` — and assert zero matches (the four codes are branched on only via err.code now). Also confirm `grep -rn "todayErrorCode" src/ e2e/` is empty (D-08 retirement held). - - Full gate: run `make verify` and confirm green end-to-end (typecheck, lint, test-ts, test-rust, fmt-check, clippy, build-frontend — Makefile:321). Per the Phase 1 shared-checkout lesson, if a gate fails solely inside files owned by a concurrent session (e.g. uncommitted hwped.rs/lib.rs changes from another session), verify each gate this phase owns individually and record the situation in the SUMMARY rather than diagnosing foreign files. + - Full gate: run `make verify` and confirm green end-to-end (typecheck, lint, test-ts, test-rust, fmt-check, clippy, build-frontend — Makefile:321). The Phase 1 and 2 escape hatch for uncommitted hwped.rs/lib.rs no longer applies: that work is committed as of 34f96ee and `make verify` is green end-to-end on it, so a red gate here is this phase's to diagnose. - Record the final count and both grep results in the SUMMARY. - COUNT=$(grep -roE "Result<.*, String>" src-tauri/src --include="*.rs" | wc -l | tr -d ' ') && [ "$COUNT" -ge 1118 ] && [ "$COUNT" -le 1138 ] && ! grep -rnE '\.includes\("(today_conflict|task_conflict|document_conflict|evidence_binder_revision_conflict)"' src/ && make verify + # Substitute B from 03-01-SUMMARY.md if it differs from 1138: LO=$((B-20)) HI=$B +COUNT=$(grep -roE "Result<.*, String>" src-tauri/src --include="*.rs" | wc -l | tr -d ' ') && [ "$COUNT" -ge 1118 ] && [ "$COUNT" -le 1138 ] && ! grep -rnE '\.includes\("(today_conflict|task_conflict|document_conflict|evidence_binder_revision_conflict)"' src/ && make verify - - The pinned count command prints a value in [1118, 1138] and the value is recorded in the SUMMARY + - The pinned count command prints a value in [B-20, B] (expected [1118, 1138]) and both the value and B are recorded in the SUMMARY - The residual-matcher grep over src/ prints nothing; the retired-parser grep over src/ and e2e/ prints nothing - `make verify` exits 0 (or the Phase 1 shared-checkout exception is documented per gate with each owned gate proven green individually) @@ -149,7 +150,7 @@ Output: drill evidence and the final count in the SUMMARY; full verify green. - Rename drill: red evidence recorded both directions, both files reverted -- ERR-04 count within [1118, 1138] via the pinned command +- ERR-04 count within [B-20, B] via the pinned command, B being 03-01's recorded pre-migration baseline (expected [1118, 1138]) - ERR-03 residual-matcher grep empty; retired-parser grep empty - `make verify` green (or the documented shared-checkout exception) diff --git a/.planning/phases/03-typed-ipc-error-contract/03-CONTEXT.md b/.planning/phases/03-typed-ipc-error-contract/03-CONTEXT.md index 99764de1..565f9f41 100644 --- a/.planning/phases/03-typed-ipc-error-contract/03-CONTEXT.md +++ b/.planning/phases/03-typed-ipc-error-contract/03-CONTEXT.md @@ -10,8 +10,9 @@ Give the errors the frontend branches on a stable machine-readable `code`, so a rename breaks the build on both sides instead of silently breaking a recovery path. Covers ERR-01..04: a typed `{ code, message }` contract over IPC for the branch-on set, a two-sided rename-fails-the-build mechanism, migration of the -existing `message.includes(...)` matchers, and zero touch to the 1,118 -display-only `Result` signatures. +existing `message.includes(...)` matchers, and zero touch to the ~1,138 +display-only `Result` signatures (measured baseline; CONCERNS.md's +1,118 is stale). @@ -95,7 +96,8 @@ display-only `Result` signatures. ### Evidence - `.planning/codebase/CONCERNS.md` §Tech Debt — "Stringly-typed IPC errors": - the 1,118 signatures, the two existing error enums + the 1,118 signatures (stale as written; the measured baseline is 1,138 — + RESEARCH Pitfall 6), the two existing error enums (`src-tauri/src/agent_host/status.rs:351`, `src-tauri/src/hub_client/http.rs:19`), and the prescribed `{ code, message }` struct + `types.ts` mirror approach @@ -136,7 +138,8 @@ display-only `Result` signatures. ## Specific Ideas -- ERR-04's guard is a count: `Result` stays within a few of 1,118 — +- ERR-04's guard is a count: `Result` stays within a few of the + pre-migration baseline 03-01 measures (planning-time figure 1,138) — the four migrated commands are the only signature changes. - The phase's own proof of ERR-02 is a deliberate rename drill: rename a code on one side, watch `make verify` go red on the other, revert (the Phase 1