the KB already knows what it knows about an entity, but it has no way to show how that knowledge accrued over time. kb.read_entity returns the entity and its current claims as a flat set; kb.neighbors (#184) expands graph adjacency at a point in time; kb.trace (#197) walks the provenance DAG to explain how one artifact derives from another. none of these answers "what did the KB learn about this entity, in what order?" — a trajectory. the raw material is already on disk: Claim carries created_at / updated_at / last_confirmed_at, Relation carries created_at / updated_at, and decision time is recoverable from the decided_at field on decided/ records and the append-only audit.log.jsonl. this issue asks for a read-only method that orders an entity's approved claims, relations, and decisions along that time axis.
proposed surface
a new read-only method kb.timeline and its CLI mirror vouch timeline.
- input:
entity_id (required); optional since / until iso-8601 bounds; optional order in {effective, decided} (default effective — orders by created_at / last_confirmed_at; decided orders by the decided_at recovered from decided/ + the audit log); optional types filter (e.g. decision,fact); optional limit.
- output: an ordered list of entries, each
{when, kind, id, summary, status} where kind is claim | relation and when is the chosen timestamp. status reflects the artifact's current ClaimStatus for claims (a superseded claim still appears, flagged); Relation has no status field, so status is null for relation entries. the shape stays close to kb.neighbors for surface consistency.
- CLI:
vouch timeline <entity-id> [--order decided] [--since 2026-01-01] [--until ...] [--types decision,fact] [--limit N], human table by default, --json for the machine shape.
only approved durable artifacts are read; pending proposals never appear.
review gate & scope
this is a pure read — no propose_*, no kb.approve, no mutation. it does not create or edit knowledge, so there are no gate implications: it is a viewport over already-reviewed artifacts, exactly like kb.read_entity and kb.neighbors. the timestamp reconstruction reads decided/ and audit.log.jsonl only; it never edits either. all timestamp/ordering logic lives in the read path (a new helper alongside the other read methods), not in storage.py, which stays pure I/O. everything runs against the local .vouch/ — no network, no external service.
per the per-tool convention, kb.timeline attaches the _meta.vouch_salience sidebar inline (via salience.attach_salience, as kb_context does) rather than relying on any global decorator.
being a new kb.* method, it must touch the four registration sites or test_capabilities will fail:
- MCP tool in
src/vouch/server.py (@mcp.tool() kb_timeline)
- JSONL handler in
src/vouch/jsonl_server.py (_h_timeline + HANDLERS["kb.timeline"])
METHODS list in src/vouch/capabilities.py
- CLI command in
src/vouch/cli.py
plus a test under tests/test_timeline.py.
relationship to adjacent issues
distinct from #197 (kb.trace): that walks the provenance DAG to explain derivation — which source or claim an artifact descended from; timeline orders an entity's own artifacts along a time axis (one is causal, one is chronological). distinct from #184 (kb.neighbors): that expands graph adjacency, not chronology. distinct from #232 (visibility-aware kb.audit): timeline reads the audit log only to recover decided_at for ordering, it is not an audit-query surface.
acceptance criteria
the KB already knows what it knows about an entity, but it has no way to show how that knowledge accrued over time.
kb.read_entityreturns the entity and its current claims as a flat set;kb.neighbors(#184) expands graph adjacency at a point in time;kb.trace(#197) walks the provenance DAG to explain how one artifact derives from another. none of these answers "what did the KB learn about this entity, in what order?" — a trajectory. the raw material is already on disk:Claimcarriescreated_at/updated_at/last_confirmed_at,Relationcarriescreated_at/updated_at, and decision time is recoverable from thedecided_atfield ondecided/records and the append-onlyaudit.log.jsonl. this issue asks for a read-only method that orders an entity's approved claims, relations, and decisions along that time axis.proposed surface
a new read-only method
kb.timelineand its CLI mirrorvouch timeline.entity_id(required); optionalsince/untiliso-8601 bounds; optionalorderin{effective, decided}(defaulteffective— orders bycreated_at/last_confirmed_at;decidedorders by thedecided_atrecovered fromdecided/+ the audit log); optionaltypesfilter (e.g.decision,fact); optionallimit.{when, kind, id, summary, status}wherekindisclaim | relationandwhenis the chosen timestamp.statusreflects the artifact's currentClaimStatusfor claims (a superseded claim still appears, flagged);Relationhas no status field, sostatusis null for relation entries. the shape stays close tokb.neighborsfor surface consistency.vouch timeline <entity-id> [--order decided] [--since 2026-01-01] [--until ...] [--types decision,fact] [--limit N], human table by default,--jsonfor the machine shape.only approved durable artifacts are read; pending proposals never appear.
review gate & scope
this is a pure read — no
propose_*, nokb.approve, no mutation. it does not create or edit knowledge, so there are no gate implications: it is a viewport over already-reviewed artifacts, exactly likekb.read_entityandkb.neighbors. the timestamp reconstruction readsdecided/andaudit.log.jsonlonly; it never edits either. all timestamp/ordering logic lives in the read path (a new helper alongside the other read methods), not instorage.py, which stays pure I/O. everything runs against the local.vouch/— no network, no external service.per the per-tool convention,
kb.timelineattaches the_meta.vouch_saliencesidebar inline (viasalience.attach_salience, askb_contextdoes) rather than relying on any global decorator.being a new
kb.*method, it must touch the four registration sites ortest_capabilitieswill fail:src/vouch/server.py(@mcp.tool()kb_timeline)src/vouch/jsonl_server.py(_h_timeline+HANDLERS["kb.timeline"])METHODSlist insrc/vouch/capabilities.pysrc/vouch/cli.pyplus a test under
tests/test_timeline.py.relationship to adjacent issues
distinct from #197 (
kb.trace): that walks the provenance DAG to explain derivation — which source or claim an artifact descended from; timeline orders an entity's own artifacts along a time axis (one is causal, one is chronological). distinct from #184 (kb.neighbors): that expands graph adjacency, not chronology. distinct from #232 (visibility-awarekb.audit): timeline reads the audit log only to recoverdecided_atfor ordering, it is not an audit-query surface.acceptance criteria
kb.timelinereturns an entity's approved claims and relations ordered by the chosen time axis, most-recent-last (or documented direction)order=effectiveuses artifact timestamps;order=decidedreconstructs decision time fromdecided/+audit.log.jsonlwithout editing eithersince/until/types/limitfilters apply as documentedClaimStatus; relation entries carrystatus = null; pending proposals never appear_meta.vouch_salienceis attached inline per the per-tool conventiontest_capabilitiespassesvouch timelineCLI mirrors the method with human +--jsonoutputtests/test_timeline.pycovers ordering, bothordermodes, the filters, and the superseded-still-visible casemake checkgreen (pytest, mypy, ruff)