asciidoc-pubkit 0.6.1 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +250 -46
- data/lib/asciidoc_pubkit/rules.rb +50 -12
- data/lib/asciidoc_pubkit.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7ce6fe84fae75593d9444c1db5d7abb1f6c96823b77376f90750d9554358af76
|
|
4
|
+
data.tar.gz: 924c12f91b714b34c1883c5a0583259d2c97989a328d412e6648d681de01984c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b194a8c6af0423b90c41275a1287bf1bdf0b6edee930a27b99bf9cddcce15d8f348e4ea58b3008e2f0f2ef533c9b4a4d43cd74262d3cd01c00b051e96aead0ba
|
|
7
|
+
data.tar.gz: ea6b5543a41d7f7d4780b3626f1e33797c55198d428f413295e9576894fc30e92b6ca890b7461382ac069612b7a48027250bcd8c6ef937bc59fd205bc5f58be6
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.6.2 — 2026-10-01
|
|
6
|
+
|
|
7
|
+
- Explain review and scoring architecture, flows, and algorithms in a separate
|
|
8
|
+
README section with Mermaid diagrams. Add concrete applications for each
|
|
9
|
+
editorial and research reference.
|
|
10
|
+
|
|
11
|
+
- Locate `repeated-ending` candidates at the matching ending in the third
|
|
12
|
+
consecutive sentence, preserving Unicode columns and line offsets. Do not
|
|
13
|
+
join repetitions across a sentence without a matching ending.
|
|
14
|
+
- Extend four configured metaphorical-operation phrases with MeCab/IPADIC
|
|
15
|
+
predicate matching, including past, negative, and auxiliary forms. Retain
|
|
16
|
+
polarity, custom-rule replacement, allow lists, protected text, and longest
|
|
17
|
+
phrase precedence; literal mode keeps exact phrase matching.
|
|
18
|
+
|
|
5
19
|
## 0.6.1 — 2026-10-01
|
|
6
20
|
|
|
7
21
|
- Add `review score FILE` for direct AsciiDoc manuscript scoring without a
|
data/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
A toolkit for authoring and reviewing AsciiDoc books.
|
|
9
9
|
|
|
10
|
-
Version 0.6.
|
|
10
|
+
Version 0.6.2 provides shared Japanese technical writing criteria, an authoring
|
|
11
11
|
prompt, contextual review prompts, manuscript scoring, and baseline verification
|
|
12
12
|
for edited manuscripts.
|
|
13
13
|
The CLI, diagnostics, documentation, and generated instructions are in English.
|
|
@@ -23,7 +23,7 @@ Starting with version 0.1.1, the default review backend requires the external Me
|
|
|
23
23
|
command and a UTF-8 IPADIC dictionary. Node.js and textlint are not required.
|
|
24
24
|
Explicit `--tokenizer literal` mode provides limited phrase matching without MeCab.
|
|
25
25
|
|
|
26
|
-
Version 0.6.
|
|
26
|
+
Version 0.6.2 also provides `review score FILE` for a manuscript score.
|
|
27
27
|
It uses local candidate analysis by default; `--agent codex` or `--agent claude`
|
|
28
28
|
explicitly asks an installed CLI to evaluate readability and may connect to its
|
|
29
29
|
configured model provider. No scoring mode edits manuscripts.
|
|
@@ -80,7 +80,7 @@ Add the gem to your project's `Gemfile` to manage its version with Bundler:
|
|
|
80
80
|
|
|
81
81
|
```ruby
|
|
82
82
|
source 'https://rubygems.org'
|
|
83
|
-
gem 'asciidoc-pubkit', '~> 0.6.
|
|
83
|
+
gem 'asciidoc-pubkit', '~> 0.6.2'
|
|
84
84
|
```
|
|
85
85
|
|
|
86
86
|
Then install dependencies and run the CLI through Bundler:
|
|
@@ -102,7 +102,7 @@ git clone https://github.com/cybergarage/asciidoc-pubkit.git
|
|
|
102
102
|
cd asciidoc-pubkit
|
|
103
103
|
bundle install
|
|
104
104
|
gem build asciidoc-pubkit.gemspec
|
|
105
|
-
gem install ./asciidoc-pubkit-0.6.
|
|
105
|
+
gem install ./asciidoc-pubkit-0.6.2.gem
|
|
106
106
|
asciidoc-pubkit --version
|
|
107
107
|
```
|
|
108
108
|
|
|
@@ -126,7 +126,7 @@ asciidoc-pubkit writing prompt --lang ja --output writing-prompt.md
|
|
|
126
126
|
```
|
|
127
127
|
|
|
128
128
|
For example, `--lang en` exits with an unsupported-language error. No English
|
|
129
|
-
review or writing criteria are shipped in 0.6.
|
|
129
|
+
review or writing criteria are shipped in 0.6.2.
|
|
130
130
|
|
|
131
131
|
For a small trial, use `examples/book.adoc` as the scan input. Its Japanese
|
|
132
132
|
paragraphs deliberately contain review candidates; its code block must remain
|
|
@@ -144,29 +144,13 @@ asciidoc-pubkit review score book.adoc --agent codex
|
|
|
144
144
|
asciidoc-pubkit review score book.adoc --agent claude --json
|
|
145
145
|
```
|
|
146
146
|
|
|
147
|
-
Without `--agent`,
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
Successful scoring exits 0 regardless of the score; invalid input, configuration,
|
|
155
|
-
analyzer, evaluator, or output failures exit 2. Verification exit codes are
|
|
156
|
-
unchanged. The JSON report includes candidate counts, findings, coverage notices,
|
|
157
|
-
analysis identity, and the formula. Rule, allow-list, and tokenizer changes can
|
|
158
|
-
change the score even when the manuscript is unchanged.
|
|
159
|
-
|
|
160
|
-
With `--agent`, `llm-readability-v1` displays an evaluator score instead. Sentence
|
|
161
|
-
clarity and paragraph coherence are each rated 1–3 per paragraph and mapped to
|
|
162
|
-
0, 50, and 100. The displayed score averages these two dimensions across scored
|
|
163
|
-
paragraphs with equal paragraph weights. JSON also retains the individual
|
|
164
|
-
ratings, English reasons, source locations, evaluator model and CLI version,
|
|
165
|
-
rubric fingerprint, and the mechanical candidate-density score. These ordinal
|
|
166
|
-
averages are descriptive summaries, not validated interval measurements. This
|
|
167
|
-
single-document evaluation has no reference manuscript or evidence checklist;
|
|
168
|
-
it does not measure fact retention, technical accuracy, or meaning preservation.
|
|
169
|
-
`meaning_verified` remains `false` in both modes.
|
|
147
|
+
Without `--agent`, the report shows a mechanical candidate-density indicator.
|
|
148
|
+
With `--agent`, it shows a model readability rating and retains the mechanical
|
|
149
|
+
score in JSON. See [How review and scoring work](#how-review-and-scoring-work)
|
|
150
|
+
for the analysis pipeline, formulas, and interpretation of these different scores.
|
|
151
|
+
Successful scoring exits 0 regardless of the score; input, configuration,
|
|
152
|
+
analyzer, evaluator, or output failures exit 2. No scoring mode establishes
|
|
153
|
+
technical accuracy or meaning preservation; `meaning_verified` remains `false`.
|
|
170
154
|
|
|
171
155
|
Scoring uses the same configuration discovery, custom rules, glossary, allows,
|
|
172
156
|
style, language, base directory, include handling, and prose coverage as scanning.
|
|
@@ -368,9 +352,192 @@ or release readiness: `meaning_verified` is always `false`.
|
|
|
368
352
|
| `1` | Verification found protected-content, structure, source, or parsing issues |
|
|
369
353
|
| `2` | Invalid arguments, configuration, session, scan input, or output failure |
|
|
370
354
|
|
|
355
|
+
## How review and scoring work
|
|
356
|
+
|
|
357
|
+
This section explains the implementation independently of command syntax.
|
|
358
|
+
[Review workflow](#review-workflow) covers commands and session handling;
|
|
359
|
+
[Score an AsciiDoc manuscript](#score-an-asciidoc-manuscript) covers scoring options.
|
|
360
|
+
The diagrams use GitHub-supported Mermaid fenced blocks.
|
|
361
|
+
|
|
362
|
+
### Shared components and prose selection
|
|
363
|
+
|
|
364
|
+
Both paths resolve the same settings and parse the AsciiDoc entry file with
|
|
365
|
+
Asciidoctor, including local includes. `Document` selects running-prose paragraphs
|
|
366
|
+
and maps them back to their source files. Ambiguous mappings become coverage
|
|
367
|
+
notices; the analyzer does not guess locations. Headings provide context, while
|
|
368
|
+
code, tables, quotations, lists, and protected blocks are excluded from prose
|
|
369
|
+
analysis. Inline code, quoted spans, links, and other protected constructs are
|
|
370
|
+
masked with spaces that preserve character offsets and newlines.
|
|
371
|
+
|
|
372
|
+
```mermaid
|
|
373
|
+
flowchart TD
|
|
374
|
+
A["AsciiDoc entry and local includes"] --> D["Document: parse and map running prose"]
|
|
375
|
+
C["Settings: options, configuration, resolved rules"] --> D
|
|
376
|
+
D --> P["Paragraphs with source locations and headings"]
|
|
377
|
+
D --> N["Coverage notices and parser diagnostics"]
|
|
378
|
+
P --> M["Rules: mask protected inline content"]
|
|
379
|
+
C --> M
|
|
380
|
+
M --> F["Candidate detection"]
|
|
381
|
+
F --> R["Session: review baseline and evidence"]
|
|
382
|
+
F --> S["Score: manuscript indicators"]
|
|
383
|
+
P -->|"Original prose and headings"| S
|
|
384
|
+
N --> R
|
|
385
|
+
N --> S
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
`Settings` resolves rule precedence as command-line `--rules`, configuration
|
|
389
|
+
`review.rules`, then packaged defaults. A custom rule file replaces the whole
|
|
390
|
+
set. `Rules` returns review candidates with one-based Unicode line and column
|
|
391
|
+
positions; a match does not establish a defect. `Session` saves evidence for a
|
|
392
|
+
later review, while `Score` returns a report directly without creating a session.
|
|
393
|
+
Parser diagnostics must be resolved before scanning or scoring.
|
|
394
|
+
|
|
395
|
+
### Review: collect, judge, and verify
|
|
396
|
+
|
|
397
|
+
Scanning freezes the source baseline, resolved settings and rules, analyzer
|
|
398
|
+
identity, and writing criteria. The session consists of `manifest.json`,
|
|
399
|
+
`document.json`, `findings.json`, and `baseline/`. Prompt generation checks
|
|
400
|
+
artifact integrity and rejects sources changed since the scan. It emits one
|
|
401
|
+
Markdown file containing the saved criteria, context, every selected paragraph
|
|
402
|
+
in document order, and its candidate evidence, including paragraphs with no
|
|
403
|
+
candidates. Generating that prompt requires no analyzer or model invocation.
|
|
404
|
+
|
|
405
|
+
```mermaid
|
|
406
|
+
sequenceDiagram
|
|
407
|
+
actor Reviewer as Human or external agent
|
|
408
|
+
participant CLI as pubkit CLI
|
|
409
|
+
participant Sources as Manuscript files
|
|
410
|
+
participant Session as Saved review session
|
|
411
|
+
Reviewer->>CLI: review scan FILE
|
|
412
|
+
CLI->>Sources: Parse prose and detect candidates
|
|
413
|
+
CLI->>Session: Save evidence and source baseline
|
|
414
|
+
Reviewer->>CLI: review prompt SESSION
|
|
415
|
+
CLI->>Session: Check integrity and read saved evidence
|
|
416
|
+
CLI->>Sources: Check sources still match scan
|
|
417
|
+
CLI-->>Reviewer: One Markdown review prompt
|
|
418
|
+
Reviewer->>Sources: Judge context and edit prose externally
|
|
419
|
+
Reviewer->>CLI: review verify SESSION
|
|
420
|
+
CLI->>Session: Read original baseline and settings
|
|
421
|
+
CLI->>Sources: Reparse and compare protected content
|
|
422
|
+
CLI-->>Reviewer: Mechanical result, notices, remaining candidates
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
The reviewer decides whether to keep or revise each paragraph and checks source
|
|
426
|
+
evidence before adding claims. `scan`, `prompt`, and `verify` never invoke an AI
|
|
427
|
+
CLI or edit manuscripts. Verification compares the included source set, content
|
|
428
|
+
outside reviewed prose, protected inline tokens, document structure, and analyzer
|
|
429
|
+
identity against the saved baseline. Numeric changes produce manual-review
|
|
430
|
+
notices; remaining candidates alone do not fail verification. A passing result
|
|
431
|
+
means these mechanical checks passed, with `meaning_verified: false`.
|
|
432
|
+
|
|
433
|
+
### Detection: contextual rules and token boundaries
|
|
434
|
+
|
|
435
|
+
MeCab with UTF-8 IPADIC is the default analyzer. It supplies surfaces, dictionary
|
|
436
|
+
forms, part of speech, and source-aligned token spans. Explicit literal mode
|
|
437
|
+
matches configured surface strings; there is no silent fallback. Both modes
|
|
438
|
+
apply glossary, configured prose style, and sentence-ending checks.
|
|
439
|
+
|
|
440
|
+
```mermaid
|
|
441
|
+
flowchart TD
|
|
442
|
+
P["Masked running prose"] --> B{"Selected tokenizer"}
|
|
443
|
+
B -->|"mecab, default"| T["MeCab and IPADIC tokens with source offsets"]
|
|
444
|
+
T --> L["Lemma rules with allow lists and contextual overlap checks"]
|
|
445
|
+
T --> X["Four enabled contextual predicate patterns"]
|
|
446
|
+
B -->|"literal, explicit"| E["Exact configured terms"]
|
|
447
|
+
P --> Q["Exact framing and contextual phrases"]
|
|
448
|
+
L --> F["Candidate findings"]
|
|
449
|
+
X --> O["Prefer most specific phrase; apply allow lists"]
|
|
450
|
+
E --> O
|
|
451
|
+
Q --> O
|
|
452
|
+
O --> F
|
|
453
|
+
P --> G["Glossary, style, and repeated endings"]
|
|
454
|
+
G --> F
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
Morphological rules match configured noun and adjective lemmas, canonical verb
|
|
458
|
+
forms, adjacent compound nouns, and sahen nouns followed by `する` or `できる`.
|
|
459
|
+
Predicate spans extend through adjacent auxiliaries, dependent verbs, and
|
|
460
|
+
supported connective particles. Negative auxiliaries preserve polarity;
|
|
461
|
+
negative-only rules require that evidence. Unknown tokens are not guessed.
|
|
462
|
+
|
|
463
|
+
The four contextual patterns combine an exact prefix (`地味に`, `静かに`,
|
|
464
|
+
`時間を`, or `側に`) with a known independent verb lemma (`効く`, `壊れる`,
|
|
465
|
+
`溶かす`, or `倒す`). The canonical phrase must be enabled in the resolved rules.
|
|
466
|
+
For example, `地味に効かなかった` is collected as one complete candidate with
|
|
467
|
+
lemma `地味に効く` and `negative: true`. Protected text and paragraph boundaries
|
|
468
|
+
cannot bridge the pattern. Longer contextual phrases suppress contained matches,
|
|
469
|
+
including when the longer phrase is allowed. These limited patterns suggest
|
|
470
|
+
reviewing the actual effect or operation; they do not prove a metaphor is wrong.
|
|
471
|
+
|
|
472
|
+
Sentence-ending analysis splits each paragraph at `。`, `!`, and `?`, retaining
|
|
473
|
+
source offsets. It extracts supported endings such as `検証します` and `です`,
|
|
474
|
+
then checks consecutive windows of three sentences. A sentence without a matching
|
|
475
|
+
ending breaks the run. The first matching run produces one informational
|
|
476
|
+
candidate at the ending of its third sentence. Precise technical repetition may
|
|
477
|
+
be worth retaining; this is not a requirement to vary verbs.
|
|
478
|
+
|
|
479
|
+
### Scoring: density and optional readability judgment
|
|
480
|
+
|
|
481
|
+
Scoring first runs the shared candidate detector. For the default
|
|
482
|
+
`candidate-density-v1`, let `N` be all findings, including informational ones,
|
|
483
|
+
and `C` be non-whitespace Unicode characters after inline masking:
|
|
484
|
+
|
|
485
|
+
```text
|
|
486
|
+
density = N * 1000 / C
|
|
487
|
+
score = max(0, 100 - 5 * density)
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
The score is rounded to three decimals. No unmasked prose yields `null`.
|
|
491
|
+
More candidates per 1,000 characters lower the score; short texts are particularly
|
|
492
|
+
sensitive. Changing rules, allow lists, or the detector can change the score of
|
|
493
|
+
an unchanged manuscript. This heuristic is a review indicator, not a calibrated
|
|
494
|
+
quality measure or a probability that AI wrote the text.
|
|
495
|
+
|
|
496
|
+
```mermaid
|
|
497
|
+
flowchart TD
|
|
498
|
+
P["Selected prose and detected candidates"] --> D["Compute candidate density and mechanical score"]
|
|
499
|
+
D --> A{"Explicit agent option and nonempty prose?"}
|
|
500
|
+
A -->|"No"| R["Text or JSON report"]
|
|
501
|
+
A -->|"Yes"| I["Paragraph IDs, text, headings, and readability rubric"]
|
|
502
|
+
I --> J["Installed Codex or Claude CLI; configured model provider"]
|
|
503
|
+
J --> V["Validate IDs, integer ratings, and reasons"]
|
|
504
|
+
V --> M["Map ratings to 0, 50, 100 and average"]
|
|
505
|
+
M --> R
|
|
506
|
+
D --> K["Retain mechanical score in JSON"]
|
|
507
|
+
K --> R
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
With explicit `--agent`, `llm-readability-v1` asks a fresh local CLI invocation to
|
|
511
|
+
rate each paragraph's sentence clarity and paragraph coherence separately from
|
|
512
|
+
1 to 3. The rubric targets engineers familiar with the technical terms. The
|
|
513
|
+
input contains paragraph IDs, original text, and headings, without candidate
|
|
514
|
+
findings or file paths. The CLI may connect to its configured model provider.
|
|
515
|
+
The response must contain every supplied ID exactly once, integer ratings in
|
|
516
|
+
range, and nonempty reasons. Invalid responses fail scoring.
|
|
517
|
+
|
|
518
|
+
Ratings of 1, 2, and 3 map to 0, 50, and 100. Each dimension averages equally
|
|
519
|
+
across scored paragraphs; the displayed score averages the two dimension means.
|
|
520
|
+
JSON retains both dimensions, paragraph reasons, evaluator identity, and the
|
|
521
|
+
mechanical score. These ordinal averages summarize a fallible judgment. There
|
|
522
|
+
is no reference manuscript or fact checklist in this single-document path, so
|
|
523
|
+
it does not assess preservation or establish technical accuracy. Temporary CLI
|
|
524
|
+
artifacts are removed after the invocation, and manuscripts remain unchanged.
|
|
525
|
+
|
|
526
|
+
| Result | What it measures | Evidence needed |
|
|
527
|
+
| --- | --- | --- |
|
|
528
|
+
| Candidate-density score | Detected cues per text length | Current prose and resolved rules |
|
|
529
|
+
| Optional readability score | Model judgment of clarity and coherence | Current prose, context, and rubric |
|
|
530
|
+
| Development benchmark accuracy | Detector agreement and location accuracy | Fixed corpus with annotated targets |
|
|
531
|
+
| Review verification | Mechanical preservation after editing | Original session baseline and current files |
|
|
532
|
+
|
|
533
|
+
See the [development prose benchmark](#development-prose-benchmark) for
|
|
534
|
+
before/after trials with gold targets, separate fact checklists, and evaluator
|
|
535
|
+
calibration. Its precision, recall, and F1 evaluate the detector rather than
|
|
536
|
+
providing a score for an arbitrary manuscript.
|
|
537
|
+
|
|
371
538
|
## Configuration
|
|
372
539
|
|
|
373
|
-
`scan`
|
|
540
|
+
`scan` and `score` search upward from the entrypoint directory for the nearest
|
|
374
541
|
`.asciidoc-pubkit.yml`. Use `--config FILE` to select a different file. CLI options
|
|
375
542
|
override configuration values; unspecified values use built-in defaults.
|
|
376
543
|
The initial release loads one configuration file, not merged book/repository files.
|
|
@@ -434,8 +601,9 @@ sahen predicate takes precedence over a matching standalone noun rule. Auxiliary
|
|
|
434
601
|
negation, past tense, passive forms, and progressive forms in the reported surface.
|
|
435
602
|
The added reach predicate is negative-only; the existing handling predicate is
|
|
436
603
|
reviewed in both affirmative and negative forms. Glossary variants and generic
|
|
437
|
-
framing phrases continue to use literal matching. Contextual phrases
|
|
438
|
-
|
|
604
|
+
framing phrases continue to use literal matching. Contextual phrases suppress
|
|
605
|
+
overlapping morphological candidates. Except for the four predicate patterns
|
|
606
|
+
described below, they use literal matching. Compound
|
|
439
607
|
nouns are matched across adjacent noun tokens. Selection and narrowing verbs
|
|
440
608
|
include potential forms; predicate surfaces also preserve causative auxiliaries.
|
|
441
609
|
|
|
@@ -460,11 +628,30 @@ not added as general metaphor candidates. Identify the actual effect, policy,
|
|
|
460
628
|
work, or failure state from evidence rather than applying a fixed replacement.
|
|
461
629
|
Keep valid technical meanings and necessary negation.
|
|
462
630
|
|
|
631
|
+
Version 0.6.2 additionally matches these four phrases with MeCab/IPADIC
|
|
632
|
+
verb lemmas and adjacent predicate auxiliaries. For example, `地味に効かなかった`,
|
|
633
|
+
`静かに壊れていた`, `時間を溶かしてしまった`, and `側に倒しました` retain their
|
|
634
|
+
complete surfaces and polarity. A pattern is enabled only when its canonical
|
|
635
|
+
phrase (`地味に効く`, `静かに壊れる`, `時間を溶かす`, or `側に倒す`) is present in
|
|
636
|
+
the resolved `contextual-phrase.terms`. Removing those canonical entries from
|
|
637
|
+
custom rules disables their morphological patterns; remaining exact surfaces
|
|
638
|
+
still work. No YAML schema change is required. Allow lists accept the canonical
|
|
639
|
+
phrase or exact detected surface. The most specific overlapping phrase wins,
|
|
640
|
+
including allowed phrases. Masked inline content, unknown tokens, and paragraph
|
|
641
|
+
boundaries cannot bridge a pattern. This remains a limited contextual heuristic,
|
|
642
|
+
not a syntactic or semantic determination of metaphor. Literal mode retains
|
|
643
|
+
exact surfaces and does not acquire this inflection coverage.
|
|
644
|
+
|
|
645
|
+
In version 0.6.2, a `repeated-ending` candidate points to the ending
|
|
646
|
+
of the third sentence in the first consecutive run within each paragraph.
|
|
647
|
+
A sentence without a matching ending interrupts the run. The candidate remains
|
|
648
|
+
informational because precise technical repetition can be necessary.
|
|
649
|
+
|
|
463
650
|
The shared criteria distinguish reader instructions, actual system behavior,
|
|
464
651
|
and available capabilities. They also separate readability from checks for
|
|
465
652
|
missing information and unsupported additions. Sentence length, punctuation,
|
|
466
653
|
and list density are contextual cues, not fixed acceptance thresholds. These
|
|
467
|
-
updates are included in 0.6.
|
|
654
|
+
updates are included in 0.6.2; start a new session to use updated criteria and rules.
|
|
468
655
|
|
|
469
656
|
Literal term matching suppresses matches strictly contained in a longer matched
|
|
470
657
|
term, across categories. The longer term also suppresses contained matches when
|
|
@@ -484,12 +671,12 @@ reports analyzer changes instead of treating results from different dictionaries
|
|
|
484
671
|
as directly comparable. Prompt generation uses saved evidence and does not need
|
|
485
672
|
MeCab. Changed rules or dictionary settings require a new scan.
|
|
486
673
|
|
|
487
|
-
Sessions from earlier tool versions are not compatible with 0.6.
|
|
674
|
+
Sessions from earlier tool versions are not compatible with 0.6.2. Keep the original baseline for
|
|
488
675
|
an ongoing review and finish it with the original version, or start a new review
|
|
489
676
|
pass in a different directory:
|
|
490
677
|
|
|
491
678
|
```sh
|
|
492
|
-
asciidoc-pubkit review scan book.adoc --output .pubkit/review-0.6.
|
|
679
|
+
asciidoc-pubkit review scan book.adoc --output .pubkit/review-0.6.2
|
|
493
680
|
```
|
|
494
681
|
|
|
495
682
|
Severity describes review priority, not proof of an error. There is no AI-authorship
|
|
@@ -568,13 +755,19 @@ Zero denominators are reported as `null`, not perfect scores. Duplicate detectio
|
|
|
568
755
|
count as false positives. These scores measure the detector, not the manuscript.
|
|
569
756
|
|
|
570
757
|
The checked-in [`MeCab baseline`](benchmark/baseline-mecab.json), captured before
|
|
571
|
-
the
|
|
758
|
+
the detector changes, has precision 100, recall 81.818, F1 90, and location
|
|
572
759
|
accuracy 77.778. It misses two inflected metaphor phrases and mislocates two
|
|
573
760
|
repeated-ending candidates. Reports record source, criteria, rules, corpus, and
|
|
574
761
|
dictionary fingerprints. Comparisons reject changed corpora, scoring versions,
|
|
575
762
|
analyzers, or dictionaries. Reports with configuration fingerprints also reject
|
|
576
763
|
changed benchmark configuration. A fixed empty configuration
|
|
577
764
|
keeps these trials independent of auto-discovered manuscript settings.
|
|
765
|
+
The 0.6.2 location correction raises location accuracy to 100 without
|
|
766
|
+
changing precision, recall, or F1 on this corpus. Adding the four MeCab predicate
|
|
767
|
+
patterns then raises precision, recall, F1, and location accuracy to 100 on these
|
|
768
|
+
11 targets. This is regression evidence for the fixed synthetic corpus, not
|
|
769
|
+
evidence of general performance on unseen manuscripts; literal recall remains
|
|
770
|
+
81.818.
|
|
578
771
|
Record your own baseline when dictionary fingerprints
|
|
579
772
|
differ. `--tokenizer literal` explicitly selects a separate limited-mode run;
|
|
580
773
|
there is no automatic fallback, and literal results do not validate MeCab.
|
|
@@ -694,7 +887,9 @@ In literal mode, all category term lists use exact phrase matching. In MeCab
|
|
|
694
887
|
mode, abstract nouns and degree adjectives use dictionary forms, while predicates
|
|
695
888
|
use `verbs` and `sahen`; add a predicate's desired literal surface to
|
|
696
889
|
`weak-predicate.terms` as well if literal mode should detect it. Contextual and
|
|
697
|
-
generic framing phrases use literal matching in both modes
|
|
890
|
+
generic framing phrases use literal matching in both modes, with the
|
|
891
|
+
MeCab exception for the four configured contextual predicate patterns described
|
|
892
|
+
above. Questions apply to
|
|
698
893
|
both detectors. Glossary, style, repeated-ending checks, inline exclusions, and
|
|
699
894
|
morphological suffix handling remain implemented in Ruby.
|
|
700
895
|
|
|
@@ -712,19 +907,23 @@ conclusions. Candidate detection therefore leaves contextual judgment, document
|
|
|
712
907
|
structure, and final edits to the reviewer.
|
|
713
908
|
|
|
714
909
|
- [natural-japanese](https://github.com/coji/natural-japanese): separates mechanical
|
|
715
|
-
detection from contextual judgment and identifies repetitive framing.
|
|
910
|
+
detection from contextual judgment and identifies repetitive framing. Applied
|
|
911
|
+
here: candidate findings feed a contextual review prompt, while phrases such as
|
|
912
|
+
`重要なのは` and `このように` invite inspection rather than automatic deletion.
|
|
716
913
|
- [yomiyasu](https://github.com/nanaism/yomiyasu):
|
|
717
914
|
informs evidence-based review of
|
|
718
915
|
metaphorical operations, distinctions between instructions and capabilities,
|
|
719
|
-
and separate assessment of meaning preservation and readability.
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
and an AI-likeness score are not adopted.
|
|
916
|
+
and separate assessment of meaning preservation and readability. Applied here:
|
|
917
|
+
four limited metaphor patterns and third-sentence repetition locations inform
|
|
918
|
+
candidate detection; the shared criteria require evidence for concrete rewrites.
|
|
723
919
|
- [日本語技術文書の文章規範](https://gist.github.com/k16shikano/fd287c3133457c4fd8f5601d34aa817d):
|
|
724
|
-
informs paragraph logic, evidence scope, and meaningful uncertainty.
|
|
920
|
+
informs paragraph logic, evidence scope, and meaningful uncertainty. Applied
|
|
921
|
+
here: the shared criteria start from each paragraph's technical purpose and
|
|
922
|
+
preserve necessary conditions, uncertainty, and exact terminology.
|
|
725
923
|
- [AI臭い文章とは何なのか](https://speakerdeck.com/nasuvitz/ai-kusai-bunshou-toha-nanina-no-ka):
|
|
726
924
|
provides examples of unnecessary contrast, abstract referents, and paired short
|
|
727
|
-
sentences.
|
|
925
|
+
sentences. Applied here: the shared criteria ask reviewers to inspect these
|
|
926
|
+
patterns in context and recover concrete referents and connected explanations.
|
|
728
927
|
|
|
729
928
|
The development evaluation adapts ideas from the following primary research;
|
|
730
929
|
its engineering audience, corpus, and rubric do not reproduce those benchmarks.
|
|
@@ -734,13 +933,18 @@ Research texts and datasets are not redistributed here.
|
|
|
734
933
|
(Yamashita et al., LREC 2026): separates information preservation from sentence
|
|
735
934
|
and document simplicity and evaluates LLM judgments against human annotations.
|
|
736
935
|
Its elementary-school audience and Wikipedia corpus differ from this project.
|
|
936
|
+
Applied here: the scoring rubric separates sentence clarity from paragraph
|
|
937
|
+
coherence, and the development judge evaluates fact retention independently.
|
|
737
938
|
- [Evaluating Factuality in Text Simplification](https://aclanthology.org/2022.acl-long.506/)
|
|
738
939
|
(Devaraj et al., ACL 2022): distinguishes insertion, deletion, and substitution
|
|
739
|
-
errors
|
|
940
|
+
errors. Applied here: development trials check each source fact and report
|
|
941
|
+
unsupported additions and substitutions separately; a readable rewrite can
|
|
942
|
+
still fail preservation review.
|
|
740
943
|
- [Evaluating Document Simplification: On the Importance of Separately Assessing Simplicity and Meaning Preservation](https://aclanthology.org/2024.readi-1.1/)
|
|
741
944
|
(Cripwell et al., READI 2024): motivates separate readability and preservation
|
|
742
|
-
scores
|
|
743
|
-
|
|
945
|
+
scores. Applied here: readability dimensions and preservation outcomes remain
|
|
946
|
+
separate in benchmark reports; the direct manuscript score makes no
|
|
947
|
+
preservation claim. SARI-style overlap metrics are not adopted.
|
|
744
948
|
|
|
745
949
|
## License
|
|
746
950
|
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module AsciidocPubkit
|
|
4
4
|
class Rules
|
|
5
|
+
CONTEXTUAL_PREDICATES = { '地味に' => '効く', '静かに' => '壊れる',
|
|
6
|
+
'時間を' => '溶かす', '側に' => '倒す' }.freeze
|
|
5
7
|
INLINE = /`[^`\n]*`|\+\+\+.*?\+\+\+|\+\+[^\n]*?\+\+|(?<!\w)\+[^+\n]+\+|「[^」\n]*」|『[^』\n]*』|\{[^}\n]+\}|<<[^>\n]+>>|\[\[[^\]\n]+\]\]|(?:link|xref|image|footnote|pass):[^\s\[]*\[[^\]\n]*\]|https?:\/\/[^\s\[\]<>]+(?:\[[^\]\n]*\])?/m
|
|
6
8
|
def self.mask(text)
|
|
7
9
|
# Keep character offsets stable while excluding common inline constructs.
|
|
@@ -18,18 +20,24 @@ module AsciidocPubkit
|
|
|
18
20
|
token_groups = tokenizer ? tokenize_paragraphs(paragraphs, tokenizer) : []
|
|
19
21
|
paragraphs.each_with_index do |paragraph, index|
|
|
20
22
|
text = mask(paragraph.fetch('text'))
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
contextual = tokenizer ? contextual_predicates(text, token_groups[index], terms) : []
|
|
24
|
+
scan_morphemes(findings, paragraph, text, token_groups[index], settings, rules, terms, contextual) if tokenizer
|
|
25
|
+
scan_literal_terms(findings, paragraph, text, terms, settings, morphological: !!tokenizer, contextual: contextual)
|
|
23
26
|
settings.fetch('glossary').each do |canonical, variants|
|
|
24
27
|
variants.each do |variant|
|
|
25
28
|
find_term(findings, paragraph, text, variant, 'glossary-variant', 'warning', "Use #{canonical.inspect} when this variant refers to the same concept; preserve identifiers and quotations.")
|
|
26
29
|
end
|
|
27
30
|
end
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
endings = text.to_enum(:scan, /[^。!?]+/).filter_map do
|
|
32
|
+
sentence = Regexp.last_match
|
|
33
|
+
next if sentence[0].strip.empty?
|
|
34
|
+
ending = sentence[0].match(/(\p{Han}+(?:します|できます)|しています|されます|ません|です)[[:space:]]*\z/)
|
|
35
|
+
{ 'match' => ending && ending[1], 'offset' => ending && sentence.begin(0) + ending.begin(1) }
|
|
36
|
+
end
|
|
37
|
+
repeated = endings.each_cons(3).find { |items| items.first['match'] && items.map { |item| item['match'] }.uniq.length == 1 }
|
|
31
38
|
if repeated
|
|
32
|
-
|
|
39
|
+
ending = repeated.last
|
|
40
|
+
add(findings, paragraph, text, ending['offset'], ending['match'], 'repeated-ending', 'info', 'Three consecutive sentences share an ending. Check rhythm without changing precise technical verbs.')
|
|
33
41
|
end
|
|
34
42
|
pattern = case settings.fetch('style')
|
|
35
43
|
when 'desu-masu' then /(?:である|だった|なのだ)(?=。|\z)/
|
|
@@ -68,13 +76,40 @@ module AsciidocPubkit
|
|
|
68
76
|
end
|
|
69
77
|
end
|
|
70
78
|
|
|
71
|
-
def self.
|
|
79
|
+
def self.contextual_predicates(text, tokens, terms)
|
|
80
|
+
surfaces, question = terms.fetch('contextual-phrase')
|
|
81
|
+
tokens.each_with_index.filter_map do |token, index|
|
|
82
|
+
next if token['unknown'] || token['pos'] != '動詞' || token['pos_detail'] != '自立'
|
|
83
|
+
pattern = CONTEXTUAL_PREDICATES.find do |prefix, lemma|
|
|
84
|
+
surfaces.include?(prefix + lemma) && token['lemma'] == lemma &&
|
|
85
|
+
token['offset'] >= prefix.length && text[token['offset'] - prefix.length...token['offset']] == prefix
|
|
86
|
+
end
|
|
87
|
+
next unless pattern
|
|
88
|
+
prefix, lemma = pattern
|
|
89
|
+
start = token['offset'] - prefix.length
|
|
90
|
+
prefix_index = tokens[0...index].rindex { |member| member['offset'] == start }
|
|
91
|
+
next unless prefix_index
|
|
92
|
+
members = tokens[prefix_index..predicate_end(tokens, index, text)]
|
|
93
|
+
next if members.any? { |member| member['unknown'] }
|
|
94
|
+
finish = members.last['end_offset']
|
|
95
|
+
metadata = { 'lemma' => prefix + lemma, 'part_of_speech' => '動詞',
|
|
96
|
+
'negative' => negative?(members), 'detector' => 'mecab-ipadic' }
|
|
97
|
+
[start, finish, text[start...finish], 'contextual-phrase', question, metadata]
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def self.negative?(members)
|
|
102
|
+
members.any? { |member| member['pos'] == '助動詞' && %w[ない ぬ ん].include?(member['lemma']) }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def self.scan_morphemes(findings, paragraph, text, tokens, settings, rules, terms, contextual = [])
|
|
72
106
|
phrase_ranges = terms['contextual-phrase'][0].flat_map do |phrase|
|
|
73
107
|
text.to_enum(:scan, Regexp.new(Regexp.escape(phrase))).map do
|
|
74
108
|
match = Regexp.last_match
|
|
75
109
|
match.begin(0)...match.end(0)
|
|
76
110
|
end
|
|
77
111
|
end
|
|
112
|
+
phrase_ranges.concat(contextual.map { |start, finish, *_| start...finish })
|
|
78
113
|
tokens.each_with_index do |token, index|
|
|
79
114
|
next if token['unknown'] || phrase_ranges.any? { |range| range.cover?(token['offset']) }
|
|
80
115
|
lemma = token['lemma']
|
|
@@ -116,7 +151,7 @@ module AsciidocPubkit
|
|
|
116
151
|
end
|
|
117
152
|
next unless rule
|
|
118
153
|
members = tokens[index..finish_index]
|
|
119
|
-
negative =
|
|
154
|
+
negative = negative?(members)
|
|
120
155
|
next if rules.fetch('negative_only').include?(lemma) && !negative
|
|
121
156
|
surface = text[token['offset']...tokens[finish_index]['end_offset']]
|
|
122
157
|
allows = settings.fetch('allows')
|
|
@@ -143,22 +178,25 @@ module AsciidocPubkit
|
|
|
143
178
|
finish
|
|
144
179
|
end
|
|
145
180
|
|
|
146
|
-
def self.scan_literal_terms(findings, paragraph, text, terms, settings, morphological:)
|
|
147
|
-
candidates =
|
|
181
|
+
def self.scan_literal_terms(findings, paragraph, text, terms, settings, morphological:, contextual: [])
|
|
182
|
+
candidates = contextual.dup
|
|
148
183
|
terms.each do |rule, (surfaces, question)|
|
|
149
184
|
next if morphological && !%w[generic-framing contextual-phrase].include?(rule)
|
|
150
185
|
surfaces.each do |surface|
|
|
151
186
|
text.to_enum(:scan, Regexp.new(Regexp.escape(surface))).each do
|
|
152
187
|
match = Regexp.last_match
|
|
188
|
+
next if contextual.any? { |start, finish, _, candidate_rule, *_| start == match.begin(0) && finish == match.end(0) && candidate_rule == rule }
|
|
153
189
|
candidates << [match.begin(0), match.end(0), surface, rule, question]
|
|
154
190
|
end
|
|
155
191
|
end
|
|
156
192
|
end
|
|
157
193
|
# Keep the most specific phrase, including when it is explicitly allowed.
|
|
158
|
-
candidates.each do |start, finish, surface, rule, question|
|
|
194
|
+
candidates.each do |start, finish, surface, rule, question, metadata|
|
|
159
195
|
next if candidates.any? { |left, right, *_| left <= start && right >= finish && right - left > finish - start }
|
|
160
|
-
|
|
196
|
+
forms = metadata ? [surface, surface + '。', metadata['lemma']] : [surface]
|
|
197
|
+
next if forms.any? { |form| settings.fetch('allows').include?(form) }
|
|
161
198
|
add(findings, paragraph, text, start, surface, rule, 'hint', question)
|
|
199
|
+
findings.last.merge!(metadata) if metadata
|
|
162
200
|
end
|
|
163
201
|
end
|
|
164
202
|
|
data/lib/asciidoc_pubkit.rb
CHANGED