You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A pushed statement cannot carry a stated world time without a chat model #1089
A statements source exists so a system that already holds {thing, relation, value, when} can push observations without a model in the loop
(ADR 0054). Today such a caller cannot give a statement a stated world time: when and ended are stored verbatim as time mentions, and the only path from a
mention to valid_from/valid_to is resolve_time, which requires a configured
chat model. A caller that already holds 2026-09-23T08:14:03Z must spell it into
words and pay a model call to read it back — the round trip the contract removed
for the statement itself. On a deployment with no chat model the words never
resolve, and each pushed document additionally leaves a resolve_time job that
fails permanently after its retries.
Current behavior
Observed on main at 0197bd8f (an earlier version of this was also exercised live
against v0.1.0-rc7):
when/ended in the s tuple are free strings; extra contract keys refuse at
the door, but the time strings themselves are recorded verbatim as time_mentions
(utopia-extract/open.rs, api/sources_routes.rs, extraction_open.rs).
Every pushed document enqueues resolve_time (extraction_open.rs), and resolve_document returns "Chat model not configured" before reading mentions
(time_resolution.rs); jobs retry three times by default, so a model-free
deployment accumulates one failed job per push.
doc_time lands on attestation: the open row's attested_from takes it when doc_time_source is source/content, and world-axis reads order an undated row
by COALESCE(valid_from, attested_from) — an observation anchor, not a stated
interval.
With a chat model configured, an RFC 3339-looking when resolves as an absolute
date correctly today — the model does in prose what the caller already had in
structure.
Why doc_time is not equivalent
doc_time says when the source dated the observation; when/ended say when the
relation held. For live telemetry they coincide and attestation orders the row
correctly. For backfilled history, effective-dated records, event time that differs
from ingest time, or an explicit end, only when/ended can carry it — and today
that means words a model must re-read.
Generic use cases
An ERP or event-bus connector replaying postings whose effective dates precede
ingest.
A robot or sensor log pushed after collection, where event time is a timestamp,
not a phrase.
A migration or import tool whose source records carry their own validity dates.
A security or monitoring pipeline stating "the session was open between T1 and T2".
Design question
This is the open question ADR 0054 left: "Whether when should accept an RFC 3339
instant directly rather than time words, once the instant precision on the roadmap
exists." It is worth deciding on its own terms: may a pushed item carry its world
time in a form code resolves — e.g. an RFC 3339 timestamp at the ladder's finest
current precision (second, ADR 0024) — while everything else in the item remains
time words on the model path?
Smallest useful first cut
A shape to react to, not a prescription: when when/ended parses as a complete
RFC 3339 timestamp, the server resolves it in code — an absolute reference at second precision, grade A since the payload states it — and still records the
literal string as the mention for evidence. parse_time
(utopia-extract/src/lib.rs) already reads exactly this shape at
hour/minute/second precision for date-typed values and tool arguments, and 0024's
third decision already blesses "a timestamp a machine wrote" at sub-day precision —
the reader exists; the question is only whether the mentions path may call it. Every other value keeps today's model
interpretation. Questions the cut would have to answer:
Is a bare instant a point or a since? For a state property, do the binding's marks decide (as for any statement dated to one moment), or is another shape
needed given empty_state_span refuses a zero-length state span?
Does ended take the same form, and how would a caller express "ended, date
unknown"?
If resolve_time re-runs over a mention the caller resolved in code, does the
code resolution stand or defer to the model?
Polymorphic when/ended, or a sibling structured slot in s?
Non-goals
No typed writes — no predicate_id, no class, no promise that a pushed
statement binds before alignment reads it; 0054's "not doing" stands.
No batch endpoint and no new envelope fields — the response stays {"action": …} and caller identity stays external_id.
No code matching natural-language time words — 0045's eighth decision stands;
this only removes a model round trip for input that was never a word.
Correctly dated open statements is the whole deliverable. Typed
materialization still flows through alignment and a model or a person's binding
decision; nothing here makes pushed statements reach the typed graph on their own.
Suggested acceptance criteria
If the direction is accepted, a reasonable bar for a first cut:
A statements push whose when/ended is a complete RFC 3339 timestamp lands as
an open statement with valid_from/valid_to at second (or coarser, per the
ladder) precision and grade A — with no chat model configured.
The literal string stays recorded as the mention for evidence; re-running resolve_time does not silently regress it.
Any when/ended that is not a timestamp takes today's model path unchanged.
Keys outside the contract still refuse at the door; the e/n/deleted shape is
untouched.
Questions for maintainers
Polymorphic when/ended, a separate structured slot in s, or another form?
Is a caller-stated instant correctly read as an absolute reference with grade A?
How should a bare instant interact with a state property's marks and empty_state_span?
Should "ended, date unknown" be expressible, or is that words-only by design?
On resolve_time re-runs, does a code-resolved mention stand as resolved or
defer to the model?
Thanks @ansonnmm. This is the question 0054 left open, and the failing resolve_time job on a deployment with no chat model is a defect on its own. The direction is accepted; a PR is welcome.
Your five questions:
Polymorphic when / ended, no new slot. The contract's shape stays. For a pushed item the payload is the evidence, so a timestamp there is what the source said.
Grade A. A is "an absolute date the text writes" (0045), and the payload writes it.
"Ended, date unknown" stays words-only in this cut. A sentinel for it is a separate decision.
The code's resolution stands, because it happens inside resolve_time. A mention the code can read is resolved there and never sent to the model, so a re-run gives the same answer.
Three things about the cut:
A complete timestamp or a complete date. What parse_time already reads at day precision or finer: YYYY-MM-DD, and YYYY-MM-DDTHH[:MM[:SS]] with Z or an offset, each at the precision it is written to. A date matters as much as an instant: an effective-dated record knows the day, and making its caller write T00:00:00Z would store a precision the source never had (0024). A bare year or year-month is not in this cut.
Pushed statements only. Mentions read from documents keep the model path, whatever they look like.
resolve_time does not fail for want of a chat model. It resolves what code can, leaves the rest unresolved at grade C, and ends. That removes the permanently failed job per push.
The rest as you wrote it: the literal string stays as the mention, anything else takes today's path, and nothing about typed writes changes. Two notes on timing: #1087 has just changed how resolve_document orders its work, so please build on current dev; and #994 is about to change how an end that names a period is stored, so say in the PR how an ended given as a timestamp sits with it. Please add a dated note under 0054's open question and a line under 0045's decision 8 that a timestamp is not a time word.
Landed in #1090 (b29a60a on dev): a complete date or a zoned timestamp from a pushed statement is resolved in code at grade A, and resolve_time finishes for a statements source with no chat model configured. Checked end to end on a server without a chat model; the numbers are in the review on #1090.
Activity
WaylandYang commentedon Oct 5, 2026
Thanks @ansonnmm. This is the question 0054 left open, and the failing
resolve_timejob on a deployment with no chat model is a defect on its own. The direction is accepted; a PR is welcome.Your five questions:
when/ended, no new slot. The contract's shape stays. For a pushed item the payload is the evidence, so a timestamp there is what the source said.markssays whether it is the start or the end of a state; with neither, the statement stays in the open graph with its date, as today (A statement dated at one moment, bound to a state property, becomes a state that holds at no moment #966). A state row whose ends are equal is still never written (A binding to a state property says what a single date marks, and a state row whose ends are equal is never written #975).whenwithendedis an interval.resolve_time. A mention the code can read is resolved there and never sent to the model, so a re-run gives the same answer.Three things about the cut:
parse_timealready reads at day precision or finer:YYYY-MM-DD, andYYYY-MM-DDTHH[:MM[:SS]]withZor an offset, each at the precision it is written to. A date matters as much as an instant: an effective-dated record knows the day, and making its caller writeT00:00:00Zwould store a precision the source never had (0024). A bare year or year-month is not in this cut.resolve_timedoes not fail for want of a chat model. It resolves what code can, leaves the rest unresolved at grade C, and ends. That removes the permanently failed job per push.The rest as you wrote it: the literal string stays as the mention, anything else takes today's path, and nothing about typed writes changes. Two notes on timing: #1087 has just changed how
resolve_documentorders its work, so please build on current dev; and #994 is about to change how an end that names a period is stored, so say in the PR how anendedgiven as a timestamp sits with it. Please add a dated note under 0054's open question and a line under 0045's decision 8 that a timestamp is not a time word.WaylandYang commentedon Oct 6, 2026
Landed in #1090 (b29a60a on dev): a complete date or a zoned timestamp from a pushed statement is resolved in code at grade A, and
resolve_timefinishes for a statements source with no chat model configured. Checked end to end on a server without a chat model; the numbers are in the review on #1090.