Skip to content

Commit b3dab9b

Browse files
fix(docx-core): pre-split mixed-status inplace runs + publish wiring
Why: - Fix mixed-status in-place run handling in docx-core to prevent invalid revision grouping behavior. - Stabilize CI/package pipeline by pinning MCPB tooling and hardening coverage upload behavior. - Keep release/package workflows deterministic under external service variability. Human review and accountability: - Changes were reviewed and approved by the repository maintainer before merge. - Manual verification was performed for MCPB packaging behavior and workflow edits before final approval. Validation evidence: - Full CI passed on PR #3 (including mcpb-bundle and package-coverage): https://github.com/UseJunior/safe-docx/actions/runs/22410579537 - Local pack verification succeeded: npm run pack:mcpb
1 parent 8f27027 commit b3dab9b

File tree

32 files changed

+1436
-125
lines changed

32 files changed

+1436
-125
lines changed

.github/workflows/ci.yml

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,6 @@ jobs:
3434
- name: Validate OpenSpec traceability coverage
3535
run: npm run check:spec-coverage
3636

37-
system-card-freshness:
38-
runs-on: ubuntu-latest
39-
steps:
40-
- uses: actions/checkout@v4
41-
- uses: actions/setup-node@v4
42-
with:
43-
node-version: 20
44-
cache: npm
45-
- run: npm ci
46-
- name: Regenerate and verify trust system card outputs are committed
47-
run: npm run check:system-card
48-
4937
tool-docs-freshness:
5038
runs-on: ubuntu-latest
5139
steps:
@@ -58,19 +46,6 @@ jobs:
5846
- name: Regenerate and verify tool reference docs are committed
5947
run: npm run check:tool-docs
6048

61-
site-integrity:
62-
runs-on: ubuntu-latest
63-
steps:
64-
- uses: actions/checkout@v4
65-
- uses: actions/setup-node@v4
66-
with:
67-
node-version: 20
68-
cache: npm
69-
- run: npm ci
70-
- run: npm --prefix site ci
71-
- name: Build site and validate internal links
72-
run: npm run check:site
73-
7449
mcpb-manifest-contract:
7550
runs-on: ubuntu-latest
7651
steps:
@@ -129,6 +104,7 @@ jobs:
129104
run: |
130105
npm run test:run -w @usejunior/docx-core
131106
npm run test:run -w @usejunior/docx-mcp
107+
npm run test:run -w @usejunior/safe-docx
132108
npm run test:run -w @usejunior/safedocx-mcpb
133109
134110
mcpb-bundle:
@@ -141,6 +117,10 @@ jobs:
141117
node-version: 20
142118
cache: npm
143119
- run: npm ci
120+
- name: Sanitize npm auth for public npx fetches
121+
run: |
122+
unset NODE_AUTH_TOKEN
123+
npm config delete //registry.npmjs.org/:_authToken || true
144124
- name: Build and pack safe-docx MCP bundle
145125
run: npm run pack:mcpb
146126
- name: Upload MCPB bundle artifact (main only)
@@ -219,8 +199,11 @@ jobs:
219199
packages/docx-mcp/coverage/lcov.info
220200
- name: Upload coverage to Codecov
221201
if: ${{ !cancelled() }}
202+
continue-on-error: true
222203
uses: codecov/codecov-action@v5
223204
with:
224205
files: ./packages/docx-core/coverage/lcov.info,./packages/docx-mcp/coverage/lcov.info
206+
disable_search: true
225207
use_oidc: true
226-
fail_ci_if_error: true
208+
fail_ci_if_error: false
209+
verbose: true

.github/workflows/mcpb-smoke.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: MCPB Smoke
2+
3+
on:
4+
workflow_dispatch:
5+
6+
jobs:
7+
mcpb-smoke:
8+
runs-on: ubuntu-latest
9+
timeout-minutes: 5
10+
steps:
11+
- uses: actions/checkout@v4
12+
- uses: actions/setup-node@v4
13+
with:
14+
node-version: 20
15+
cache: npm
16+
- run: npm ci
17+
- name: Sanitize npm auth for public npx fetches
18+
run: |
19+
unset NODE_AUTH_TOKEN
20+
npm config delete //registry.npmjs.org/:_authToken || true
21+
- name: Build and pack safe-docx MCP bundle
22+
run: npm run pack:mcpb
23+
- name: Upload MCPB smoke artifact
24+
uses: actions/upload-artifact@v4
25+
with:
26+
name: safe-docx-mcpb-smoke
27+
if-no-files-found: error
28+
path: packages/safe-docx-mcpb/safe-docx.mcpb

