Skip to content

feat(wiki): keep a claim's event time apart from its extraction time (#717, slice 1) - #875

Merged
github-actions[bot] merged 1 commit into
mainfrom
fix/717-claim-event-time
Nov 5, 2026
Merged

github-actions[bot] merged 1 commit into
mainfrom
fix/717-claim-event-time

Conversation

@cdeust

@cdeust cdeust commented Nov 5, 2026

Copy link
Copy Markdown
Owner

Summary

First slice of #717. ClaimEvent carried only extracted_at, so a historical event extracted today had no structural place for when it happened. This adds EventTime (unknown / point / interval) built from TimeBound (a value plus its ISO 8601 precision: year, month, day, hour, minute, second). A naive value means the source stated no zone; an aware value keeps the stated offset. ClaimEvent.event_time defaults to unknown and is never derived from extracted_at.

Validators refuse anything that would invent information: components finer than the stated precision ("March 2026" must be 2026-03-01 + month, never a known day), bounds that contradict the kind, reversed intervals, and intervals mixing zoned and unzoned bounds (not comparable).

Refs #717 (not Closes: the issue spans extraction, persistence, synthesis and retrieval).

Root cause addressed in this slice: the contract itself, mcp_server/shared/wiki_ir.py, had no field to hold an occurrence time. The new models live in that file (already baselined for its pydantic import) so the craftsmanship gate stays at its baseline; the file is now 299 lines.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would change existing behavior)
  • Refactor (no functional change; rules/coding-standards.md compliance)
  • Documentation only
  • Audit-finding closure (cite the finding ID)

Test plan

  • New tests_py/shared/test_wiki_ir_event_time.py: 18 passed. Covers the acceptance items within this slice: a 1969 event extracted in 2026 keeps both times through a JSON round-trip; a claim with no stated date is unknown, not its extraction time; unknown / point (month, minute with +02:00 offset) / closed and open intervals round-trip with precision and offset; invented precision and contradictory bounds are rejected.
  • Mutation check: disabling the precision check and the interval order check makes 6 of the 18 tests fail.
  • pytest tests_py/shared/ tests_py/handlers/test_wiki_pipeline_sqlite.py tests_py/handlers/test_wiki_pointer_loop.py tests_py/handlers/test_wiki_draft_tools.py tests_py/handlers/test_wiki_extract_superseded.py tests_py/handlers/test_wiki_refine_confidence.py: 825 passed, 9 skipped (PostgreSQL not reachable here).
  • ruff check / ruff format --check on the changed files: clean. python scripts/check_craftsmanship.py: OK.
  • Not verified locally: the full suite and pyright. The environment could not install the torch stack (proxy refused download-r2.pytorch.org); dependencies were installed from uv.lock without it. CI is the receipt for those.

Audit notes

  • Persistence is untouched on purpose: handlers/wiki_extract._claim_to_dict maps fields explicitly, so the new field is not written to wiki.claim_events yet and no schema changes in this slice.
  • Remaining slices (listed on feat: preserve event time separately from extraction time in ClaimEvent #717): (2) persist event_time in wiki.claim_events on PostgreSQL and SQLite with a one-shot migration; (3) extract stated dates in core/claim_extractor via shared/temporal_normalize, with precision, without defaulting to extracted_at; (4) carry it through synthesis and retrieval, with corrections and contradictory dates preserving provenance.

Coding-standards compliance

  • §2.2 Layer dependency direction preserved (no new import; shared/wiki_ir.py keeps its baselined pydantic import).
  • §3.2 No any in production code.
  • §4.1 No file > 500 lines (repo limit 300: wiki_ir.py is 299).
  • §4.2 No function > 50 lines.
  • §4.4 No function with > 4 parameters.
  • §7 Local reasoning preserved.
  • §8 Constants carry # source: (ISO 8601-1:2019 §3.2; feat: preserve event time separately from extraction time in ClaimEvent #717).
  • §9 No dead code, no TODOs.

Breaking changes

None. event_time is optional with an unknown default; existing ClaimEvent construction is unchanged.

Reviewer checklist

  • CHANGELOG.md updated under the appropriate section.
  • Documentation updated (README / SKILL.md / docs/). Not needed for an internal IR field in this slice.
  • No secrets / credentials / PII in the diff.
  • CI passes on the latest commit. Pending.

Generated by Claude Code

…717, slice 1)

ClaimEvent carried only extracted_at, so a historical event extracted today
had no structural place for when it happened. Add EventTime (unknown, point,
interval) built from TimeBound (value + ISO 8601 precision; a naive value
means the source named no zone, an aware one keeps the stated offset).
A claim with no stated date stays "unknown": event_time is never derived
from extracted_at. Validators reject components finer than the stated
precision, bounds that contradict the kind, reversed intervals and
intervals mixing zoned and unzoned bounds.

Persistence, extraction, synthesis and retrieval are later slices.

