Skip to content

Stale Knowledge Is Worse Than No Knowledge: KCP v0.19 and v0.20 Close the Temporal Gap

Closing the Temporal Gap: the problem with timeless knowledge (compliance risk, deprecated-vs-temporal), v0.19's bi-temporal model (valid time vs transaction time, superseded_by link, manifest-level defaults), v0.20's as_of query parameter (audit mode vs production mode, critical use cases), and the new four-step search flow (semantic scoring → not_for filter → temporal evaluation → final selection).

The previous post ended with a list of gaps still visible after v0.18: federated trust delegation, transport integrity, digest cost budgets. The one I left off the list, because it was already in progress, was time.

Not performance. Not latency. Actual calendar time — the question that turns out to matter enormously for knowledge that agents load into context: when is this unit valid?

v0.19 and v0.20 answer that question. v0.19 lets manifest authors declare temporal validity. v0.20 lets agents query against it.


The problem with timeless knowledge

When you deploy a knowledge unit today, it carries no expiry date. The manifest says what the unit is. The bridge serves it. The agent loads it. There is nothing in the protocol that distinguishes a unit authored yesterday from one authored three years ago and last updated never.

This is fine for stable knowledge. An architecture diagram that has not changed in two years is probably still accurate. The problem is knowledge that does change — and that changes in ways that invalidate conclusions, not just add detail.

Compliance regulations are the sharpest example. GDPR guidance is updated. New EDPB opinions supersede old ones. A knowledge unit that accurately described the supervisory authority's position in 2022 may describe the opposite of their position today. An agent that loads it does not know this. The unit scored well against the query terms. It matched on audience. It was served.

The agent now answers a compliance question based on superseded guidance, confidently, because nothing in the context told it the knowledge was no longer valid.