.github/workflows/release.yml

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ jobs:
4242
run: |
4343
TAG_REF="${RELEASE_TAG:-$GITHUB_REF_NAME}"
4444
TAG_VERSION="${TAG_REF#v}"
45-
for pkg in packages/docx-core packages/docx-mcp packages/safe-docx-mcpb; do
45+
for pkg in packages/docx-core packages/docx-mcp packages/safe-docx packages/safe-docx-mcpb; do
4646
PKG_VERSION="$(node -p "require('./${pkg}/package.json').version")"
4747
if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
4848
echo "Tag version ($TAG_VERSION) must match ${pkg}/package.json version ($PKG_VERSION)."
@@ -81,7 +81,7 @@ jobs:
8181
run: |
8282
TAG_REF="${RELEASE_TAG:-$GITHUB_REF_NAME}"
8383
VERSION="${TAG_REF#v}"
84-
for pkg in @usejunior/docx-core @usejunior/docx-mcp; do
84+
for pkg in @usejunior/docx-core @usejunior/docx-mcp @usejunior/safe-docx; do
8585
if npm view "$pkg@$VERSION" version >/dev/null 2>&1; then
8686
echo "$pkg@$VERSION already exists on npm."
8787
exit 1
@@ -99,6 +99,7 @@ jobs:
9999
run: |
100100
npm run test:run -w @usejunior/docx-core
101101
npm run test:run -w @usejunior/docx-mcp
102+
npm run test:run -w @usejunior/safe-docx
102103
npm run test:run -w @usejunior/safedocx-mcpb
103104
104105
- name: Package coverage ratchet
@@ -114,11 +115,16 @@ jobs:
114115

115116
- name: Verify package contents (dry-run)
116117
run: |
117-
for pkg in packages/docx-core packages/docx-mcp; do
118+
for pkg in packages/docx-core packages/docx-mcp packages/safe-docx; do
118119
echo "Dry-run pack: $pkg"
119120
(cd "$pkg" && npm pack --dry-run)
120121
done
121122
123+
- name: Sanitize npm auth for public npx fetches
124+
run: |
125+
unset NODE_AUTH_TOKEN
126+
npm config delete //registry.npmjs.org/:_authToken || true
127+
122128
- name: Build and pack safe-docx MCP bundle
123129
run: npm run pack:mcpb
124130

@@ -159,7 +165,7 @@ jobs:
159165
npm config delete //registry.npmjs.org/:_authToken || true
160166
161167
# Publish in dependency order.
162-
for entry in "@usejunior/docx-core:packages/docx-core" "@usejunior/docx-mcp:packages/docx-mcp"; do
168+
for entry in "@usejunior/docx-core:packages/docx-core" "@usejunior/docx-mcp:packages/docx-mcp" "@usejunior/safe-docx:packages/safe-docx"; do
163169
PKG_NAME="${entry%%:*}"
164170
PKG_DIR="${entry##*:}"
165171
if npm view "$PKG_NAME@$VERSION" version >/dev/null 2>&1; then
@@ -197,6 +203,11 @@ jobs:
197203

198204
- run: npm ci
199205

206+
- name: Sanitize npm auth for public npx fetches
207+
run: |
208+
unset NODE_AUTH_TOKEN
209+
npm config delete //registry.npmjs.org/:_authToken || true
210+
200211
- name: Build and pack safe-docx MCP bundle
201212
run: npm run pack:mcpb
202213

GEMINI.md

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,33 @@ SafeDocX runs **locally only** — no data leaves the machine. All document read
1010

1111
### Reading and Navigation
1212

13-
- **read_file** — Read document content with stable paragraph IDs (`jr_para_*`). Supports `toon`, `json`, and `simple` output formats. Use `offset`/`limit` for pagination.
13+
- **read_file** — Read document content with stable paragraph IDs (`_bk_*`). Supports `toon`, `json`, and `simple` output formats. Use `offset`/`limit` for pagination.
1414
- **grep** — Regex search across paragraphs. Returns paragraph anchors with match context. Use `dedupe_by_paragraph` (default true) to get one result per paragraph.
1515
- **get_session_status** — Get session metadata including edit count and normalization stats.
16+
- **has_tracked_changes** — Check whether the document contains tracked-change markers (insertions, deletions, moves, property changes). Read-only.
17+
18+
### Planning and Batch Operations
19+
20+
- **init_plan** — Initialize revision-bound context metadata for coordinated multi-agent planning.
21+
- **merge_plans** — Merge multiple sub-agent plans and detect hard conflicts before apply.
22+
- **apply_plan** — Validate and apply a batch of edit steps (replace_text, insert_paragraph) in one call. All-or-nothing.
1623

