Adopt the hwp-editor bridge, and land Phase 2 + Phase 3 planning - #277
Merged
Conversation
… helpers - NEW src-tauri/src/paths.rs: GENERATED_DIRS (flat 14-entry union, D-03), ensure_within (promoted lexical containment, D-07), require_absolute (SCAN-04 guard), lexical_normalize re-export (D-01/D-02) - Register mod paths in lib.rs (alphabetical between outlook_mso and project_activity), merging alongside concurrent hwped session edits - workspace_files.rs: drop local pub(crate) const, import shared union - content_search.rs: repoint GENERATED_DIRS import to crate::paths - Unit tests: ensure_within descendant/equal/escape/unrelated, require_absolute absolute/relative (6 new paths::tests)
…t: true - rg_hidden_and_git_traversal_follow_dot_folder_allowlist: allowlisting nested/.git/refs must no longer resurrect .git into rg results (SCAN-02) - RED proof: fails against current rg_visibility (left exclude_git: false)
- rg_visibility: exclude_git is now unconditionally true — .git is in the GENERATED_DIRS union and the rg glob loop prunes it regardless of the dot-folder allowlist (SCAN-02) - vault.rs: remove could_include_dot_folder_named (lost its only caller; dead code under -D warnings) - ScanFilter::from_options untouched — scope reduction stays silent, never a user-facing settings error - GREEN proof: content_search suite 20/20 incl. rg/fallback parity tests (rg 15.0.0 present, tests did not skip)
…vate copy - maru_dir.rs: delete private ensure_within; rule_path and the templates call site resolve through crate::paths::ensure_within (SCAN-03, D-06) - Error message byte-identical: "Path escapes the .maru directory" - Drop now-unused lexical_normalize import from maru_dir.rs - Remove the temporary allow(dead_code) on paths::ensure_within — the first consumer landed in this commit - Untouched: MARUIGNORE_DEFAULTS (13 entries, D-05), studio/diagram sibling copies (D-06)
- scan_excludes_generated_dirs_union_including_pycache: fixture tree with __pycache__/, .git/, .venv/ must surface only keep.md - RED pre-rewire: vault's private 7-entry prune list lacks __pycache__ (non-dot, so the dot-segment rule does not cover it) - failure: left [__pycache__/cached.md, keep.md] vs right [keep.md]
- vault.rs: delete private 7-entry prune list; import crate::paths::GENERATED_DIRS (ScanFilter::is_excluded_path injected-parameter signature preserved) - secrets.rs: delete private 12-entry list; same import; should_prune's vault/.maru/secrets/.secrets domain prefix rules untouched - Task 1 union-proof test now GREEN (red->green pair recorded in SUMMARY)
- project_activity.rs: PRUNED_DIRS[6] deleted; is_pruned_dir membership reads GENERATED_DIRS; dot-prefix rule and Korean doc comment byte-intact - evidence_binder.rs: 5-name matches! replaced by union membership OR module-local .maru check (Maru state, deliberately out of the constant) - inbox.rs empty-slice call sites untouched and green (Pitfall 5); ops_catalog/scan.rs untouched - full lib suite 1212 passed = 1211 baseline + 1 new union-proof test
…-04)
- maru_home_rejects_relative_test_home: relative MARU_TEST_HOME must make
maru_home()/env_root()/install_root_base() return Err with no cwd tree
- Red run: FAILED with Ok("relative-home/.maru") — guard not yet applied
…-04) - maru_home()/install_root_base() validate their final value via crate::paths::require_absolute on every return path, incl. the test-override branch (D-08/D-09, Pitfall 6) — Err-only, no assert/panic - env_root()/skills_root() covered transitively via maru_home() - Drop the temporary #[allow(dead_code)] on require_absolute — its first consumer has landed (closes WINDOWS.md ledger entry 1) - Regression test maru_home_rejects_relative_test_home now green; full lib suite 1213 passed (+1 over the 1212 post-02-02 baseline)
Six Tauri commands (read/render/edit/compose/validate/capabilities) that spawn the hwp binary (hwp-cli >= 0.8.7) behind the hwp-editor HwpEngine contract, mirroring the Node CliEngine so both hosts stay on one contract. - Fixed argv, no shell, 60s timeout, 32MB stdout cap - Documents cross as workspace paths (escape-guarded against workspaceRoot) or base64 bytes; edit ops arrive as JS-side opsToArgv fragments, so Rust owns no op grammar - Binary resolution follows the find_hwpx_tool convention: MARU_HWP_BIN -> augmented PATH -> bundled skill fallbacks - Errors are prefixed strings following the gws convention and stay display-only; src/lib/hwped.ts does no error branching - 6 unit tests cover version parsing, output-name sanitising, the workspace escape guard, and the SVG/PNG size sniffers Backend only. Embedding the editor UI into a Maru mode is a later decision.
- 03-PATTERNS.md: per-file analog map for the 20 files phase 3 touches - 03-04-PLAN.md: record D-06 explicitly - clipboard.ts's "clipboard is empty" match and the RibbonTable.tsx diagram string matches are local, non-IPC matchers that stay out of the contract and out of the ERR-03 residual grep - .planning/config.json: the workflow config the phase ran under
Two tracks were running beside the v1 milestone with no record in it. - ROADMAP.md gains section 11 for the hwp-editor bridge (backend landed, UI embedding undecided, resident MCP server deliberately skipped) and section 10 for the semantica-inspired intelligence track, whose four stages are specified in docs/semantica-adoption-plan.md - The milestone charter forbids new product features, so the bridge is recorded as an adopted exception rather than silently contradicting it: the narrowed rows in REQUIREMENTS.md and PROJECT.md point at a new "Scope Exceptions" section in STATE.md - STATE.md also records that the bridge's error strings are exempt from the phase 3 typed contract (no frontend branches on them), and replaces the now-false ".planning/config.json does not exist" blocker with the live one: phase 3's ERR-04 count band was measured with hwped.rs in the tree, which contributes 19 of the 1,138 matches, so the baseline must be re-measured before phase 3 executes Still owed once the hwp-editor track reports complete: the phase 2.1 planning artifacts, the HWPE-01..03 requirements, and phases 6-9 for the semantica stages.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 20c0482532
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
stage_document canonicalized the joined path, which follows symlinks. A
deliberate workspace symlink whose target sits outside the workspace - the
documented inbox layout - therefore resolved outside the root and every
path-based read/render/edit/validate was rejected as an escape.
This is the exact mistake paths.rs was written to stop ("Using canonicalize()
here was wrong"), so reach for the canonical helper instead of a private copy:
- crate::paths::ensure_within for containment, lexical_normalize for the
returned path, keeping hwped's own prefixed error string
- `..` traversal stays blocked - stage_document_blocks_workspace_escape is
unchanged and still green
- existence is now checked explicitly, since canonicalize() was doubling as
the not-found check
Regression test stage_document_follows_workspace_symlink_to_an_outside_target
is #[cfg(unix)], so Linux CI runs it too. Proven by reverting the body to
canonicalize(): the test goes red with "document path escapes workspaceRoot:
inbox/a.hwpx", then green again on restore.
Reported by Codex review on #277 (P2).
entelecheia
added a commit
that referenced
this pull request
Aug 23, 2026
…st migration (#278) Planning-docs only. No source changes. Phase 3's ERR-04 gate asserts the Result<T, String> signature count lands inside [1118, 1138]. The 1,138 baseline had been measured on a working tree that happened to contain the then-uncommitted hwped.rs. The number was right, but 19 of those matches live in that file, so a checkout predating #277 would have produced a band Phase 3 could not satisfy. - Baseline re-confirmed at 1,138 on the committed tree at 34f96ee, with that commit recorded as the provenance instead of a bare date. - The measurement moves to 03-01's first action, before any file is edited. 03-01 flips evidence_binder_mutate, so neither 03-02 nor 03-04 can establish a pre-migration baseline; 03-04 taking one would observe the post-migration count and let unintended conversions pass. 03-02 now records against B-10 and 03-04 asserts within [B-20, B]. - 03-CONTEXT.md, ROADMAP.md, REQUIREMENTS.md and PROJECT.md all cited CONCERNS.md's stale 1,118 while the plans used 1,138. They now agree, and the CONCERNS.md quotation is marked stale in place rather than rewritten. Both Codex review findings on this PR were defects introduced by its first commit, and are fixed in 66e9198.
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
origin/mainhas been sitting at the Phase 1 merge while three separate trackspiled up locally. This PR pushes all of it, and closes the one thing that was
keeping
make verifyuntrustworthy.What's in here
1. The hwp-editor bridge (new). Six
hwped_*Tauri commands that spawn thehwpbinary (hwp-cli >= 0.8.7) behind the editor'sHwpEnginecontract:read / render / edit / compose / validate / capabilities. Fixed argv, no shell,
60s timeout, 32MB stdout cap, mirroring the Node
CliEngineso both hosts stayon one contract. Documents cross as workspace paths (escape-guarded) or base64;
edit ops arrive as JS-side
opsToArgvfragments, so Rust owns no op grammar.Backend only — embedding the editor UI into a Maru mode is a later decision.
2. Phase 2 execution + Phase 3 planning (already committed locally, never
pushed). 34 commits: the shared prune list and containment helper (SCAN-01..05,
complete), then the full Phase 3 plan set for the typed IPC error contract.
3. The planning record for both off-roadmap tracks.
ROADMAP.mdgainssection 11 (hwp-editor bridge) and section 10 (the semantica-inspired
intelligence track, specified in
docs/semantica-adoption-plan.md).Why the bridge matters beyond the feature
hwped.rswas written by a parallel session and left untracked. While untrackedit failed
cargo clippy -- -D warnings(needless_borrow,useless_format) andcargo fmt --check, which is why three plan summaries recordmake verifyasunprovable locally and defer to CI —
01-06-SUMMARY.md:163,01-07-SUMMARY.md:191,02-02-SUMMARY.md:148. Phase 1's entire deliverable wasa trustworthy verify signal; an uncommitted file breaking two gates is exactly
what that signal exists to catch.
Both clippy findings and the formatting diffs are fixed here (all semantic
no-ops: a needless
&muton a&mut Command,format!("page-")to a literal,and rustfmt's own output including
mod/usereordering).make verifynowpasses end to end — the first green composite run on this checkout since the
gates went live.
Scope exception, recorded rather than smuggled
The v1 milestone charter excludes new product features. The bridge is one, so
rather than let the tree quietly contradict the plan, it is written down: the
Out of Scoperows in.planning/REQUIREMENTS.mdand.planning/PROJECT.mdarenarrowed to point at a new
Scope Exceptionssection in.planning/STATE.md.The milestone owns the gate signal here, not the feature.
Also recorded there: the bridge's prefixed error strings (
cli_missing:,hwp_timeout:, ...) stayResult<T, String>and are exempt from the Phase 3typed contract, because
src/lib/hwped.tsdoes no error branching at all —exactly the display-only class ERR-04 protects from migration.
One live gotcha for the next Phase 3 session
Phase 3's ERR-04 gate is coupled to this file. The pinned command reports
1,138 on this tree and
hwped.rscontributes 19 of those matches.Without it the tree reads 1,119, and after the planned 10-signature migration it
lands at 1,109 — below
03-04-PLAN.md's[1118, 1138]band, gate red. Thebaseline must be re-measured before Phase 3 executes; continued hwp-editor work
keeps moving it. This replaces the now-false ".planning/config.json does not
exist" entry in STATE.md's blockers.
Still owed
Deferred until the hwp-editor implementation track reports complete: the Phase
2.1 planning artifacts, the HWPE-01..03 requirement registration, and Phases 6-9
for the semantica stages.
Verification
make verify→ green end to end (typecheck, lint, release-version-check,icons-check, lint-i18n, guards, test-ts, test-rust 1213/1213, fmt-check,
clippy, build-frontend with the bundle budget)
hwped.rscarries 6 unit tests: version parsing, output-name sanitising, theworkspace escape guard (relative-without-root and escape cases), base64
staging, and the SVG/PNG size sniffers