The Compliance Trap: what the agent sees (GDPR guidance from 2022, scored and served) versus the real world (that guidance superseded, the agent's answer now the opposite of correct). Stale knowledge that looks authoritative acts as a hallucinogen.

API documentation is the same story. You mark a unit deprecated when the endpoint is retired. But deprecated is a boolean — it says "do not use this" without saying "and here is when it stopped being true." Architecture decisions get superseded by later decisions. Data retention policies get updated when regulation changes. Security controls get revised after incidents.

Without a validity window, an agent has no basis for distinguishing "this was true last week" from "this was true in 2021." It treats both the same.

Stale Knowledge Is Worse Than No Knowledge: without temporal metadata, agents cannot distinguish between a stable architecture diagram and a superseded compliance regulation. The [WARNING: Temporal Context Missing] annotation is not a UI error — it is the correct state of every timeless knowledge unit.


v0.19: declaring when knowledge is valid

The fix is a temporal block, optional at both the manifest root and per unit (§4.22):

units:
  - id: gdpr-data-subject-rights
    path: regulations/eu/gdpr/chapter-3.md
    intent: "Data subject rights under GDPR Articles 15-22"
    temporal:
      valid_from: "2018-05-25"
      valid_until: null          # open-ended — still current
      recorded_at: "2026-03-01"  # when this version was authored

  - id: schrems-ii-guidance
    path: regulations/eu/edpb/schrems-ii-2021.md
    intent: "EDPB guidance on Schrems II transfers post-judgment"
    temporal:
      valid_from: "2021-06-18"
      valid_until: "2023-09-14"  # superseded by new SCCs guidance
      superseded_by: "scc-guidance-2023"

  - id: scc-guidance-2023
    path: regulations/eu/edpb/scc-guidance-2023.md
    intent: "Updated standard contractual clauses guidance"
    temporal:
      valid_from: "2023-09-14"

The model is bi-temporal: two independent timelines per unit.

Valid time (valid_from, valid_until) describes when the knowledge is true in the real world — when the regulation took effect, when the guidance was superseded, when the policy was retired. This is the timeline that matters for queries.

Transaction time (recorded_at) describes when this version was authored in the manifest — when the knowledge was added to this dataset. This is informational, useful for auditing the knowledge base itself.

superseded_by links the old unit to its replacement within the same manifest. Validators warn on dangling references and on valid_until in the past without a superseded_by — that is almost always a maintenance oversight.

The bi-temporal model: two independent timelines per unit. Valid time (valid_from/valid_until) tracks when the knowledge is true in the real world. Transaction time (recorded_at) tracks when this version entered the manifest. They move independently — a unit authored in March 2026 can declare valid time starting May 2018.

Manifest-level defaults

If most units in a manifest share the same temporal window — say, a regulatory corpus that became effective on a specific date — you can set a manifest root default:

kcp_version: "0.19"
project: gdpr-corpus
temporal:
  valid_from: "2018-05-25"
  recorded_at: "2026-01-01"
units:
  - id: article-5
    # inherits valid_from: 2018-05-25 from manifest root
    ...
  - id: article-5-amendment-2022
    # unit-level override applies field-by-field
    temporal:
      valid_from: "2022-07-12"

Unit-level overrides apply field-by-field, not as a block replacement. Set one field; the rest inherit from the root default.

What parsers and bridges must do

Parsers must read and expose temporal fields. The spec is explicit: bridges without temporal evaluation — bridges that have not yet implemented the query phase — must treat all units as active. That is the safe default. Not serving any units until you implement temporal filtering would be worse than serving all of them.

Bridges that do implement temporal evaluation must filter: valid_from ≤ today AND (valid_until IS NULL OR valid_until ≥ today). Today is the default query date.


v0.20: querying a point in time

Declaring temporal validity would be useful even if the bridge just applied a "valid today" filter automatically. But there is a second use case that requires an explicit query parameter: asking what was valid on a specific date.

v0.20 adds as_of to search_knowledge (§15.13):

{
  "name": "search_knowledge",
  "arguments": {
    "query": "data subject access rights transfers",
    "as_of": "2022-01-15"
  }
}

The bridge returns only units whose validity window contained that date. The Schrems II guidance unit, valid from 2021-06-18 until 2023-09-14, would appear in this result. The updated SCC guidance unit, valid from 2023-09-14, would not.

The as_of Time Machine: a single query parameter moves the bridge's perspective to any point in history. Pass as_of: "2022-01-15" and the Schrems II guidance (valid 2021–2023) appears; the 2023 SCC guidance does not yet exist. The bridge returns what was true on that date — not what is true today.

This is not a niche feature. The scenarios that require it are the same ones where getting the answer wrong has the most consequence.

Compliance audits. "What was our understanding of the transfer requirements in Q1 2022?" requires knowing what knowledge was valid then — not what is valid today.

Incident post-mortems. "What did the security policy say when this architecture decision was made?" requires querying the policy as it stood at that point.

Regulatory gap analysis. "What changed between the old regulation and the current one?" can be answered by comparing two as_of queries.

Without as_of, these questions require manual research through change logs, document history, and version control. With as_of, they are a single tool call.

Beyond "Active Today": the three use cases that require as_of — compliance audits ("what was our understanding in Q1 2022?"), incident post-mortems ("what did the security policy say when this decision was made?"), and regulatory gap analysis ("what changed between the old regulation and the current one?"). All three are a single tool call with as_of; without it, they are manual research projects.

include_all_temporal: audit mode

There is a complementary parameter: include_all_temporal: true returns all units regardless of their validity window, with their full temporal metadata intact. This is the knowledge base audit view.

{
  "name": "search_knowledge",
  "arguments": {
    "query": "data transfer mechanisms",
    "include_all_temporal": true
  }
}

You see the complete temporal history: what was superseded, what is currently active, what is pending in the future. Useful when you are maintaining the knowledge base itself and want to understand the full lineage, not just the currently-valid answer.

The two parameters are mutually exclusive. Requesting both — a specific as_of date and include_all_temporal: true — returns a temporal_query_conflict error. They answer different questions; no single query asks both simultaneously.

Choosing the right parameter: Default (no parameter) filters to valid today — right for production agents answering current-state questions. Point-in-Time (as_of) returns what was valid on a specific date — right for audits and post-mortems. Audit (include_all_temporal) bypasses filtering entirely — right for knowledge base maintenance. Requesting both returns temporal_query_conflict.

Filter order

The full filter order in search_knowledge is now (§15.12):

  1. Score against query terms
  2. Apply not_for filter (strict exclusion and soft demotion)
  3. Apply temporal filter (as_of evaluation, or today by default)
  4. Take top-N

Temporal filtering happens after scoring and after not_for. A unit that scores well but is outside its validity window is excluded entirely — not demoted, excluded. Serving an expired unit at any score would defeat the purpose.

The four-stage filter pipeline: (1) semantic scoring against query terms, (2) not_for exclusion and soft demotion, (3) temporal evaluation — expired units are removed entirely, not demoted, (4) top-N cut. The pipeline is deterministic and ordered: a unit cannot survive stage 3 on the strength of its stage 1 score.


The filter in practice

The bridge defaults are designed to be safe and unsurprising.

If you do nothing — no as_of parameter, no include_all_temporal — the bridge filters to units valid today. A unit with valid_until: "2020-12-31" is silently excluded. You do not see it unless you ask for it.

If you pass as_of: "2024-06-01", you get units valid on that date. Units with valid_from after that date are not yet active. Units with valid_until before that date have already expired.

If you pass include_all_temporal: true, temporal filtering is bypassed entirely. You get everything that matches the query terms, with each unit's temporal block exposed so you can see the validity windows yourself.

The default — filter to today — is the right behaviour for production agents answering questions about the current state of the world. as_of is for queries that require a specific historical perspective. include_all_temporal is for knowledge base maintenance and auditing.


Bridge version alignment

v0.20 also changed how we version the bridges. Previously, the bridges (TypeScript, Java, Python) used their own incremental version numbers: 0.14.0, 0.15.0. As of v0.20.0, bridge versions align with the spec version they implement.

The mismatch was causing confusion: "does kcp-mcp v0.15 implement spec v0.19?" Now the answer is in the package version. Bridge v0.20.0 implements KCP spec v0.20. The Python bridge also reached full Tier 1 parity this release: get_unit and get_command_syntax are now implemented across all three bridges.

Bridge version alignment: before v0.20, each bridge tracked its own version number, creating a matrix of "which bridge implements which spec?" questions. After: bridge versions snap to the spec version. Bridge 0.20.0 implements spec 0.20. The answer is in the package version.


The arc

KCP v0.18 answered: is this content what was signed? Content hashes bound the signed manifest to the files it points to.

KCP v0.19 answers: when is this content valid? The temporal block declares the validity window in the real world.

KCP v0.20 answers: what was valid on a specific date? The as_of parameter makes that a query, not a research project.

Each release closes a gap that the previous one made visible. v0.18 made it worthwhile to declare that content is authentic. v0.19 makes it worthwhile to declare that content is currently authentic. v0.20 makes that declaration queryable at any point in time.

The next visible gap: temporal validity at the manifest level in the federation layer — declaring when an entire federated sub-manifest is relevant, not just individual units. That work is in RFC-0021, and it will be the subject of the next post when it is ready.

The Arc of Authenticity: v0.18 answered "is this content what was signed?" (authenticity). v0.19 answered "when is this content valid?" (validity). v0.20 answered "what was valid on a specific date?" (perspective). RFC-0021 targets the federation layer — temporal validity for entire sub-manifests, not just individual units.


Try it

The spec, CLI, and all three bridges (TypeScript, Java, Python) are open source:

Add a temporal block to any unit with a known expiry date. Run kcp validate — the validator will warn if valid_until is in the past without a superseded_by. Query with as_of set to a past date and verify that superseded units correctly disappear. Then query with include_all_temporal: true and see the full lineage.

For compliance-heavy knowledge bases in particular: if you are storing regulatory guidance, GDPR articles, or policy documents, temporal validity is not optional metadata. It is the difference between an agent that knows what was true and when, and one that confidently answers questions about superseded rules.


Co-authored with Claude. The protocol design, temporal model, and RFC are mine; Claude helped draft and sharpen the narrative.