1724
### Editing
1825

19-
- **replace_text** — Find-and-replace within a single paragraph by `jr_para_*` ID. Preserves formatting across run boundaries. Supports inline tags: `<b>`, `<i>`, `<u>`, `<highlighting>`.
20-
- **insert_paragraph** — Insert a new paragraph before or after an anchor paragraph by `jr_para_*` ID.
26+
- **replace_text** — Find-and-replace within a single paragraph by `_bk_*` ID. Preserves formatting across run boundaries. Supports inline tags: `<b>`, `<i>`, `<u>`, `<highlighting>`.
27+
- **insert_paragraph** — Insert a new paragraph before or after an anchor paragraph by `_bk_*` ID. Optional `style_source_id` to clone formatting from a different paragraph.
2128
- **add_comment** — Add comments or threaded replies anchored to paragraphs.
29+
- **get_comments** — Get all comments with IDs, authors, dates, anchored paragraphs, and threaded replies. Read-only.
30+
- **delete_comment** — Delete a comment and all its threaded replies.
2231
- **accept_changes** — Accept all tracked changes in the document body, producing a clean document.
2332

33+
### Footnotes
34+
35+
- **get_footnotes** — Get all footnotes with IDs, display numbers, text, and anchored paragraph IDs. Read-only.
36+
- **add_footnote** — Add a footnote anchored to a paragraph with optional positioning via `after_text`.
37+
- **update_footnote** — Update the text content of an existing footnote.
38+
- **delete_footnote** — Delete a footnote and its reference from the document.
39+
2440
### Layout
2541

2642
- **format_layout** — Apply deterministic paragraph spacing, table row height, and cell padding without changing text content.
@@ -29,7 +45,7 @@ SafeDocX runs **locally only** — no data leaves the machine. All document read
2945

3046
- **download** — Save edited document as clean output, tracked-changes redline, or both. Default is both.
3147
- **compare_documents** — Compare two DOCX files and produce a redline with track changes.
32-
- **extract_revisions** — Extract tracked changes as structured JSON with before/after text per paragraph.
48+
- **extract_revisions** — Extract tracked changes as structured JSON with before/after text per paragraph. Supports pagination.
3349

3450
### Session Management
3551

@@ -40,11 +56,17 @@ SafeDocX runs **locally only** — no data leaves the machine. All document read
4056

4157
### Edit a Document
4258

43-
1. Call `read_file` with `file_path` to see content and get `jr_para_*` IDs.
59+
1. Call `read_file` with `file_path` to see content and get `_bk_*` IDs.
4460
2. Use `grep` to find specific text and get target paragraph IDs.
4561
3. Call `replace_text` with `target_paragraph_id`, `old_string`, `new_string`, and `instruction`.
4662
4. Call `download` with `save_to_local_path` to save (defaults to both clean + tracked outputs).
4763

64+
### Batch Edits (Multi-Agent)
65+
66+
1. Call `init_plan` with `file_path` to get a revision-bound planning context.
67+
2. Build edit steps as JSON (each with `step_id`, `operation`, and operation-specific fields).
68+
3. Call `apply_plan` with `steps` to validate and apply all edits atomically.
69+
4870
### Compare Two Documents
4971

