Skip to content

Commit d0f4297

Browse files
authored
Merge pull request #52 from Hafeok/claude/prd-tick-rate-validity-re7ofs
docs(g-track): tick rate as ground, and the act-indexed validity horizon
2 parents 0f3a539 + 8b63aa6 commit d0f4297

1 file changed

Lines changed: 93 additions & 5 deletions

File tree

docs/g-track/prd-ground-as-ontology.md

Lines changed: 93 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ behaviour.**
113113

114114
| Component | Source | Note |
115115
|---|---|---|
116-
| Ontology vocabulary | product-cli's existing RDF vocabulary | **Assumption A1:** the axis registry uses this vocabulary, extended with act-relevant properties, not a separate one. Open ruling 25; the PRD takes the extend position. If ruled otherwise, §7.2 changes and nothing else does. |
116+
| Ontology vocabulary | product-cli's existing RDF vocabulary | **Assumption A1:** the axis registry uses this vocabulary, extended with act-relevant properties, not a separate one. Open ruling 25; the PRD takes the extend position. If ruled otherwise, §7.2 changes and nothing else does. The registry vocabulary gains tick rate as a property of entities and axes (§5.6), carried as a Reading like any other ground. It is not a caching parameter and is not editable outside the proposal path. |
117117
| **Registry authority** | a dedicated **registry repository** per instance, generated from the versioned **registry template** *(re-typed at G-1 Gate 3: `docs/g-track/registry-template/`; the first instance generates at G0 entry with its parameters — owner, name, base IRI, ratifier — supplied then)* | canonical store of ratified triples; per-triple files in Turtle (the per-claim file pattern — the pattern transfers, the format was never the load-bearing part; Gate 3 ruling); ratification by PR merge; supersession, never rewriting; CI runs SHACL on every change; the founding-decision slot is the instance's first ratified content. **Not** inside any codebase and **not** inside the framework canon repos — it is the owning organisation's ground, not the framework's. |
118118
| **Registry serving** | SPARQL endpoint rebuilt from the registry repo at each merge | read-only; **pinned to a ref** — every Reading served carries the ref, giving Q11 as-of semantics with no extra machinery. Local mode: product-cli embeds a triple store (Oxigraph — Rust, SPARQL 1.1, fits the workspace natively) over a pinned clone; works offline. Central mode: the same store or Fuseki/GraphDB in Azure Container Apps behind Entra ID — the O-track stack arriving early. Store choice is swappable because **the endpoint is a projection of the repo, not the source** (Q30's diagnostic, applied to our own database). **Inference runs at projection build**: entailments are recomputable, so they never enter the authority repo — each endpoint rebuild derives the inferred graph for that ref, every entailed triple `prov:wasDerivedFrom` the asserted triples plus the rule id, assurance = the rule. The repo stays pure assertion; inference inherits as-of discipline for free. *(Verified at G-1, 2026-08-17:)* Oxigraph holds — 0.5.9 (2026-06-18), active ~monthly cadence, SPARQL 1.1 Query/Update/Federated, full named-graph support; honest risk signal is a bus-factor of ~1. ~~**The workspace's `oxigraph = "0.4"` pin is a dead line**~~ (last 0.4.11, 2025-05-21; 0.5.0 shipped 2025-09-13 as a breaking line) — **discharged 2026-08-17, before G0 entry**: the upgrade landed as PR #46 (`product-core` now pins `oxigraph = "0.5"`, with `pf::query`, `pf::seed` and `pf::sparql_rules` migrated to the 0.5 API). The stale-ground finding in our own ground is closed; what remains is the standing obligation not to let the pin drift again. *(Ruled at G-1 Gate 2:)* inference mechanism is **CONSTRUCT-to-fixpoint on Oxigraph as primary** — it reuses the `pf::sparql_rules` shape already in the codebase and adds no dependency — with the Rust `reasonable` crate (0.4.4, BSD-3) as the named fallback; recorded as track decision `g-dec-02` with a `revisit_if`: fixpoint wall-time exceeding an acceptable projection-build budget at G0 scale flips to the fallback. The no-benchmark hedge is discharged by measuring at G0, not by choosing conservatively now. |
119119
| Validation | SHACL shapes, SPARQL | as-is; shapes gain the act properties in §4.2 |
@@ -544,7 +544,7 @@ extractor is built re-runnable from day one because it costs nothing then and ev
544544
### 5.5.1 Review is batched by the pair, with the derivation as the sub-key
545545

546546
*(Added 2026-08-20.)* Per-triple review does not scale: 561 files was already at the edge of it and
547-
the full solution is ≈16.7× that corpus. Wholesale merge is manufactured ground by §5.6, so the
547+
the full solution is ≈16.7× that corpus. Wholesale merge is manufactured ground by §5.7, so the
548548
answer is neither of those — it is **review by kind**.
549549

550550
A **batch** is the set of assertions sharing a derivation signature:
@@ -577,8 +577,8 @@ merged reads as acceptance of a reviewed set, and what was actually ratified was
577577
superset.
578578

579579
This is `term:presumed-discharge` at the ratifier's seat rather than in an instrument: a record
580-
state where a pass is indistinguishable from a skip. It is also the rubber-stamp failure §5.6 exists
581-
to forbid, arriving through the one path §5.6 did not describe — not wholesale merge *instead of*
580+
state where a pass is indistinguishable from a skip. It is also the rubber-stamp failure §5.7 exists
581+
to forbid, arriving through the one path §5.7 did not describe — not wholesale merge *instead of*
582582
review, but wholesale merge *after* review, which looks like the discharged form and is not.
583583

584584
Binding from here:
@@ -598,7 +598,72 @@ rewrite, and it is a ratifier's act with a ruling behind it, never an instrument
598598
record landed; they are now two queries (§5.2). The argument for recording derivation was made in
599599
the abstract; this is the first real defect in the ratified set it has repaid.
600600

601-
### 5.6 Guard
601+
### 5.6 Tick rate and the validity horizon
602+
603+
**The claim.** An entity's expected rate of change is ground about that entity. Held in the registry
604+
rather than in a caching layer, it makes read-versus-cache a computed decision rather than a
605+
judgement — and it makes the same question answerable at design time, before any system runs.
606+
607+
**The computation, and where each input lives.**
608+
609+
| Input | Held by | Note |
610+
|---|---|---|
611+
| `as_of` | the Reading (§4.1) | already carried |
612+
| tick rate | the **entity or axis** in the registry | this addendum's addition |
613+
| tolerance τ | the **act** | not the entity — see below |
614+
| **validity horizon** | computed, per act | the age at which expected drift exceeds what this act tolerates |
615+
616+
**Tolerance sits on the act, and that is the load-bearing part.** The same entity read for two
617+
different acts may have two different acceptable stalenesses: a display may tolerate an hour where a
618+
settlement tolerates none. So the horizon is a function of the *pair*, never of the entity alone.
619+
This is v5.7.0's act-indexed discharge (`DDD-frame-16`) applied to freshness — a reading is fresh
620+
*for an act*, not fresh in general.
621+
622+
**The tick rate is itself a Reading, and this is what lets the system be designed before it runs.**
623+
The same four-tuple as any other ground:
624+
625+
| Stage | value | provenance | assurance |
626+
|---|---|---|---|
627+
| Design time | the expected rate | `declared` *(not in §4.1's value set — open item 21)* | low — an assumption by whoever declared it |
628+
| Running system | the observed rate | `observed` | higher — measured over retained acts |
629+
630+
So design-time assumptions are not a separate mode of the system. They are the same field at lower
631+
assurance, superseded when observation arrives. **The gap between declared and observed is
632+
measurable** — the proxy-fidelity check applied to the drift model itself: a rate was declared, a
633+
rate was observed, and the divergence is a finding rather than an embarrassment.
634+
635+
**Observation is nearly free where the arrangement is event-sourced.** The event stream is the tick
636+
record; rate per entity is a query over retained acts. This is the same argument retention always
637+
makes — retained events answer questions nobody planned for, and this is one of them.
638+
639+
**It makes co-drift computable.** Clustering by co-drift and ordering by rate has been a heuristic
640+
for domain modelling. With rates on entities it is a measurement, and cache boundaries — what
641+
invalidates together — fall out of the registry rather than out of intuition.
642+
643+
### 5.6.1 Three constraints on the model
644+
645+
**Rate is a distribution, not a scalar.** Many entities change in bursts, and a mean rate silently
646+
under-serves exactly the moments that matter. The cheap form is a scalar; the honest form carries
647+
burstiness. Whichever is taken, the vocabulary must record **which form a given rate is**, so a
648+
consumer never reads a mean as though it were a bound.
649+
650+
**It is ground, not a tuning knob.** The value of a tick rate is that it carries provenance and
651+
assurance and is consumed by caching decisions. The moment it becomes a parameter someone edits for
652+
performance, it stops being ground and every horizon computed from it stands on a number nobody
653+
filed. Rate changes are proposals, reviewed like any other assertion.
654+
655+
**Bootstrap honestly.** Declared rates are proxies over open predicates — the same structure as the
656+
proxy-predicate gap. That is legitimate and better than nothing, on two conditions: they are marked
657+
`declared`, and they are **superseded** rather than left in place once observation is available. A
658+
declared rate still governing after a year of observation is the proxy-predicate gap in miniature.
659+
660+
### 5.6.2 What this replaces
661+
662+
Data as a separate specification domain. There is no data layer holding freshness policy: there are
663+
entities carrying rates, acts carrying tolerances, and horizons computed from the pair. Caching
664+
configuration becomes a *projection* of that, regenerated when either side moves — not a source.
665+
666+
### 5.7 Guard
602667

603668
The reviewer's acceptance path is **per triple, never per run**. A single review of a large codebase
604669
that accepts wholesale is a rubber stamp, and rubber-stamped ground is manufactured ground.
@@ -998,6 +1063,29 @@ Emil's rulings needed, in order of how much they change the design:
9981063
Propose the retry shape — how many attempts, how spaced, how the attempts are written into the
9991064
run evidence so the verdict's assurance is visible — and rule on it before any row's gating
10001065
depends on an operation that has ever timed out.
1066+
18. **Tick-rate vocabulary shape** — scalar versus distribution, the unit, and how burstiness is
1067+
expressed where it is carried. Emil rules before implementation; the model's honesty depends on
1068+
a consumer never mistaking a mean for a bound.
1069+
19. **Where tolerance is declared.** The horizon needs τ per act, and the act-site extractors do not
1070+
currently carry one. Whether tolerance is declared per act kind, per decision, or per act at
1071+
declaration time is a design ruling, and it gates any implementation of §5.6.
1072+
20. **Observation source.** Rate observation assumes retained acts to count. For arrangements that
1073+
are event-sourced this is a query; for those that are not, it is unavailable and rates stay
1074+
`declared` indefinitely. State that limit rather than implying observation is always reachable.
1075+
21. **Design-time provenance***(opened 2026-08-20 by the tick-rate addendum; a defect in the
1076+
addendum itself, of the same class as the G-1 mis-pin — a value used from conversation as
1077+
though it were filed.)* §5.6's stage table gives declared rates `provenance = declared`, which
1078+
is **not in §4.1's value set** (`controlled` / `observed` / `inferred` / `institutional`).
1079+
Three candidate resolutions: (a) **a fifth value** — a filing consequence, since the set is
1080+
track-owned under `g-dec-01` pending the Q25/Q27/Q30 wave, and this would be a second consumer
1081+
waiting on that wave alongside the existing one; (b) **`inferred`**, with the assumption
1082+
recorded in `assurance` rather than `provenance`; (c) **a distinct axis** for whether a value
1083+
was assumed or measured, on the grounds that assumed-versus-measured may be orthogonal to where
1084+
a value came from — "assumed at design time" and "inferred by rule" are different things, and
1085+
forcing them into one column is the conflation §5.2's second axis was added to correct. Emil
1086+
rules, with the wave's context; gates any implementation of §5.6. **Deliberately not folded
1087+
into item 18**: 18 is the rate's shape and stays inside the G-track, 21 is whether the
1088+
provenance value set gains a member and carries a canon consequence.
10011089

10021090
---
10031091

0 commit comments

Comments
 (0)