Refs #717

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018kyZYtFuPz5BXnBBdsYsJn
@cdeust
cdeust marked this pull request as ready for review November 5, 2026 18:47
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-05T18:48:43.450119Z a039118 Draft marked ready
🔒 Security Review ✅ Completed 2026-11-05T18:49:48.666747Z a039118 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@claude

claude Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Claude finished @cdeust's task in 1m 28s —— View job


PR #875 review (1 of 3)

I found no Blockers. The review lists three Notes, none of which needs action before merge. The main one: when the two bounds have different precisions, wiki_ir.py:80 rejects intervals that are valid.

I could not run pytest, ruff or the craftsmanship gate because this job's permissions did not allow it, so the review relies on CI for those.

Verdict: approve

@claude

claude Bot commented Nov 5, 2026

Copy link
Copy Markdown

Review 1 of 3 (ADR-1119), commit a039118

I found no Blockers. I ran five reviews: code quality, documentation accuracy, performance, security and test coverage. I traced the validators in mcp_server/shared/wiki_ir.py against the tests and checked them for layering, source: annotations and file size (299 lines). The two naive/aware checks run before the comparison, so mixed-zone input fails with a ValidationError, not a TypeError. Nothing writes event_time yet, as the PR states. I could not run pytest, ruff or the craftsmanship gate here because the permissions did not allow it, so CI is the receipt for those.

Notes, no action before merge

  • wiki_ir.py:80: start.value > end.value compares the starts of the two periods. When the bounds have different precisions, a valid interval is rejected: start 2026-03-15 at day precision, end March 2026 at month precision. Compare against the last instant of the end's period, or document the rule and pin it with a test.
  • wiki_ir.py:31 and :49: _FINER_COMPONENTS only works while it stays exactly one step behind TemporalPrecision. Add a test that ties the two together, so a new precision can't quietly check the wrong components.
  • wiki_ir.py:71: self.start or self.end relies on pydantic models always being truthy. Use is not None, as lines 73 and 75 already do.

View job run

Verdict: approve

cdeust commented Nov 5, 2026

Copy link
Copy Markdown
Owner Author

Les trois notes de la revue 1 restent hors de cette PR (ADR-1119 : on ne pousse que pour un Blocker).

  • La note 1 est fondée. start.value > end.value compare les débuts des deux périodes, donc l'intervalle valide « 2026-03-15 (jour) → mars 2026 (mois) » est refusé aujourd'hui. Aucun code n'écrit encore event_time, donc rien n'est touché en pratique. La tranche 2 de feat: preserve event time separately from extraction time in ClaimEvent #717 traitera la comparaison avec le dernier instant de la période de fin, avec son test, avant que le champ soit persisté ou extrait.
  • La note 2 (un test qui lie _FINER_COMPONENTS à TemporalPrecision) et la note 3 (is not None) viendront avec la même tranche.

Generated by Claude Code

cdeust commented Nov 5, 2026

Copy link
Copy Markdown
Owner Author

CI autopilot : merge en échec sur a039118, la cause n'est pas dans la PR.

merge (run 42087687009) a échoué avec install: abandoned, needs success. Ce job agrège les autres, et tous étaient verts sauf install (macos-latest). Ce dernier a été annulé sans démarrer : aucun runner ne l'a pris (annotation GitHub : « The job was not acquired by Runner of type hosted even after multiple attempts », capacité macOS arm64 saturée). La PR ne touche ni scripts/, ni .github/, ni les dépendances. Sur le même sha, install (macos-latest) était vert lors du run précédent.

J'ai relancé une fois les jobs en échec (rerun-failed-jobs). Rien n'a été poussé. Si l'échec revient pour la même cause, il faudra attendre que GitHub ait de nouveau des runners macOS.


Generated by Claude Code

@github-actions
github-actions Bot merged commit 149b943 into main Nov 5, 2026
42 of 44 checks passed
github-actions Bot pushed a commit that referenced this pull request Nov 6, 2026
…slice 2) (#894)

* feat(wiki): persist a claim's event time in wiki.claim_events (#717, slice 2)

ClaimEvent.event_time (slice 1, #875) existed only in memory: the table had
no column for it, so a stored claim kept extracted_at and lost the time of
the event it describes. The column holds the JSON of EventTime (JSONB in
PostgreSQL, JSON in SQLite) so a naive bound stays naive and an aware bound
keeps its stated offset; a TIMESTAMPTZ pair would normalise both to UTC.
Older stores migrate once, existing rows reading back unknown.

Refs #717

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018kyZYunvyM3xpNmz9DLUT7

* fixup! feat(wiki): persist a claim's event time in wiki.claim_events (#717, slice 2)

x

* fixup! feat(wiki): persist a claim's event time in wiki.claim_events (#717, slice 2)

* fixup! feat(wiki): persist a claim's event time in wiki.claim_events (#717, slice 2)

---------

Co-authored-by: Claude <[email protected]>
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