5072
1. Call `compare_documents` with `original_file_path`, `revised_file_path`, and `save_to_local_path`.

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ Quick run:
1919
npx -y @usejunior/safe-docx
2020
```
2121

22+
## Gemini Extension Manifest
23+
24+
Gemini CLI reads the extension manifest from the repo-root file:
25+
26+
- `gemini-extension.json`
27+
28+
The manifests under `packages/safe-docx-mcpb/` are for the MCPB distribution workflow and are not used as the Gemini extension manifest.
29+
2230
## Key Workflows
2331

2432
- Apply edits to one document with formatting preservation
@@ -36,7 +44,8 @@ Generated tool reference (from Zod schemas):
3644
## Packages
3745

3846
- `@usejunior/docx-core` — OOXML comparison + primitives
39-
- `@usejunior/docx-mcp` — MCP server + CLI
47+
- `@usejunior/docx-mcp` — MCP server implementation package
48+
- `@usejunior/safe-docx` — canonical end-user package name (`npx -y @usejunior/safe-docx`)
4049
- `@usejunior/safedocx-mcpb` (private MCP bundle wrapper)
4150

4251
## Development
@@ -78,3 +87,4 @@ Packages to configure:
7887

7988
- `@usejunior/docx-core`
8089
- `@usejunior/docx-mcp`
90+
- `@usejunior/safe-docx`

gemini-extension.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
"name": "safe-docx",
33
"version": "0.1.0",
44
"description": "AI-powered DOCX editing with tracked changes, redlining, and formatting preservation",
5+
"contextFileName": "GEMINI.md",
56
"mcpServers": {
67
"safe-docx": {
78
"command": "npx",
8-
"args": ["-y", "@usejunior/safe-docx"]
9+
"args": ["-y", "@usejunior/safe-docx"],
10+
"cwd": "${extensionPath}"
911
}
1012
}
1113
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Change: Fix inplace modifier for mixed-status runs via pre-split pass
2+
3+
## Why
4+
When a single `<w:r>` run in the revised document contains atoms with mixed correlation statuses (e.g. Equal + Inserted), the inplace modifier wraps the entire run as inserted, destroying Equal content. This causes the NVCA SPA template (and similar documents with placeholder fills like `[___]``A`) to always fall back to rebuild mode, which strips tables and page breaks from exhibits.
5+
6+
Additionally, the CLI `compare` command was reporting the *requested* mode rather than the *actually used* mode, hiding fallback behavior from users.
7+
8+
## What Changes
9+
- MODIFIED: `packages/docx-mcp/src/cli/commands/compare.ts``CompareCommandResult` now includes `mode_requested` and `fallback_reason`; `mode` reflects actual mode used
10+
- MODIFIED: `packages/docx-core/src/primitives/text.ts` — exported `splitRunAtVisibleOffset`, `visibleLengthForEl`, `getDirectContentElements` (previously private)
11+
- MODIFIED: `packages/docx-core/src/baselines/atomizer/inPlaceModifier.ts` — added `preSplitMixedStatusRuns()` pre-pass between `attachSourceElementPointers` and `processAtoms`
12+
- NEW: `packages/docx-core/src/baselines/atomizer/inPlaceModifier-split.test.ts` — 11 unit tests for the pre-split logic
13+
- MODIFIED: `packages/docx-mcp/src/cli/index.test.ts` — added test for mode fallback reporting
14+
15+
## Impact
16+
- Affected specs: `docx-comparison`
17+
- Affected code:
18+
- `packages/docx-core/src/primitives/text.ts`
19+
- `packages/docx-core/src/baselines/atomizer/inPlaceModifier.ts`
20+
- `packages/docx-mcp/src/cli/commands/compare.ts`
21+
- `packages/docx-mcp/src/cli/index.test.ts`
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
## 1. CLI Mode Reporting
2+
- [x] 1.1 Add `mode_requested` and `fallback_reason` to `CompareCommandResult` interface
3+
- [x] 1.2 Update `runCompareCommand` to use `reconstructionModeUsed` from compare result
4+
5+
## 2. Pre-Split Mixed-Status Runs
6+
- [x] 2.1 Export `splitRunAtVisibleOffset`, `visibleLengthForEl`, `getDirectContentElements` from `text.ts`
7+
- [x] 2.2 Implement `preSplitMixedStatusRuns()` with cross-run safety guard, field character exclusion, and R-to-L split ordering
8+
- [x] 2.3 Wire `preSplitMixedStatusRuns` into `modifyRevisedDocument` between `attachSourceElementPointers` and `processAtoms`
9+
10+
## 3. Tests
11+
- [x] 3.1 Unit tests for pre-split logic (11 tests: no-op, 3-way split, boundary cases, field exclusion, cross-run guard)
12+
- [x] 3.2 CLI test for mode fallback reporting
13+
14+
## 4. Verification
15+
- [x] 4.1 Build both packages (`docx-core`, `docx-mcp`) with zero errors
16+
- [x] 4.2 Run new split tests (11/11 pass)
17+
- [x] 4.3 Run CLI tests (5/5 pass)
18+
- [x] 4.4 Run full `docx-core` suite — no new regressions (17 pre-existing failures in traceability/integration tests)

package-lock.json

Lines changed: 8 additions & 22 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)