Skip to content

Commit 736cfb1

Browse files
Elon Muskclaude
andauthored
docs(adr): retire ADR-0076 D11's contention metric and the reopen threshold it fed (#10154)
* docs(adr): retire ADR-0076 D11's contention metric and the reopen threshold it fed (#10076) D11's 2026-08-16 revision installed a replacement metric — the per-PM-round count of dispatchable cards blocked from running concurrently because both land in `packages/rest/src/rest-server.ts` — and gated reopening option A on it sustaining above ~1/round. Nothing ever computed that counter. Batch independence is computed per round to schedule work and is not persisted; no script, gate, workflow or agent instruction file in this repo produces, stores or reads the series. Its reporting anchor (#5949) has been closed/completed since 2026-08-16, so every reading owed after that date had nowhere live to land, and the readings that were filed went into a closed card. So the ADR claimed a reopen threshold that nothing fed. Per the maintainer ruling of 2026-08-20 (option B), the metric and the threshold are retired together, in writing: - section ③'s metric and threshold are marked retired IN PLACE rather than deleted, since the 2026-08-16 decision was argued from them; - a new 2026-08-20 revision records what is retired, why, and — explicitly — what it does not do: it does not revive the raw `wc -l` series, does not make B permanent, and does not touch ①/④ or the 2026-08-15 ruling; - the status line and the D11 status bullet stop pointing readers at a live metric. Reopening A is now an ordinary new decision under Prime Directive #13, argued from a measurement taken for that purpose, rather than an automatic trigger nothing was ever going to pull. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019bmVFqoQPq63zhKrxdYG1r * docs(adr): mark the withdrawn D11 threshold in house style, not GFM strikethrough GFM matches strikethrough on a run of ONE or two tildes, and the withdrawn sentence contains a bare `~` in "~1/round" — so the marking depended on delimiter-run lengths rather than on being unambiguous. The rest of this record marks withdrawn text with a ⛔ prose marker and leaves the sentence standing (the "central route generator" framing above does exactly that), so this now does the same. No change to what is retired. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019bmVFqoQPq63zhKrxdYG1r --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 8d74a57 commit 736cfb1

1 file changed

Lines changed: 27 additions & 4 deletions

File tree

docs/adr/0076-objectql-core-tiering.md

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# ADR-0076: objectql is the data engine — relocate metadata management (protocol) out of it; enforce the boundary; defer the engine repo-split
22

3-
**Status**: Accepted — D1/D2/D4/D5/D6/D8/D9 implemented; D10/D12 partially landed; **D11 implemented on the dispatcher half and *decided* on the rest-server half — delineated, not split** (maintainer ruling 2026-08-15; revision recorded under D11 by #9024); D3 unbuilt; D7 deliberately deferred (proposed 2026-06-28, rev. 9 · calibrated 2026-08-05 by #5063 · D11 revised 2026-08-16). This ADR has been binding for some time — `AGENTS.md`, `packages/runtime/src/domains/*` (D11's own output), `packages/objectql`'s core boundary and ADR-0121 all cite it as a settled decision — while the header still read `Proposed`; per Prime Directive #13 an accepted decision binds until superseded, so the header is corrected here and, following #4919's calibration of ADR-0078, **per decision** rather than by a single flip. Verified against source (not against the rev.9 narrative), 2026-08-05:
3+
**Status**: Accepted — D1/D2/D4/D5/D6/D8/D9 implemented; D10/D12 partially landed; **D11 implemented on the dispatcher half and *decided* on the rest-server half — delineated, not split** (maintainer ruling 2026-08-15; revision recorded under D11 by #9024); D3 unbuilt; D7 deliberately deferred (proposed 2026-06-28, rev. 9 · calibrated 2026-08-05 by #5063 · D11 revised 2026-08-16 · D11's replacement metric and reopen threshold retired 2026-08-20 by #10076). This ADR has been binding for some time — `AGENTS.md`, `packages/runtime/src/domains/*` (D11's own output), `packages/objectql`'s core boundary and ADR-0121 all cite it as a settled decision — while the header still read `Proposed`; per Prime Directive #13 an accepted decision binds until superseded, so the header is corrected here and, following #4919's calibration of ADR-0078, **per decision** rather than by a single flip. Verified against source (not against the rev.9 narrative), 2026-08-05:
44

55
- **D1 — implemented, with OQ#2 unexecuted.** `packages/metadata-protocol/` owns `protocol.ts`, `sys-metadata-repository.ts` and `metadata-diagnostics.ts`; `packages/objectql` depends on it one-way (`packages/objectql/package.json`, `src/plugin.ts` imports `assembleMetadataProtocol`). **But `SysMetadataEngine` never moved to `@objectstack/metadata-core`** — it is still declared in `packages/metadata-protocol/src/sys-metadata-repository.ts` and `packages/metadata-core/src/` has no occurrence of the name. OQ#2 is therefore *unexecuted*, not resolved; the D1 bullet below states the intent, not the state.
66
- **D2 — implemented.** `packages/objectql/src/core-boundary.ratchet.test.ts` walks the `core.ts` import closure and forbids `@objectstack/metadata-protocol` plus the local `plugin` / `kernel-factory` modules.
77
- **D3 — unbuilt. The one decision here with no implementation at all.** No `profile: 'trusted' | 'internal'` exists in `packages/objectql/src`, `packages/core/src` or `packages/spec/src/kernel`; nothing derives required capabilities from object declarations; boot is **not** fail-closed — `packages/core/src/kernel.ts` only *warns* ("System started with degraded capabilities. Missing core services: …"). The `requiredCapabilities` / `provides` keys that do exist belong to the unrelated plugin-conformance schema `packages/spec/src/kernel/plugin-capability.zod.ts`, not to this decision. (Zero-hit counter-checked against live neighbours in the same search surface: `registerMiddleware`, `isServiceServeable`, `__serviceInfo` all match.)
88
- **D4/D5/D6/D8 — ratifications, all still true.** `@objectstack/formula` remains an `objectql` dependency used by `engine.ts` / `hook-wrappers.ts` / `validation/rule-validator.ts` (D4). `@objectstack/spec` carries only `zod` as a runtime dependency, and `examples/embed-objectql/` boots `@objectstack/objectql/core` against an ordinary `ObjectSchema.create` object — the same definition shape a full backend ships (D5, and Phasing P4). `engine.ts`'s `registerMiddleware` is the attachment point `plugin-security` and `plugin-sharing` actually use (D6), and the engine still hard-codes no governance (D8) — the D2 ratchet is what keeps it that way.
99
- **D9 — implemented, past step-1, at its own rev.7 end-state.** `packages/spec/src/api/protocol.zod.ts` declares ten per-domain contracts (`DataProtocol`, `MetadataProtocol`, `AnalyticsProtocol`, `AutomationProtocol`, `PackageProtocol`, `ViewProtocol`, `PermissionProtocol`, `RealtimeProtocol`, `NotificationProtocol`, `I18nProtocol`), and **no `ObjectStackProtocol` union type is declared or exported anywhere in the repo** — the transitional composed alias is *dissolved*, not merely deprecated. Consumers are narrowing accordingly: `packages/rest/src/rest-server.ts` types its dependency as `RestProtocol = DataProtocol & MetadataProtocol`.
1010
- **D10 — partially implemented.** Landed: the feed domain and then the analytics fallback left the facade (analytics now has exactly one implementation, in `service-analytics`), and OQ#7 resolved to keep the package name. Outstanding: the central facade class this decision rules out **still exists**`packages/metadata-protocol/src/protocol.ts` declares `class ObjectStackProtocolImplementation implements DataProtocol, MetadataProtocol, PackageProtocol` — so the `DataProtocol` implementation has not moved to the engine-adjacent/transport layer and **OQ#6 remains open**.
11-
- **D11 — dispatcher half implemented; rest-server half *decided*: delineated, not split.** The dispatcher decomposed exactly as OQ#9's verdict describes: every domain body lives in `packages/runtime/src/domains/*`, routing goes through `packages/runtime/src/domain-handler-registry.ts`'s `DomainHandlerRegistry`, and `http-dispatcher.ts` is down to ~2.1k LOC from the ~3.8k recorded below (2,087 lines measured at `dc280d326`; this bullet recorded ~1.7k at the 2026-08-05 calibration). Multi-adapter is validated (OQ#10) by `packages/qa/http-conformance`. The second half — `packages/rest/src/rest-server.ts` — is **not** decomposed and, by the maintainer ruling of 2026-08-15, **will not be**: it is already delineated by domain inside the file, and the decomposition programme does not open. See the **2026-08-16 revision at the end of D11** for the structural reading, the price that decision knowingly accepts, and the metric that replaces the retired line count. *(Until 2026-08-16 this bullet read "the rest-server half is untouched" and compared two raw line counts — i.e. it recorded an open obligation, and measured it with the proxy the ruling retired. Both are corrected: the obligation is a closed decision, and the raw-LOC comparison is gone rather than refreshed.)*
11+
- **D11 — dispatcher half implemented; rest-server half *decided*: delineated, not split.** The dispatcher decomposed exactly as OQ#9's verdict describes: every domain body lives in `packages/runtime/src/domains/*`, routing goes through `packages/runtime/src/domain-handler-registry.ts`'s `DomainHandlerRegistry`, and `http-dispatcher.ts` is down to ~2.1k LOC from the ~3.8k recorded below (2,087 lines measured at `dc280d326`; this bullet recorded ~1.7k at the 2026-08-05 calibration). Multi-adapter is validated (OQ#10) by `packages/qa/http-conformance`. The second half — `packages/rest/src/rest-server.ts` — is **not** decomposed and, by the maintainer ruling of 2026-08-15, **will not be**: it is already delineated by domain inside the file, and the decomposition programme does not open. See the **2026-08-16 revision at the end of D11** for the structural reading and the price that decision knowingly accepts. The metric that replaced the retired line count is **itself retired** by the **2026-08-20 revision**, together with the `~1/round` reopen threshold it fed — nothing ever computed either, so there is now no standing metric on this file and no automatic reopen trigger. *(Until 2026-08-16 this bullet read "the rest-server half is untouched" and compared two raw line counts — i.e. it recorded an open obligation, and measured it with the proxy the ruling retired. Both are corrected: the obligation is a closed decision, and the raw-LOC comparison is gone rather than refreshed.)*
1212
- **D12 — framework side implemented; console side cross-repo and unverified from here.** `packages/spec/src/api/discovery.zod.ts` carries the `unavailable` / `degraded` / `stub` statuses plus `ServiceSelfInfoSchema` + `readServiceSelfInfo`; `svcAvailable` in `packages/runtime/src/http-dispatcher.ts` derives `status`/`handlerReady` from that marker for every slot (no hardcodes left); `packages/runtime/src/service-serveable.ts`'s `isServiceServeable` is the single predicate the dispatcher-owned domains, the route-mount gate and discovery all read. The consumer half ("trust only `handlerReady: true`") lives in cloud/objectui and cannot be confirmed from this repo.
1313
- **D7 — deferred as designed, and now instrumented.** `packages/objectql` is still in the monorepo. New since rev.9: `scripts/check-engine-split-ratio.mjs` and `.github/workflows/engine-split-metric.yml` compute and report the cross-package commit ratio that gates the split; it is deliberately **report-only** because the threshold itself is still OQ#5.
1414

@@ -153,13 +153,15 @@ D11 names two central transport layers. The dispatcher half was decomposed (OQ#9
153153

154154
**② The price this decision knowingly accepts — B is defensible, and it is not free.** Merge conflicts, affected-subset CI, and the PM lane's batch-independence rule all operate on **file paths**. A delineation expressed as methods inside one file is invisible to all three. Two agents editing `registerApprovalsEndpoints` and `registerReportsEndpoints` — blocks that share no line — still collide, still serialise, and still dirty the same CI subset. **Adopting B accepts that permanently, until someone reverses this decision.** It is written into the ADR so the price arrives with the decision instead of being discovered by whoever pays it.
155155

156-
**③ The replacement metric, and the reopen condition.**
156+
**③ The replacement metric, and the reopen condition. ⛔ Both RETIRED 2026-08-20 — see the revision at the end of D11.** The block is kept, not deleted, because the 2026-08-16 decision was argued from it and a later reader needs to see what was withdrawn rather than find a gap.
157157

158158
The line-count series this question was argued over is **retired, not paused**. It used raw `wc -l` on a file whose code and prose are close to the same size: measured with a comment-aware classifier at `dc280d326`, **5,665 code lines against 4,690 comment/blank (45.3%)** — and those comments are dense explanatory documentation (why a refusal is shaped as it is, why route order is load-bearing), not commented-out code. A raw line delta over such a file cannot separate "a new endpoint landed inline" from "someone documented a subtle refusal properly", so it cannot answer the question it was being used to answer.
159159

160160
> **Replacement metric — per PM round: the number of times two dispatchable cards could not run concurrently because both land in `packages/rest/src/rest-server.ts`.**
161+
>
162+
> **RETIRED 2026-08-20 (#10076).** Nothing ever computed this counter. Do not resume the series, and do not file readings into #5949 — that card has been closed since 2026-08-16.
161163
162-
It measures the cost this file demonstrably creates rather than its volume, and the dispatch loop already computes batch independence, so it needs no `git log` window — which is what repairs the 2026-08-08 hold's "measured merge-conflict tax" arm after shallow clones defeated two separate seats trying to measure it. **Reopen A if this counter sustains above ~1/round.**
164+
It measures the cost this file demonstrably creates rather than its volume, and the dispatch loop already computes batch independence, so it needs no `git log` window — which is what repairs the 2026-08-08 hold's "measured merge-conflict tax" arm after shallow clones defeated two separate seats trying to measure it. **Reopen A if this counter sustains above ~1/round.****WITHDRAWN 2026-08-20 (#10076)** — the threshold rested on a counter no mechanism ever produced, so the condition could never fire. The sentence is left standing rather than struck because the 2026-08-16 decision was argued with it; reopening A is now an ordinary new decision under Prime Directive #13. See the revision below.
163165

164166
- **First datapoint (2026-08-15 rounds, recorded with the ruling): 0.** Re-verified rather than restated: #8087, the one queued card that would land in this file, is still open and still blocked for a reason unrelated to it, so the collision never materialised.
165167
- **Re-derived for the 2026-08-16 round: 0.** None of the 24 open `pm:dispatched` cards lands in `rest-server.ts`.
@@ -169,6 +171,27 @@ It measures the cost this file demonstrably creates rather than its volume, and
169171

170172
**Which ruling figures moved.** The 18 methods and the domain axis are unchanged. The whole-file composition the ruling quoted — 6,008 code against 5,400 comment/blank, 11,408 total — was taken at `5047cb8ab` and is **superseded**: #8850's prologue extraction (landed as `8664a2c99`) moved 1,296 lines of ADR-0112 error/fault classification out to `error-response.ts` after that measurement. The same classifier used above reproduces `6,008 / 5,400` exactly at `5047cb8ab`, so the two readings differ because the file changed, not because the method did. `registerMetadataEndpointsInner` reads 2,324 lines on that tree against the ruling's ~2,328 (a method-boundary convention, not a disagreement) and 2,545 today, the `/meta` work since having landed inside it. Recorded as measured; ⛔ deliberately not averaged, rounded together, or reconciled in either direction.
171173

174+
#### Revision 2026-08-20 (#10076) — the per-round contention metric is RETIRED, and the reopen threshold goes with it [ruled]
175+
176+
**Provenance.** Maintainer, decision-inbox session 2026-08-20, verbatim 「其他接受你的建议。」, accepting option **B** at [#10076](https://github.com/objectstack-ai/objectstack/issues/10076) — retire the contention-sequence reporting commitment, paired with this note. ⛔ Deliberately **not** a silent retirement: the condition of the ruling was that this record stop claiming a reopen threshold nothing feeds, in writing, with the reason attached.
177+
178+
**What is retired.** ③'s replacement metric — the per-PM-round count of dispatchable cards that could not run concurrently because both land in `packages/rest/src/rest-server.ts`**and the `~1/round` reopen condition that rested on it**. Neither is measured, reported nor consumed from this date. ⛔ Do not resume the series, and ⛔ do not file readings into #5949: posting measurements into a closed card is the precise shape this retirement exists to end.
179+
180+
**Why — three findings, all re-measured at head for this revision.**
181+
182+
1. **No mechanism ever computed it.** ③ reasoned that "the dispatch loop already computes batch independence, so it needs no `git log` window". Batch independence is computed per round to *schedule* work; it is not persisted, and the counter was never derived from it. Measured 2026-08-20: no script, gate, workflow or agent-instruction file in this repo produces, stores or reads the series. It lived in prose in one seat post, handed from seat to seat — which is a carrier, not a mechanism.
183+
2. **Its anchor has been closed since 2026-08-16.** #5949 — the finding this whole revision line descends from — is `closed`/`completed` as of 2026-08-16T06:47:27Z. Every reading owed after that date had nowhere live to land, and the readings that were filed went into a closed card that no longer appears in any open-issue view.
184+
3. **The delivery record shows the failure was structural, not personal.** Over rounds 15–28: round 23 filed a reading into the already-closed anchor and flagged it; rounds 24–25 produced no entry at all; rounds 26–28 were filed only retroactively, during the audit that produced this note. Three seats, thirteen rounds, one gap. ⭐ **An obligation with no live named reader and no trigger cannot fail loudly, so it reads as satisfied right up until somebody audits it** — the same defect class this repo has measured in `Restart-when:` hand-offs that delegate work to an unrelated card.
185+
186+
**The threshold could not have carried a decision even if the series had been maintained.** Every reading from round 18 onward sat at or below `~1/round`, and the only two rounds ever re-checked (19 and 20) were **both** found mis-counted. So "it was once sustained above the threshold" is itself unverified. That weakens the case *for* reopening A and the case *against* it equally, and is recorded here rather than resolved in either direction.
187+
188+
**⛔ What this revision does NOT do.**
189+
190+
- It does **not** revive the raw `wc -l` line-count series. That was retired by the 2026-08-16 revision above on its own merits — a file that is ~45% explanatory prose cannot be judged by a line delta — and retiring its *replacement* does not hand the question back to it. There is now **no standing metric on this file**, deliberately.
191+
- It does **not** make B permanent or unchallengeable. ②'s price stands unchanged: a delineation expressed as methods inside one file is invisible to merge conflicts, affected-subset CI and batch independence, and that is still the reason someone may want A. What changes is the route — reopening A is an ordinary new decision under Prime Directive #13, argued from a measurement taken **for that purpose**, rather than an automatic trigger nothing was ever going to pull.
192+
- It does **not** touch ①'s structural reading, ④'s `registerMetadataEndpointsInner` carve-out (#8851), or the 2026-08-15 ruling recorded above that B rests on.
193+
194+
172195
### D12 — Honest capabilities: discovery must distinguish real services from stubs/fallbacks [new — kernel review]
173196

174197
**Root cause of agents being misled.** Several plugins register stub / dev / fallback services under canonical names, and the discovery builder reports *any* present service as fully real: `runtime/http-dispatcher.ts`'s `svcAvailable` hardcodes `{ enabled: true, status: 'available', handlerReady: true }` for every registered service — it **ignores stub markers** (its own comment even says "handlerReady:false … may be served by a stub", but the code never computes it). So `discovery.services.*` claims capabilities that are only stubbed, and consumers (AI agents, the console) trust them. A dev AI stub advertised this way has already confused an agent.

0 commit comments

Comments
 (0)