Skip to content

Adopt the hwp-editor bridge, and land Phase 2 + Phase 3 planning - #277

Merged
entelecheia merged 38 commits into
mainfrom
feat/hwp-editor-bridge
Aug 23, 2026
Merged

Adopt the hwp-editor bridge, and land Phase 2 + Phase 3 planning#277
entelecheia merged 38 commits into
mainfrom
feat/hwp-editor-bridge

Conversation

@entelecheia

Copy link
Copy Markdown
Member

origin/main has been sitting at the Phase 1 merge while three separate tracks
piled up locally. This PR pushes all of it, and closes the one thing that was
keeping make verify untrustworthy.

What's in here

1. The hwp-editor bridge (new). Six hwped_* Tauri commands that spawn the
hwp binary (hwp-cli >= 0.8.7) behind the editor's HwpEngine contract:
read / render / edit / compose / validate / capabilities. Fixed argv, no shell,
60s timeout, 32MB stdout cap, mirroring the Node CliEngine so both hosts stay
on one contract. Documents cross as workspace paths (escape-guarded) or base64;
edit ops arrive as JS-side opsToArgv fragments, 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.md gains
section 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.rs was written by a parallel session and left untracked. While untracked
it failed cargo clippy -- -D warnings (needless_borrow, useless_format) and
cargo fmt --check, which is why three plan summaries record make verify as
unprovable 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 was
a 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 &mut on a &mut Command, format!("page-") to a literal,
and rustfmt's own output including mod/use reordering). make verify now
passes 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 Scope rows in .planning/REQUIREMENTS.md and .planning/PROJECT.md are
narrowed to point at a new Scope Exceptions section 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:, ...) stay Result<T, String> and are exempt from the Phase 3
typed contract
, because src/lib/hwped.ts does 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.rs contributes 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. The
baseline 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.rs carries 6 unit tests: version parsing, output-name sanitising, the
    workspace escape guard (relative-without-root and escape cases), base64
    staging, and the SVG/PNG size sniffers

… 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.
Copilot AI lite review requested due to automatic review settings August 23, 2026 00:51

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src-tauri/src/hwped.rs Outdated
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
entelecheia merged commit 34f96ee into main Aug 23, 2026
1 check passed
@entelecheia
entelecheia deleted the feat/hwp-editor-bridge branch August 23, 2026 01:20
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants