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

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.

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.](../../../../../assets/images/kcp-019-020-02-stale-knowledge.webp)
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.

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.

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.

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.

Filter order¶
The full filter order in search_knowledge is now (§15.12):
- Score against query terms
- Apply
not_forfilter (strict exclusion and soft demotion) - Apply temporal filter (
as_ofevaluation, or today by default) - 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 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.

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.

Try it¶
The spec, CLI, and all three bridges (TypeScript, Java, Python) are open source:
- Spec + CLI (
kcp): github.com/Cantara/knowledge-context-protocol
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.
Series: Knowledge Context Protocol
← Signing the Map, Not the Territory: KCP v0.18 Adds Unit Content Integrity and Origin Evidence · Part 32 of 32