Repository navigation
V1.1.1 — rule-reachability, false-positive and reliability fixes - #1
Merged
Merged
Conversation
…sitives V1.1.0 shipped entry-format rules that could never fire on a real .docx, because the field extractor destroyed the very formatting it was meant to police, and RR-FMT-015 read two fields nothing ever assigned. Parser (fields.ts): - keep pagesRaw / citedDateRaw as printed, alongside the normalised values - capture the page range span verbatim, so RR-FMT-011 can still see ~ — – - accept full-width punctuation and non-ISO printed cited dates; add normalizeCitedDate() so present-but-undeterminable stays distinguishable from absent - extract place/publisher positionally (no institution keyword list), so a degree-granting unit is recognised whatever it is called - publication year now takes the first plausible 1500-2100 group, so "GB/T 7714—2025" no longer yields year 7714 Rules (definitions.ts): - RR-FMT-011/017 now read the printed spans and fire on real documents - RR-FMT-013 skips any entry carrying a printed cited date; the format problem is reported once, by RR-FMT-017, never as "element missing" - RR-FMT-015 stops flagging correctly formed thesis entries - RR-FMT-018 recognises GB/T, IEC/IEEE, IEEE P, AIAA G, ANSI/ANS, ISO/IEC, part numbers and a responsible body before the number; it matched three of the standard's own examples as "missing" before - RR-FMT-014 cites the table that actually governs each type (adds 表11 for 网页 and 表14 for 数据集) and honours 表14's electronic-resource condition - fix previews are only offered for a span that occurs exactly once New src/core/rules/entry-requirements.ts holds the per-type element facts (element, necessity, clause, table) verified against the official 2025 scan; only bibliographic facts are recorded, the standard's text is not reproduced. New src/core/rules/reachability.test.ts drives DOCX bytes through the whole pipeline and is the permanent gate that unit tests with hand-injected fields could never provide: 20 of its 37 cases failed before this commit.
…fuses The preview could differ from what was copied, and a finding could offer no preview at all without explaining why. - copy now uses split/join on the exact span the panel displays, so `$&`, `$1` and back-quote sequences inside a document cannot be expanded by String.replace, and preview text equals clipboard text by construction - Issue gains fixBlocked: when a mechanical fix is unsafe — the value occurs twice, or “2025-13-45” resolves to no real date — the reason is shown in place of the copy button instead of silence - RR-FMT-011/017 report through mechanicalFix(), which returns either an actionable preview or that reason New coverage: a chromium E2E uploads a real DOCX, reads the actual clipboard and compares the whole corrected entry verbatim (not merely “it changed”), and asserts the duplicated-date entry renders the refusal and no copy button; plus four pipeline-level safety cases in reachability.test.ts.
The old unpacker inflated the whole archive with unzipSync and checked sizes afterwards; the comment claimed periodic aborts that did not exist, and the pre-scan trusted central-directory declarations that an author of the file controls. A lying header therefore bypassed every cap (ratio checks were also skipped whenever a size was declared as 0). Rebuilt on fflate's streaming Unzip, fed in 64 KiB slices: - per-entry and cumulative caps are applied to each chunk as it is produced, and feeding stops as soon as one trips — bounded memory and bounded work - entry names are validated before anything is inflated - only the seven parts the OOXML reader consumes are inflated at all; images, embedded fonts and themes are enumerated and skipped, which also stops a media-heavy paper being refused on size alone - unknown compression methods, decode errors and truncated streams surface as corrupt-zip with fflate's message attached, and a part whose stream never finalised is not published at all (the loader then reports it) rather than parsing half a document - chunk buffers are copied on receipt; the inflater may reuse them - caps are injectable so the boundary can be tested with tiny payloads
P2 items from the takeover review, each with a failing-first case:
- rules/engine.ts: a rule that threw was dropped with `catch { continue }`, so
a broken check looked like a clean report. runRules now takes an onFailure
reporter; pipeline.ts and text-analysis.ts route it into parseWarnings, which
already renders in 分析范围, the HTML report and the PDF. The paste-text path
no longer hardcodes an empty warning list.
- docx/ooxml.ts: flagsEqual omitted `superscript`, so a superscript “[1]”
coalesced into the preceding plain run and citations.ts lost its strongest
signal.
- references/fields.ts: a four-digit group is only a year if it is plausible and
not one end of a dash-joined span, checked against plausible years on both
sides — so “GB/T 7714—2025” yields 2025 while “1901—2024 年…数据集” does not
yield 1901.
- references/parse.ts + pipeline.ts: unlabeled paragraphs could be folded into
one entry without limit, turning a list with lost auto-numbering into a giant
entry plus bogus “missing reference” findings. Merging now caps at two
continuations and records `entry-merge-suspect`; a detected section that
yields no entries at all is disclosed. Neither case can pass silently again.
- public/sw.js: install used allSettled and then skipWaiting unconditionally, so
a partially failed precache was committed while activate deleted the previous
version’s complete cache — offline users got a shell whose chunks 404. The
install now rejects unless every asset and the shell cached, leaving the old
worker in charge.
- docx/unzip.ts: caps parameterised as plain numbers (UnpackCaps) so the
boundary can be tested with kilobyte payloads instead of provoking a real OOM.
Documentation was describing a different build than the code: - limitations.md still carried the v1.0.0 header, “12 rule families” and a blanket “field completeness not implemented”; it now states what is checked, what 有则必备 cannot be judged from text, the segmentation disclosures and the offline-PDF font condition - README (en + zh): the offline claim no longer says report export “included” — the CJK font is deliberately not precached, so PDF needs one prior online export; the non-goals and the v1.3 roadmap now match what shipped in v1.1 - rule-coverage.md: RR-FMT-013/014 clause strings corrected to 8.11.2.1 表10 / 8.11.3.1 表11 / 8.14.2 表14 / 8.15.1 表15; every RR-FMT row describes the behaviour as fixed; a V1.1.1 reachability-audit table records cause → fix; the test-status section now states that injected-field unit cases are necessary but not sufficient - testing.md: V1.1.1 results recorded (239 unit, 14 passed / 4 skipped E2E, audit 0) plus the method note explaining why byte-level reachability exists - verification.md: the element-table re-verification carried out for this release (表4/6/8/10/11/14/15 plus Annex A), failing-first evidence with the per-rule counts, the decompression-boundary method, the full gate table, and an explicit “not verified in this round” list - real-docx-validation.md: a dated correction block instead of a rewrite — the v1.0.2 study’s “zero deterministic false positives” did not cover review-level findings, its cited-date expectation was superseded by the V1.1 audit, and RR-FMT-011/017 silence was never evidence of correctness - CHANGELOG: 1.1.1 entry describing all of the above; the 1.1.0 section’s “185 tests” corrected to the 184 the release commit measures, with its fix-preview verification claim marked as unsupported
package.json 1.1.0 → 1.1.1 and sw.js rr-v1.1.0 → rr-v1.1.1 so the update flow actually ships the fixes to installed clients: an unchanged sw.js byte-for-byte would leave installs on the broken build. Verified — a complete precache still activates, the banner appears on the byte-diff, caches migrate to the new version, the previous version's cache is then evicted, and analysis plus HTML export work offline afterwards. Nothing is tagged and no GitHub Release is created; the version is only merged on the maintainer's decision.
scripts/pwa-update-offline-check.mjs installs the built worker, bumps only dist/sw.js (gitignored build output, restored in finally), and asserts the ten steps that matter for installed users: complete precache, shell cached, asset list present, banner on the byte-diff, caches migrated, the previous version’s cache evicted, offline analysis and offline HTML export after the update, and zero page errors throughout. All ten pass on this branch; nothing under version control is modified by running it. testing.md and verification.md record it, and the “not verified” note is now precise: the happy path is proven, the incomplete-precache refusal is not forced by any test and rests on inspection.
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.
Independent acceptance of this branch (executed locally, all green):
reachability.test.tsgate that drives the REAL pipeline (DOCX bytes → rules) because hand-injected
field unit cases could not detect unreachable rules, and
bounds.test.tswhich crafts archives with lying headers and asserts caps are enforced on
bytes the inflater actually produces
pnpm audit: no known vulnerabilitiesoffline emulation are Chromium-only — recorded reasons)
Key fixes: entry-format rules made reachable from real documents; four false
positives removed (013/015/018 + copy-path); decompression caps enforced on
produced bytes instead of declarations; rule failures surfaced as warnings
instead of silent fake-success; SW install rejects incomplete precaches so the
previous cache always survives; honest parse warnings for unsegmentable
reference lists.