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
Assertion date fields (created, updated, last-verified) are stored as date-only YYYY-MM-DD with no timezone marker. Two failures follow:
Unmarked timezone, so agents get it wrong, repeatedly. Nothing in the stored value says the date is UTC. Agent harnesses inject a local "today" into the agent's context, while the write helper derives created/updated from the real UTC clock. Near the UTC-midnight boundary these are different calendar days, so an agent supplying --last-verified from its local clock disagrees with the helper's UTC created.
Day-truncation hides the disagreement. A real incident: a note was written with created: 2026-06-09 (helper, UTC) and last-verified: 2026-06-08 (agent, local), i.e. verified before it was created, produced silently. With sub-day precision the two values would have been visibly adjacent across midnight; truncating to the day erased which side each fell on.
Context
A single parse chokepoint absorbs the format change. Every staleness and freshness decision funnels through one helper, computeAgeDays, which returns whole days. Nothing else in the codebase parses a stored date value, sorts notes lexically by date, or depends on sub-day precision. The stored format can gain precision without rippling into consumers.
captured-at is the in-repo precedent, at a different precision. The event record family already stores captured-at as a full ISO-8601 instant, but at millisecond precision. Aligning both families on one second-precision format is the consistency win, not a new convention.
Legacy notes can't be migrated cleanly, so they aren't. The established coding vault holds ~275 assertion notes already on date-only values, and their created dates are hand/template-authored semantic dates not recoverable from git history. Rewriting them would be large and would fabricate sub-day times the records never had. Because computeAgeDays reads both forms identically at day resolution, leaving legacy values in place is lossless for every consumer.
Coherence is cheaper to guarantee at the writer than the validator. With one clock owning all three dates at creation, last-verified < created becomes structurally impossible to produce, so no cross-field validation rule (and the edit-time friction it would add to legacy notes) is needed.
Proposed solution
Store created, updated, last-verified, and event captured-at as explicit-UTC second-precision timestamps, YYYY-MM-DDTHH:MM:SSZ, sourced from a single authoritative clock.
One clock at creation.kb-add derives last-verified from the same instant it stamps created (a freshly authored note is verified as of creation), so the three dates start equal and diverge only as the note is edited or re-verified. The agent-supplied --last-verified flag, the entry point for a foreign-clock date, is removed.
Permanent dual-accept validation. Frontmatter validation accepts both the legacy bare-date form and the new timestamp form, and rejects genuinely malformed values, including a timestamp with no Z marker. Writers emit only the timestamp form. This is a lasting compatibility policy, not a transition window; legacy notes keep their day-resolution values.
Consumers unchanged in granularity. The single age helper parses both forms and continues to return whole-day ages, so staleness and freshness ranking behave as before.
Coherence guaranteed at the writer. Because every writer orders last-verified at or after created, an incoherent last-verified < created record cannot be produced; no cross-field validator is introduced.
Acceptance criteria
Must have
created, updated, and last-verified are written as explicit-UTC second-precision timestamps in the form YYYY-MM-DDTHH:MM:SSZ.
Event captured-at is written in the same second-precision form, so timestamp representation is consistent across the assertion and event record families.
For any note kb-add writes, created, updated, and last-verified all originate from one creation instant; the --last-verified flag is removed so no agent-supplied date from a foreign clock can enter.
No writer can produce a record where last-verified precedes created (kb-add writes them equal; kb-edit --verify sets last-verified to an instant at or after created).
Frontmatter validation accepts both the bare YYYY-MM-DD form (legacy notes) and the full YYYY-MM-DDTHH:MM:SSZ form, and rejects malformed values, including a timestamp lacking the explicit Z marker or carrying an unreal calendar date or time. The permanent dual-accept policy (legacy notes are not migrated) is documented.
kb-curate staleness detection and kb-retrieve freshness ranking compute correct whole-day ages from both date forms.
Documentation, help text, and usage examples are updated to remove the --last-verified flag (including the kb-add skill doc), leaving no dangling references.
New and modified behavior in this change is covered by tests.
Nice to have
The kb create schema seed gains a one-line note documenting the timestamp date convention, so newly provisioned stores explain it.
Problem
Assertion date fields (
created,updated,last-verified) are stored as date-onlyYYYY-MM-DDwith no timezone marker. Two failures follow:created/updatedfrom the real UTC clock. Near the UTC-midnight boundary these are different calendar days, so an agent supplying--last-verifiedfrom its local clock disagrees with the helper's UTCcreated.created: 2026-06-09(helper, UTC) andlast-verified: 2026-06-08(agent, local), i.e. verified before it was created, produced silently. With sub-day precision the two values would have been visibly adjacent across midnight; truncating to the day erased which side each fell on.Context
computeAgeDays, which returns whole days. Nothing else in the codebase parses a stored date value, sorts notes lexically by date, or depends on sub-day precision. The stored format can gain precision without rippling into consumers.captured-atis the in-repo precedent, at a different precision. The event record family already storescaptured-atas a full ISO-8601 instant, but at millisecond precision. Aligning both families on one second-precision format is the consistency win, not a new convention.createddates are hand/template-authored semantic dates not recoverable from git history. Rewriting them would be large and would fabricate sub-day times the records never had. BecausecomputeAgeDaysreads both forms identically at day resolution, leaving legacy values in place is lossless for every consumer.last-verified < createdbecomes structurally impossible to produce, so no cross-field validation rule (and the edit-time friction it would add to legacy notes) is needed.Proposed solution
Store
created,updated,last-verified, and eventcaptured-atas explicit-UTC second-precision timestamps,YYYY-MM-DDTHH:MM:SSZ, sourced from a single authoritative clock.kb-addderiveslast-verifiedfrom the same instant it stampscreated(a freshly authored note is verified as of creation), so the three dates start equal and diverge only as the note is edited or re-verified. The agent-supplied--last-verifiedflag, the entry point for a foreign-clock date, is removed.Zmarker. Writers emit only the timestamp form. This is a lasting compatibility policy, not a transition window; legacy notes keep their day-resolution values.last-verifiedat or aftercreated, an incoherentlast-verified < createdrecord cannot be produced; no cross-field validator is introduced.Acceptance criteria
Must have
created,updated, andlast-verifiedare written as explicit-UTC second-precision timestamps in the formYYYY-MM-DDTHH:MM:SSZ.captured-atis written in the same second-precision form, so timestamp representation is consistent across the assertion and event record families.kb-addwrites,created,updated, andlast-verifiedall originate from one creation instant; the--last-verifiedflag is removed so no agent-supplied date from a foreign clock can enter.last-verifiedprecedescreated(kb-addwrites them equal;kb-edit --verifysetslast-verifiedto an instant at or aftercreated).YYYY-MM-DDform (legacy notes) and the fullYYYY-MM-DDTHH:MM:SSZform, and rejects malformed values, including a timestamp lacking the explicitZmarker or carrying an unreal calendar date or time. The permanent dual-accept policy (legacy notes are not migrated) is documented.kb-curatestaleness detection andkb-retrievefreshness ranking compute correct whole-day ages from both date forms.--last-verifiedflag (including thekb-addskill doc), leaving no dangling references.Nice to have
kb createschema seed gains a one-line note documenting the timestamp date convention, so newly provisioned stores explain it.