Skip to content

Latest commit

 

History

History
193 lines (145 loc) · 6.44 KB

File metadata and controls

193 lines (145 loc) · 6.44 KB

Lifecycle, versioning, and health

Context rot is quiet. Nothing fails; agents just start answering from last year's pricing. Everything here exists to make that visible.

Lifecycle state is computed, never stored

future      valid_from is still ahead
current     inside its freshness window
stale       past its ttl, still resolved and reported
expired     past expires
superseded  replaced by a declared successor

State is always evaluated against a timestamp. That is what makes --at work: asking for context as it stood last quarter re-evaluates every window rather than reading a cached flag.

opencontext resolve --role support --at 2026-03-01
opencontext list --at 2026-03-01

The fields

created:    2026-07-01T00:00:00Z
updated:    2026-08-09T00:00:00Z   # freshness is measured from here
valid_from: 2026-08-01T00:00:00Z   # future before this
expires:    null                   # explicit null = never expires
ttl:        180d                   # overrides freshness.default_ttl
durability: long-lived

expires: null is a statement that the object never expires, and is distinguishable from omitting the field (where the repository default applies).

Durations use fixed unit lengths — y = 365d, w = 7d, d = 24h — so "stale after 30d" is the same number of milliseconds in every timezone. Calendar arithmetic would make resolution non-deterministic, which the specification forbids.

Stale context still resolves

freshness:
  default_ttl: 30d
  stale_is_error: false
  exclude_expired: true

A stale object is returned and warned about:

{ "code": "stale", "id": "procedures.refund", "severity": "warning",
  "message": "procedures.refund has not been updated inside its freshness window." }

Dropping it silently would hide exactly the thing the operator needs to see. Expired and not-yet-valid context is excluded, because a policy with an end date has one for a reason.

Set stale_is_error: true to make --strict fail on staleness.

Durability

Value Meaning
ephemeral A single exchange
session One conversation or task
operational Current working state
long-lived Standing policy, products, architecture
permanent Organizational record — mission, decisions

Permanent context should be superseded rather than destroyed.

Review cadence

review:
  interval: 180d
  required_approvers: 2
  next_review: 2027-02-09
  last_review: 2026-08-09

doctor reports overdue reviews. An explicit next_review wins; otherwise the interval is measured from last_review, else updated.

Approval

status: approved
approval:
  required: true
  roles: [legal, executive]
  minimum: 1
  approved_by:
    - role: legal
      at: 2026-08-08T10:00:00Z

States: draft, pending, approved, rejected, retired. Drafts and pending objects are excluded from default resolution.

An object requiring two approvals and carrying one is not approved. The specification defines the metadata and states; it does not require a hosted approval workflow.

Versions and supersession

# context/pricing/enterprise.v2.md
id: pricing.enterprise
version: 2
supersedes: [pricing.enterprise@1]

The previous version stays on disk. That is the point — it preserves the ability to answer "what did we believe in March", which deletion destroys.

Supersession is declared, never inferred from version numbers. See authority for why.

opencontext supersede pricing.enterprise --content "Enterprise plans start at \$2,500/month."
opencontext history pricing.enterprise
opencontext diff pricing.enterprise@1 pricing.enterprise@2
opencontext resolve --role sales --include-historical

Health score

deduction = Σ (weight[code] × occurrences) / max(objects, 1)
score     = clamp(100 − deduction × 100, 0, 100)

A weight is "how much of the repository's health one instance of this problem costs". Normalising by object count is deliberate: one broken canonical conflict in a ten-object repository matters far more than one in a thousand-object repository.

Default weights, highest first — anything that makes the resolver produce a wrong answer costs more than anything that makes it produce an incomplete one:

Weight Codes
1.0 schema-invalid, manifest-invalid, duplicate-id, duplicate-canonical, conflict-ambiguous, secret-detected, path-traversal
0.8 broken-supersession, supersession-cycle, untrusted-canonical, role-cycle
0.6 unknown-scheme, source-unavailable
0.5 broken-reference, unknown-role, unknown-authority
0.4 multiple-active-versions, expired
0.3 conflict-declared, missing-provenance, invalid-permission
0.2 missing-digest, missing-owner
0.15 stale, review-overdue
0.1 orphaned, unapproved, empty-scope, unknown-extension
0.05 not-yet-valid

Override per repository:

health:
  minimum_score: 90
  fail_on: error
  require_owner: true
  weights:
    stale: 0.3          # we care more about freshness than the default
    orphaned: 0.0       # we do not care about orphans yet

A score is comparable only within a repository's own configuration. That is why the formula is published rather than opaque.

In CI

opencontext doctor --strict                    # fail on errors and the minimum score
opencontext doctor --strict --min-score 95     # override the floor
opencontext stale --strict                     # fail on anything stale or expired
OpenContext Health
────────────────────────────────
Mission                 ✓ canonical
Pricing                 ✓ current
Engineering SOPs        ⚠ stale

Orphaned context        7
Conflicting context     2
Expired context         4
Stale context           3
Missing owners          3
Broken sources          1

Context health: 91%

Reconstructing the past

Three mechanisms, and they compose:

  1. --at re-evaluates every window against a past instant.
  2. --include-historical returns superseded versions alongside current ones.
  3. git://<rev>/<path> reads context out of a past commit, offline, with no server.
opencontext resolve --role support --at 2026-03-01 --include-historical

That is how a decision made in March stays auditable in December.