Skip to content

Resolve pushed structured dates and timestamps without a chat model - #1090

Merged
WaylandYang merged 1 commit into
deeplethe:devfrom
ansonnmm:fix/1089-pushed-structured-time
Oct 6, 2026
Merged

WaylandYang merged 1 commit into
deeplethe:devfrom
ansonnmm:fix/1089-pushed-structured-time

Conversation

@ansonnmm

@ansonnmm ansonnmm commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Refs #1089

Why

A pushed statement already carries when / ended as caller-supplied words (ADR 0054). When those words are a complete date or a zoned timestamp, the payload is already structured — yet today every time mention goes through the chat model, so on an installation without a chat model a push leaves a resolve_time job that fails retries even though the value is deterministically readable.

What changes

Inside resolve_document, for documents whose source kind is statements only:

  • Each distinct mention is first offered to the existing parse_time. A complete date or zoned timestamp (YYYY-MM-DD, YYYY-MM-DDTHH[:MM[:SS]] with Z or an offset) becomes a synthetic point / absolute interpretation at the precision the value writes, and is never included in the model request list — so a rerun asks the same nothing and a model reply cannot overwrite it.
  • The seeded interpretation flows through the unchanged resolve_interpretations / combine / set_open_validity machinery, so it can also anchor model-answered mentions.
  • With no chat model configured, pushed documents resolve what code can, leave the rest at grade C (mention kept, interpretation empty), and the job completes instead of failing.

The distinction is source identity, not string shape: a document whose text happens to contain 2026-09-23T08:14:03Z keeps the existing model path, and non-pushed documents still fail fast without a chat model.

What does not change

  • The statements contract: when / ended stay polymorphic words; no new slot, no new shape for a bare instant.
  • parse_time itself, the store/schema, marks binding semantics, materialization, equal-end invariants, and job retry machinery.
  • Bare years and year-months are not in this cut — they still go to the model or wait at C.

Time semantics

A caller-stated complete date/timestamp is grade A at exactly the precision written; a when date under a state binding still resolves to whatever marks decides; when + ended is an interval; equal-end rows stay forbidden.

#994 relationship

A timestamp-valued ended is read at its stated precision by this change. Period-word ends ("ended last quarter" style) remain words on the model path — this PR does not implement #994's period-end semantics and does not invent a sentinel.

Validation

  • New time_resolution::pushed_tests (real Postgres + scripted model endpoint): no-model date/timestamp grade A resolution, precision matrix day/hour/minute/second, +08:00 offset folded into the UTC value, when+ended interval, unreadable mentions at C with the job succeeding, mixed pushes, document-path unchanged, code-resolved mention never asked and stable across reruns (also serving as an anchor for a model-answered relative mention).
  • cargo test -p utopia-server green (596 tests incl. all existing time_resolution / statements / extraction tests), cargo test --workspace green, cargo clippy clean, cargo fmt clean.
  • ADR 0054 open question annotated with the settled decision; ADR 0045 decision 8 notes a structured timestamp is not a time word.

A pushed statement (0054) carries its when/ended as caller-supplied
words, and a complete date or zoned timestamp in that payload is
already structured: resolve_time reads it in code at the precision it
writes, keeps the literal as the mention, and never asks the model
about it. Bare years, year-months and words code cannot read still go
to the model, or wait at grade C when no chat model is configured, and
the job completes either way instead of failing for a missing chat
configuration.

The distinction is source identity, not string shape: documents keep
the model path even when their text happens to look like a timestamp.

Signed-off-by: Anson <310461893+ansonnmm@users.noreply.github.com>

@WaylandYang WaylandYang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the cut agreed on #1089, and nothing more: a complete date or a zoned timestamp from a pushed statement is read in code and graded A, everything else still goes to the model, and resolve_time no longer fails for a statements source when no chat model is configured.

I ran both builds as a real server with no chat model and pushed the same three statements through POST /sources/{id}/statements:

when / ended dev this PR
2026-09-23T08:14:03+08:00 no interval, job retries with "Chat model not configured" 2026-09-23 00:14:03Z, second precision, grade A
2024-03-01 / 2025-02-28 no interval 2024-03-01 to 2025-02-28, day precision, grade A
last Tuesday no interval no interval, grade C

All three resolve_time jobs finish on the first attempt with the PR; on dev they stay queued and retry.

One thing to know, not a blocker: parse_time reads a clock without a zone (2026-09-23T08:14:03) as the date at day precision, so such a value lands on the day, not the second. That is the reader's existing behaviour and the right side to err on. If you push sensor timestamps, send the offset.

Merging. Thanks for the careful tests and for writing the decision into 0045 and 0054.

@WaylandYang
WaylandYang merged commit b29a60a into deeplethe:dev Oct 6, 2026
6 checks passed
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