Commit Graph
6 Commits
Author SHA1 Message Date
Mark Eaton d061f78b7f v2 chunk 2a iter 1: rules 6+18 — ed. invariant, expand publishers
Two prompt edits to SYSTEM_PROMPT_NOTES driven by chunk 1 smoke test
gaps on chapter_first_doyle.toml.

Rule 6 (chapter form) expanded with an explicit invariance statement:
"ed." is the canonical abbreviation regardless of editor count — do
NOT pluralize to "eds." for multiple editors. CMOS NB treats it as
an invariant abbreviation, not a number-agreeing word.

New rule 18 (publisher expansion) mirrors v1 formatter.py rule 14:
publisher names must be in full canonical form, with note-form-
specific examples ("U of Chicago Press" → "University of Chicago
Press"). Includes the same MIT Press / ALA Editions / MLA carve-out
for publishers whose canonical self-presentation legitimately uses
initials.

Two new prompt-content unit tests added to tests/test_note_formatter.py
following the v1 test_formatter.py discipline. TDD cycle: red-green-
verified end-to-end.

Real-API smoke test, 3 v2 exemplars × 2 runs each: 6/6 byte-perfect
matches (was 4/6 in chunk 1; chapter_first_doyle went 0/2 → 2/2,
book and journal still 2/2). v1 untouched, 96/96 v1 tests still
passing. Total suite: 116/116.
2026-04-11 18:27:55 -04:00
Mark Eaton 28ca3ac928 v2 chunk 1: scaffold note formatter (Path B parallel artifact)
Begin v2 (in-text citation note form) as a parallel artifact to the
v1 bibliography formatter, per the Path B architectural decision:
no shared mutable state, no shared prompt content, no v1 changes.

New artifacts:

- src/cmos/parser.py: add find_notes() / NoteDefinition /
  NotesParseResult as siblings to split_bibliography(). Targets
  pandoc-style markdown footnote definitions [^marker]: text.
  Single-line only; multi-line continuation deferred.

- src/cmos/note_formatter.py: new module mirroring formatter.py.
  17-rule SYSTEM_PROMPT_NOTES for CMOS 18 first-occurrence note
  form. Reuses cmos.runtime_validator.validate() unchanged — its
  structural checks all apply to note form too. Caller injection,
  retry loop, and model selection mirror v1.

- tests/test_parser.py: 7 new tests for find_notes().

- tests/test_note_formatter.py: 11 new tests mirroring v1 test
  discipline (no API calls, fake-caller injection, retry semantics,
  prompt smoke checks).

- exemplars/notes/: new subdirectory with 3 hand-synthesized
  first-occurrence note exemplars (book, journal article, chapter
  in edited book). Uses expected_note as the field name (not v1's
  expected_bibliography). harness/score.py:load_exemplars uses a
  non-recursive glob, so the v1 canary loader does not see these —
  Path B isolation is automatic.

v1 untouched. v1 canary still loads exactly 17 exemplars. Full
test suite: 96 v1 + 18 new v2 = 114 passing.

Smoke-tested all 3 v2 exemplars against real GPT-5 (not fakes),
2 runs each. book_first_yu and journal_first_kwon: 4/4 byte-perfect
on the first try. chapter_first_doyle: 0/2, surfacing two known
prompt gaps for the next iteration:

  1. Publisher abbreviation not expanded ("U of Chicago Press"
     preserved instead of "University of Chicago Press"). v1's
     formatter.py rule 14 is missing from the v2 prompt.

  2. "ed." pluralized to "eds." for multiple editors. CMOS NB
     uses "ed." invariantly regardless of editor count.

Both gaps are addressable prompt edits, not architectural problems —
exactly the kind of finding the dev loop is designed to surface.

Out of scope (deferred to chunk 2 and later): CLI extension,
shortened-form generation, document reassembly, harness scoring
loop integration, v2 linter rules, real-draft testing.
2026-04-11 18:20:50 -04:00
Mark Eaton 2a8c72ae22 gitignore: protect rough_drafts/*.txt from accidental commits
The user's drafts in rough_drafts/ are real in-progress academic
work. Project discipline (per memory) is to keep them untracked,
but a single accidental `git add -A` could leak unpublished
scholarship into git history. Adding the pattern to .gitignore
makes the protection structural rather than discipline-based.

Scoped to .txt only (the docx->txt converted form the project
actually consumes); does not affect sample.md, .gitkeep, or any
other extensions that might land in rough_drafts/ later.
2026-04-11 17:46:00 -04:00
Mark Eaton 22404fbd6b cli: add __main__ guard so python -m cmos.cli actually runs
Without `if __name__ == "__main__": sys.exit(main())` at the bottom
of cli.py, `python -m cmos.cli format <path>` imports the module
but never invokes main(), so the process silently exits 0 with
empty stdout — indistinguishable from a successful run that
produced no output. Discovered during real-draft testing on
2026-04-11.

Adds a regression test that subprocesses the CLI with --help and
asserts on stdout content. argparse --help exits 0 in both broken
and fixed states; stdout content is the only discriminator.

Both invocation paths now work:
- uv run cmos format <path>                (pyproject script entry)
- uv run python -m cmos.cli format <path>  (module invocation)
2026-04-11 17:45:18 -04:00
Mark Eaton 1d970e84f9 exemplars: canary for CMOS 8.159 preposition rule (Huttunen, Mehra)
Two new real-draft-derived journal exemplars locking down prompt
rule 29 from the preceding commit. Follows the Hasanah precedent
for promoting real-draft failures into the canary corpus, per the
"defense in depth" feedback in project memory.

- journal_preposition_huttunen.toml exercises "among" mid-title,
  plus four other CMOS 8.159 lowercased words ("on", "in", "a",
  and implicitly "among"). Real article: Huttunen and Kortelainen,
  JASIST 72, no. 7 (2021). The messy_input uses the ASCII hyphen
  in "Meaning-Making" for clarity — the source draft actually had
  a U+2010 non-breaking hyphen from docx->txt conversion, but
  mixing Unicode hyphen normalization into this exemplar would
  have conflated two independent failure modes. The U+2010 issue
  is noted in the exemplar comment for a separate iteration.

- journal_preposition_mehra.toml exercises "beyond" mid-title,
  plus mid-title exclamation-point preservation (rule 25) and
  leading-"The" drop from "The Library Quarterly" (rule 17).
  Real article: Mehra, Library Quarterly 91, no. 2 (2021).

Both are canary-enabled and will block any future prompt iteration
that regresses on these patterns via the exact-match canary axis.
The canonical dicts additionally lock them down via the field-
level diff, per the upstream-refinement feedback in project
memory.

Canary corpus size: 15 -> 17.
2026-04-11 15:30:02 -04:00
Mark Eaton 0238f5e5d7 iter 8: prompt rule 29 for CMOS 8.159 headline-case prepositions
Real-draft testing on the HML bibliography surfaced inconsistent
preposition capitalization in headline-style titles: the formatter
was sometimes lowercasing "among", "beyond", "within", "into", etc.
and sometimes capitalizing them, violating CMOS 8.159. The behavior
was nondeterministic across similar entries — smoking-gun evidence
of GPT-5 judgment drift on a rule the existing prompt only
implicitly covered via rule 6's "headline-style capitalization".

Rule 29 makes the carve-out explicit and instructs the model to
apply CMOS 8.159 independently of the source's casing, with:

- a substantial but illustrative preposition list including "as"
  (which CMOS 8.159 calls out as always lowercased)
- an explicit scope restriction to PREPOSITIONS, with subordinating
  conjunctions (If, That, Because, Although, Unless, etc.)
  capitalized
- a CMOS 8.161 carve-out for hyphenated compounds: the first
  element is always capitalized (so "In-School" stays, not
  "in-School")
- a first/last-word exception covering "last word of the main
  title immediately before a subtitle colon", which the model
  was treating as mid-title

Verified on the 134-entry HML draft: ~17 entries now produce more
CMOS-correct forms, and two consecutive re-runs show no new rule-
29-related regressions. Observed GPT-5 drift on orthogonal axes
(multi-author inversion, inner-quote style, periodical
italicization) washed out across re-runs, consistent with the
~5% single-run variance documented in project memory.

Rule count: 28 -> 29. No linter version bump required.
2026-04-11 15:29:43 -04:00