Skip to content

rules-v2: midnight ends, tanker hours, bilingual completions; refuse unreadable LLM values - #100

Merged
baz8080 merged 9 commits into
mainfrom
claude/trusting-dirac-17nbln-extraction
Sep 24, 2026
Merged

baz8080 merged 9 commits into
mainfrom
claude/trusting-dirac-17nbln-extraction

Conversation

@baz8080

@baz8080 baz8080 commented Sep 24, 2026 •

Copy link
Copy Markdown
Owner

PR D of the fixes from the retroactive review. The LLM prompt and PROMPT_VERSION are unchanged. The LLM makes the same midnight mistake; the prompt fix is baz8080/uisce#102, deferred by the owner.

Rules (rules.py, now rules-v2)

  • Midnight. "from 2pm until midnight on 11 September" was read as 11 Sep 00:00, before the works began. Now:
    • a start earlier on the same day ends at D+1 00:00;
    • after a start the day before, only a literal "12am on D" reads D 00:00 ("from 9pm on 21 May until 12am on 22 May");
    • the word "midnight" after a start the day before abstains, as does no start at all.
  • Tanker hours. An until inside a sentence about an alternative water supply, tanker, bowser, water station, bottled water or standpipe makes the rules abstain. It was emitted as the works' end for 239696 to 239698. Word boundaries keep "wastewater station" out of the match.
  • Bilingual completions. An English completion below an Irish block is read when it is the same day's update. The Irish date comes from its header when that parses, else from the date the block opens with. Irish alone still abstains. This closes 244845, 244925 and 245025.
  • "16:59pm" (an hour from 13 to 23 plus "pm") now reads as the 24-hour time.

LLM values and records (inference.py, build.py, eval_rules_shadow.py)

  • Model values build.py couldn't read. Before, one of these failed every CI build.
    • Values with only one possible meaning are normalised: "9:30" becomes "09:30", "24:00" becomes 00:00 on the next day, and a window closing at "24:00" closes at "00:00".
    • Anything else fails the case loudly. The case keeps its previous record, and the failure shows in the run's count.
  • readable_latest and current_states are the one reading of the JSONL, shared by inference, build and the shadow eval.
    • A case whose newest record can't be read is redone.
    • A readable older record is still published meanwhile.
  • Stale records. build.py warns about a stale published record using inference's own selection and state map.
  • One transaction. The DROP, CREATE and INSERTs run together, so a failure can no longer leave inferred_cases empty.
  • The shadow eval compares against the model's own records only.

Review

First round. A high-effort code review confirmed the RULES_VERSION bump is safe and found 10 things. Among them were three places where rules-v2 guessed instead of abstaining: tanker hours under a revising update, midnight after a daytime start, and an Irish completion read against an older English update.

Second round. A medium review found that my first-round fixes had gone too far in places:

  • Unreadable replies. Storing an unreadable reply as not_found could silently replace a good published record. Reverted: the case fails loudly again.
  • The midnight cutoff. An 18:00 cutoff still guessed, so it was replaced by the literal "12am" rule above.
  • The Irish date check. It missed the "10.15rn" header form.
  • The shadow eval. It compared the rules with rules-v1 records.
  • Smaller fixes: type guards, a shared state map, and reusing the open connection.

Measured (release DB and committed JSONL)

rules against the model's own records rules-v1 rules-v2
answered (of 10,870) 9,970 (91.7%) 10,082 (92.8%)
agree 9,969 10,065
disagree 1 17

The 17 disagreements, read in full:

  • 15 are midnight rows where the LLM is wrong.
  • 1 is a known digit transposition by the LLM (237463).
  • 1 has Irish and English headers that disagree with each other (244190).

Labelled replays:

  • Round 1: 73/73.
  • 2026-08-21 round: 113/113.
  • Round 2: 110/111. The one miss is 231853, whose label accepted the midnight bug. The CSV is left as recorded.

rules-v2 abstains on 4 of the 2,691 cases whose latest record is rules-v1: 243084, 244538, 244584 and 244720. CI's --rules-only run can't replace those, so their v1 answer stands, listed in the stale warning, until the LLM residue is run by hand.

JSONL growth: the version bump re-stamps about 2,700 rules-v1 cases with the same answer on the first CI build after merge, taking the file from 19.8 MB to about 21.1 MB.

Notes

Tests

  • New and updated tests across test_rules.py, test_inference.py and test_build.py, including one for each reviewer probe text.
  • uv run ruff check and uv run pytest pass: 734 tests, merged with current main.

🤖 Generated with Claude Code

