Skip to content

Commit db97e2b

Browse files
pfranktestingclaude
andcommitted
Merge fix/bctest-edgecases into main (v1.4.0)
19 edge cases from library reviewer testing against BCTestArticle.docx. See edgecases.md for full details and CHANGELOG.md for the summary. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2 parents df28d2c + 7606997 commit db97e2b

7 files changed

Lines changed: 759 additions & 64 deletions

File tree

CHANGELOG.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,34 @@
11
# Changelog
22

3+
## [1.4.0] - 2026-06-27
4+
5+
### Added
6+
7+
- **Heading numbering** — Flags section numbers (1, 2, 3.1, 3.1.1, …) prefixed to headings; APA headings are not numbered.
8+
- **Heading capitalization** — Detects headings in sentence case that should be Title Case.
9+
- **Heading bold** — Flags Level 1–3 headings that are missing bold formatting.
10+
- **Heading level alignment** — Flags Level 1 headings that are not centered, and Level 2/3 headings that are not flush left.
11+
- **References start on new page** — Detects whether the References section begins on a hard page break. Returns a review note (with a warning about Word's built-in bibliography tool) when the heading is inside a content control and ordering cannot be reliably determined.
12+
- **References numbered** — Flags reference entries that use Word's automatic list numbering or bullets; APA uses plain hanging indents.
13+
- **Citation comma** — Flags missing commas between author and year (`(Li 2024)``(Li, 2024)`), including the `et al.` case, and catches stray trailing commas after the year (`(2023,)`).
14+
- **Reference DOI format** — Flags bare DOIs (`10.xxxx/…`) and the old `doi:` prefix; all DOIs must use the full `https://doi.org/` URL form.
15+
- **Reference forbidden phrases** — Flags `Available at` and `accessed [date]` in reference entries; APA uses neither.
16+
17+
### Changed
18+
19+
- **"Inline citations" renamed "In-text citations"** — Matches the terminology used in the APA Manual throughout the UI, check cards, and README.
20+
- **Bare DOI recognition**`checkReferenceDOIs` now recognizes bare `10.xxxx/…` strings as DOIs, so references with bare DOIs no longer falsely report a missing DOI/URL.
21+
- **Numbered heading detection** — Paragraphs beginning with a section number (`1`, `3.1`, `3.1.1`, …) are now classified as headings even when no Word heading style is applied, enabling the four new heading checks above.
22+
23+
### Fixed
24+
25+
- **All-uncited / all-unmatched false positives** — Mangled author strings like `CambFierroJ.` now yield a clean surname via a lowercase→uppercase boundary split in `extractReferenceKey`, eliminating floods of false "uncited reference" and "unmatched citation" reports.
26+
- **Italics false positive on article titles** — A non-breaking space (U+00A0) between the article title and journal name prevented the title/journal boundary split, causing the journal's italic span to be attributed to the title. Fixed by using `\s` (which matches U+00A0) in the split pattern.
27+
- **Title-case false positives** — Reduced from ~10 to ~1 by requiring two or more *consecutive* capitalized words and skipping words that follow sentence-ending punctuation.
28+
- **Lowercase subtitle not flagged**`checkReferenceTitleCapitalization` now also flags a lowercase first word after a colon in the title (e.g., `well-being: an empirical` → should be `An`).
29+
30+
*Full details, including root-cause analysis and affected functions for all 19 items, are in [edgecases.md](edgecases.md).*
31+
332
## [1.3.2] - 2026-05-23
433

534
### Changed

README.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,21 @@ The report can be printed or saved as a PDF directly from the browser using the
3333
|---|---|
3434
| Page numbering | Plain page number in the upper-right header on every page, starting at 1 on the title page; no "Page" or "Pg" label |
3535
| Title page | Detects a standard APA title page at the start of the document |
36+
| Heading numbering | Flags section numbers (1, 3.1, 3.1.1, …) on headings — APA headings are not numbered |
37+
| Heading capitalization | Headings use Title Case, not sentence case |
38+
| Heading bold | Level 1–3 headings are bold |
39+
| Heading level alignment | Level 1 headings are centered; lower levels are flush left |
3640
| References page | Detects a References page near the end of the document |
37-
| Inline citations | At least one (Author, Year) citation found in the body |
41+
| References start on new page | References section begins on a new page (hard page break before the heading) |
42+
| References numbered | Flags automatic list numbering or bullets on reference entries — APA uses plain hanging indents |
43+
| In-text citations | At least one (Author, Year) citation found in the body |
3844
| References heading alignment | "References" heading is centered |
3945
| References formatting | Hanging indents on reference entries; flags broken or bare-URL entries |
40-
| Uncited references | Every reference entry has a matching inline citation |
41-
| Unmatched citations | Every inline citation has a matching reference entry |
46+
| Uncited references | Every reference entry has a matching in-text citation |
47+
| Unmatched citations | Every in-text citation has a matching reference entry |
4248
| Reference DOI/URL | Each reference includes a visible DOI or URL |
49+
| Reference DOI format | DOIs use the full https://doi.org/ URL format; flags bare DOIs (10.xxxx/...) and old doi: prefix |
50+
| Reference forbidden phrases | References do not use non-APA phrases such as "Available at" or "accessed [date]" |
4351
| Reference short link | Reference URLs link to a specific page, not just a domain homepage |
4452
| Unapproved source | References do not use sources on [AIU's list of 145+ unapproved domains](https://careered.libguides.com/AIUS/unacceptablewebsites) |
4553
| Reference link verification | DOIs are verified against CrossRef — flags mismatches where the DOI resolves to a different source than the reference claims; URLs cannot be verified from the browser (status codes are inaccessible via cross-origin fetch) and are noted for manual review |
@@ -51,6 +59,7 @@ The report can be printed or saved as a PDF directly from the browser using the
5159
| Heading paragraph spacing | 0 pt before and after headings |
5260
| Body first-line indents | 0.5-inch first-line indent on body paragraphs |
5361
| Body alignment | Left alignment on body text |
62+
| Citation comma | Commas are present between author and year: (Author, Year); no trailing commas after year |
5463
| Font | 12pt Times New Roman throughout; flags mixed sizes or families |
5564

5665
---

edgecases.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
Thank you for the opportunity to test the APA Coach from a library perspective. For the test document, we used the attached CC by 4.0 article (see attribution below). We inserted several errors, many of which were successfully detected by APA Coach.
2+
3+
4+
5+
Following are the areas that it missed.
6+
7+
8+
9+
In-text citations:
10+
11+
Used the term inline – The APA Manual uses the term in-text.
12+
Overlooked missing commas in the in-text citations e.g., (Li 2024), (Liu 2024), (Tang 2024), (Yang 2024)
13+
Corrections required: (Li, 2024), (Liu, 2024), (Tang, 2024), (Yang, 2024),
14+
Although it detected the missing period after et al., it did not detect the missing comma e.g., (Schwartz et al 2022), (Sloan et al 2024), (Dwivedi et al 2023)
15+
Corrections required: (Schwartz et al., 2022), (Sloan et al., 2024), (Dwivedi et al., 2023).
16+
Overlooked a comma we inserted after a date in the test doc i.e., Dempere et al (2023,)
17+
Correction required: Dempere et al. (2023)
18+
Overlooked the numbering that preceded headings in the test doc, e.g., 1 Ethical use of ChatGPT in education—Best practices to combat AI-induced plagiarism
19+
Overlooked the use of sentence case instead of title case and bold print in the headings e.g., 1 Ethical use of ChatGPT in education—Best practices to combat AI-induced plagiarism
20+
Example of corrections required: Ethical Use of ChatGPT in Education—Best Practices to Combat AI-Induced Plagiarism
21+
Did not point out that all the headings labelled 1, 2, 3, 4, should have been level one headings
22+
Example of corrections required:
23+
Bold, Title Case, and Centered
24+
25+
Did not point out that the headings labelled 3.1, 3.2, 3.3, 3.4, 3.5 should have been level 2 headings (Bold, Title Case, against the Left Margin) e.g., point number 3.1 Current educational strategies to counter unethical use of LLMs
26+
Example of corrections required:
27+
Current Educational Strategies to Counter Unethical Use of LLMs
28+
29+
Did not point out that all headings labelled 3.1.1, 3.1.2, 3.1.3, 3.1.4, 3.1.5, 3.1.6 and 3.5.1, 3.5.2, 3.5.3, 3.5.4 should have been level 3 headings (Bold, Title Case, against the left margin) e.g., point number 3.1.1 Regulating AI usage within curricula
30+
Example of corrections required:
31+
Regulating AI Usage Within Curricula
32+
33+
Stated that all 14 items cited in-text had not been cited in the references.
34+
All 14 items were included in the references.
35+
Example of note required: All the in-text citations contained formatting errors.
36+
Stated that 11 items listed in the reference had not been cited in-text.
37+
All items listed in the references were cited in-text
38+
Example of note required: All the in-text citations contained formatting errors.
39+
40+
41+
References:
42+
43+
Did not point out that the references started immediately after (on the same page as) the text
44+
Example of instruction required: References must start on a new clean page
45+
Did not point out that all the references were numbered
46+
Example of instruction required: The APA Manual does not use numbers or bullets in the references.
47+
Stated that no DOIs were included in the references. Eleven of the 14 references contained DOIs
48+
However, should have noted that all 11 DOIs were incorrectly formatted.
49+
Example of instruction: All DOIs must be preceded by https://doi.org/ e.g., instead of 10.1007/s10639-024-12871-0 should have been https://doi.org/10.1007/s10639-024-12871-0
50+
Stated that most of the items used title case instead of sentence case for the titles. I could only find 1 item that had that error.
51+
Overlooked the use of lower case in the first letter of the first word of every subtitle in the references e.g., ChatGPT adoption and its influence on faculty well-being: an empirical research in higher education
52+
Example of corrections required: ChatGPT adoption and its influence on faculty well-being: An empirical research in higher education.
53+
Stated that italics were wrongly used in the references. I could not find any titles that were italicized.
54+
In the test doc, only journal titles were (correctly) italicized
55+
Did not detect the use of “available at” in some references.
56+
Example of instruction required: The APA Manual does not use that phrase “available at.”
57+
Did not detect that some reference used “accessed” and a date.
58+
Example of instruction required: The APA Manual does not use “accessed.”
59+
60+
---
61+
62+
## Fixes Applied
63+
64+
All 19 items were resolved across four implementation slices (branch `fix/bctest-edgecases`).
65+
66+
### Slice A — Quick detectors
67+
68+
**Item 1 — “inline” should be “in-text”**
69+
Renamed the rule label from “Inline citations” to “In-text citations” throughout: `checkApaFormatting.js`, `main.jsx` (`CHECK_CATEGORY` map and UI label text), and `README.md` checks table. Internal function names (`checkInlineCitations`) were left unchanged.
70+
71+
**Items 2, 3, 4 — Missing and stray commas in citations**
72+
Added `checkCitationComma` in `checkApaFormatting.js`. The check scans parenthetical citations for a missing comma between the author group and the year (`(Li 2024)``(Li, 2024)`), handles the `et al` (no period) case (`(Schwartz et al 2022)`), and also catches a trailing comma after the year (`Dempere et al (2023,)`). Registered in `checkApaFormatting`, exported, and added to `CHECK_CATEGORY` in `main.jsx` and `README.md`.
73+
74+
**Item 14 — Bare DOIs not detected / format not flagged**
75+
Updated `hasDOIOrURL` in `checkApaFormatting.js` to match bare DOIs (`\b10\.\d{4,}/\S+`) so `checkReferenceDOIs` no longer falsely reports missing DOIs. Extended `checkReferenceDOIFormat` to flag bare DOIs as misformatted (must be preceded by `https://doi.org/`). The two checks interact intentionally: DOI/URL passes (DOI is present), DOI format fails (DOI needs the full URL prefix).
76+
77+
**Items 18, 19 — “Available at” and “accessed [date]” phrases**
78+
Added `checkReferenceForbiddenPhrases` in `checkApaFormatting.js`. Scans reference entries for `available at` (case-insensitive) and `accessed` followed by a date. Registered, exported, added to `CHECK_CATEGORY` in `main.jsx` and `README.md`.
79+
80+
---
81+
82+
### Slice B — Reference-parser hardening
83+
84+
**Items 10, 11 — All 14 refs falsely uncited / all 11 citations falsely unmatched**
85+
Root cause: `extractReferenceKey` in `checkApaFormatting.js` could not recover a clean surname from mangled author strings like `CambFierroJ.` or `LiM.`. Fixed by matching a lowercase→uppercase boundary regex (`/^(.*?[a-zà-ÿ])(?=[A-ZÀ-Ÿ]\.)/`) against the raw `beforeYear` string (retaining the trailing period so the lookahead fires correctly). This allowed all 14 reference keys and all 12 citation keys to match, eliminating all 26 false reports.
86+
87+
**Item 15 — Title case over-reported (~10 false positives, ~1 real)**
88+
Rewrote the title-case detection logic in `checkReferenceTitleCapitalization` in `checkApaFormatting.js`. Changed the heuristic from “any two capitalized words” to “two or more *consecutive* capitalized words” and added a skip for words that follow sentence-ending punctuation (`.`, `?`, `!`). Reduced false positives from ~10 to ~1 real case.
89+
90+
**Item 16 — Lowercase subtitle after colon not flagged**
91+
Extended `checkReferenceTitleCapitalization` to detect a lowercase first word after a colon in the title (e.g., `well-being: an empirical` → should be `An`). The check looks for `: [a-z]` patterns in the parsed title span.
92+
93+
**Item 17 — Italics falsely flagged on article titles**
94+
Root cause: `parseReferenceEntry` in `checkApaFormatting.js` was splitting the title from the journal name at `. ` (period-space), but U+00A0 (non-breaking space) between the title and journal name caused the split to miss, pulling the journal name into the title span. Fixed by changing the split pattern from `/\. | \./` to `/\.\s|\s\./` so `\s` matches non-breaking space. Once the boundary was correct, `getSpanItalicState` found no italic article titles and all five false positives cleared.
95+
96+
---
97+
98+
### Slice C — Heading overhaul
99+
100+
**Item 5 — Heading numbering not flagged**
101+
Added `checkHeadingNumbering` in `checkApaFormatting.js`. Flags headings that carry a leading section number (`1`, `3.1`, `3.1.1`, …). Gated on `hasNumberedHeadings` so the check only runs when numbered headings are actually present, ensuring zero impact on properly styled documents.
102+
103+
**Items 6, 7, 8, 9 — Sentence case, missing bold, wrong level/alignment**
104+
Added `checkHeadingTitleCase`, `checkHeadingBold`, and `checkHeadingLevelFormat` in `checkApaFormatting.js`. These checks rely on a new `isNumberedHeadingText` detector and `parseHeadingNumber` helper added to `extractDocxFormatting.js`, which recognize paragraphs beginning with a section number (`^\d+(\.\d+)*\s+`) as headings even when no Word heading style is applied. The numbering depth determines the APA level: one part = L1, two parts = L2, three parts = L3. `checkHeadingLevelFormat` flags L1 headings that are not centered and L2/L3 headings that are not flush left. All four checks registered, exported, added to `CHECK_CATEGORY` in `main.jsx` and `README.md`.
105+
106+
---
107+
108+
### Slice D — Structural reference checks
109+
110+
**Item 12 — References not on a new page**
111+
Added `checkReferencesStartNewPage` in `checkApaFormatting.js`. Checks for a hard page break before the References heading by inspecting (a) `pageBreakBefore` in the heading's own paragraph properties, and (b) `endsWithPageBreak` on the immediately preceding paragraph (a `w:br w:type=”page”` run). Both signals are surfaced as new per-paragraph fields (`pageBreakBefore`, `endsWithPageBreak`) extracted in `extractDocxFormatting.js`. To avoid false positives when References is inside a Word content control (sdt) — where `collectParagraphNodes` does not preserve the interleave order between `body.p` and `body.sdt` elements — the check returns `review` rather than `pass` or `fail` when either the heading or the preceding paragraph came from an sdt. Documents using Word's built-in bibliography tool will see a `review` result with a note explaining the limitation and advising against the built-in tool. A `fromSdt` flag was added to each paragraph in `extractDocxFormatting.js` to support this distinction.
112+
113+
**Item 13 — References are numbered**
114+
Added `checkReferencesNumbered` in `checkApaFormatting.js`. Flags reference entry paragraphs that carry a `w:numPr` list-numbering element (automatic Word list numbering or bullets). The `numPr: { numId, ilvl }` signal is surfaced as a new per-paragraph field extracted in `extractDocxFormatting.js`. BCTestArticle's references carried `numId=22`, confirmed by raw XML inspection before implementation. Both new checks registered, exported, added to `CHECK_CATEGORY` in `main.jsx` and `README.md`.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "apa-coach",
3-
"version": "1.3.2",
3+
"version": "1.4.0",
44
"description": "",
55
"main": "index.js",
66
"scripts": {

src/browser/main.jsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import "./styles.css";
77

88
const APP_INFO = {
99
version: packageInfo.version,
10-
lastUpdated: "May 23, 2026",
10+
lastUpdated: "June 27, 2026",
1111
supportEmail: "pfrank@aiuniv.edu",
1212
resources: [
1313
{
@@ -55,6 +55,10 @@ const CHECK_CATEGORY = {
5555
// Paper formatting
5656
"Page numbering": "Paper formatting",
5757
"Title page": "Paper formatting",
58+
"Heading numbering": "Paper formatting",
59+
"Heading capitalization": "Paper formatting",
60+
"Heading bold": "Paper formatting",
61+
"Heading level alignment": "Paper formatting",
5862
"Margins": "Paper formatting",
5963
"Body line spacing": "Paper formatting",
6064
"Heading line spacing": "Paper formatting",
@@ -66,6 +70,8 @@ const CHECK_CATEGORY = {
6670
"Unconverted markup symbols": "Paper formatting",
6771
// References
6872
"References page": "References",
73+
"References start on new page": "References",
74+
"References numbered": "References",
6975
"References heading alignment": "References",
7076
"References line spacing": "References",
7177
"Reference hanging indent": "References",
@@ -78,14 +84,16 @@ const CHECK_CATEGORY = {
7884
"Reference italics": "References",
7985
"Reference punctuation": "References",
8086
"Reference DOI format": "References",
87+
"Reference forbidden phrases": "References",
8188
"Reference link verification": "References",
8289
// Citations
83-
"Inline citations": "Citations",
90+
"In-text citations": "Citations",
8491
"Uncited references": "Citations",
8592
"Unmatched citations": "Citations",
8693
"Personal communication": "Citations",
8794
"Citation ampersand": "Citations",
8895
"Citation et al. format": "Citations",
96+
"Citation comma": "Citations",
8997
"Citation no-date format": "Citations",
9098
"Citation page format": "Citations",
9199
"Citation multiple sources": "Citations",
@@ -526,7 +534,7 @@ function CheckCard({ check }) {
526534
</div>
527535
</div>
528536
) : null}
529-
{check.rule === "Inline citations" ? (
537+
{check.rule === "In-text citations" ? (
530538
<div className="citation-example">
531539
<p className="citation-example-label">Cite every source you use in the body of your paper:</p>
532540
<div className="citation-example-demo">
@@ -547,7 +555,7 @@ function CheckCard({ check }) {
547555
) : null}
548556
{check.rule === "Uncited references" ? (
549557
<div className="citation-example">
550-
<p className="citation-example-label">Each reference needs a matching inline citation:</p>
558+
<p className="citation-example-label">Each reference needs a matching in-text citation:</p>
551559
<div className="citation-example-demo">
552560
<div className="citation-example-col">
553561
<span className="citation-example-tag citation-example-tag--wrong">✗ Missing citation</span>
@@ -570,7 +578,7 @@ function CheckCard({ check }) {
570578
) : null}
571579
{check.rule === "Unmatched citations" ? (
572580
<div className="citation-example">
573-
<p className="citation-example-label">Each inline citation needs a matching reference entry:</p>
581+
<p className="citation-example-label">Each in-text citation needs a matching reference entry:</p>
574582
<div className="citation-example-demo">
575583
<div className="citation-example-col">
576584
<span className="citation-example-tag citation-example-tag--wrong">✗ No reference</span>

0 commit comments

Comments
 (0)