https://claude.ai/code/session_0168hLW2X3mkV3Jhm26LJSwQ

- "from 2pm until midnight on 11 September" was read as 11 September
  00:00, before the works began (244089, 245031). A 00:00 end is now
  read against the "from" before its "until": a start earlier that
  day gives D+1 00:00, a start the day before keeps D 00:00, and no
  start (or an estimated completion time of midnight) abstains.
- An "until" in a sentence about an alternative water supply, tankers
  or bottled water gave the tanker's hours as the works' end (239696).
  It is no longer a candidate; alone, the rules abstain.
- The Irish-completion check abstained per block, so an English
  completion below an Irish block was never read (244925, 129 cases).
  An Irish completion now abstains only when the next block with text
  has no English completion.
- "16:59pm" (245025): an hour 13-23 with "pm" reads as the 24-hour time.

Shadow eval against the LLM's 10,870 hash-stable records: coverage
91.7% -> 93.3%, 17 disagreements, 15 of them the LLM's midnight
reading. RULES_VERSION bumps, so CI re-answers every rules-v1 case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0168hLW2X3mkV3Jhm26LJSwQ
- parse_response rejects a local_time, local_date or window value that
  is not HH:MM (00-23) or an ISO date, so the case counts as failed
  and is retried. "24:00", "5pm" or "28/04/2026" in the JSONL used to
  raise in build and fail every CI build after it.
- build leaves an already-written unreadable record out with a
  ::warning::, and uisce-infer treats it as absent so the case is
  inferred again.
- The inferred_cases rebuild runs in one transaction: DROP and CREATE
  autocommitted, so a raise after them left the table empty.
- A published record uisce-infer would redo (description changed, or
  extractor or prompt retired) is still published but now listed in a
  ::warning::, decided by the same is_current the inference run uses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0168hLW2X3mkV3Jhm26LJSwQ
The rules note gets a dated 2026-09-24 section with the three rules
changes, the shadow eval, the labelled replays and the rules-only run
against the 2026-09-23 release. The roadmap gets the prompt line that
makes the LLM read "until midnight on D" as the start of D, with the
16 affected published records, a proposed wording and the order of
work, waiting on the owner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0168hLW2X3mkV3Jhm26LJSwQ
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0168hLW2X3mkV3Jhm26LJSwQ
From a code review of this PR against current main:

- An alternative-supply `until` was skipped, which could leave the stale
  original schedule as the only candidate under a revising update. It
  now makes the rules abstain; ALTERNATIVE_SUPPLY has word boundaries,
  so "wastewater station" is not a water station.
- "until midnight on D" after a start on D-1 is read as D 00:00 only
  for a start from 18:00; a daytime start abstains.
- An Irish completion reads the next block only when it is the same
  day's update, the Irish date taken from its opening text.
- A model value that cannot be read is normalised where it has one
  meaning and otherwise stored as not_found with the value in its notes,
  instead of failing and being re-sent at temperature 0 forever.
- readable_latest is the one newest-record reading, shared by inference,
  build and the shadow eval; unreadable newest records are redone and
  warned about only until they are; the stale warning reuses inference's
  own selection and no longer counts open by the feed's status.

Re-measured: rules-v2 answers 10,082 of 10,870 LLM records (was
10,137), agreeing on 10,065, the same 17 disagreements; labelled
rounds 73/73, 110/111, 113/113 as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0168hLW2X3mkV3Jhm26LJSwQ
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0168hLW2X3mkV3Jhm26LJSwQ
From a second code review of the review-fix commit:

- An unreadable model reply fails the case again. Storing it as
  not_found silently replaced a good published record with none; a
  failure keeps that record and shows in the run's count. The
  normalisation of one-meaning near-misses stays, now type-guarded.
- After a start the day before, only a literal "12am on D" reads D
  00:00; the word "midnight" abstains whatever the start time (a 6pm
  start was no less ambiguous than 9am).
- The Irish block's date comes from its parsed header when there is one
  ("10.15rn"), else from its opening text.
- The shadow eval compares against the model's own records only.
- stale_cases shares inference's state map (current_states) and the
  open connection (cases_needing_inference).

Re-measured: unchanged at 10,082 answered of 10,870 model records,
10,065 agreeing, the same 17 disagreements; replays 73/73, 110/111,
113/113. 4 rules-v1 records keep their answer until the residue run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0168hLW2X3mkV3Jhm26LJSwQ
@baz8080
baz8080 merged commit 33c7117 into main Sep 24, 2026
2 checks passed
@baz8080
baz8080 deleted the claude/trusting-dirac-17nbln-extraction branch September 24, 2026 11:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants