docs(protocol): drop the unproduced rate-limit headers and the quota wire fence from error-handling - #17703
Merged
os-bill merged 1 commit intoSep 11, 2026
Conversation
…wire fence error-handling.mdx taught three wire artefacts that no producer emits. X-RateLimit-Limit / -Remaining / -Reset appeared in both 429 header blocks. Measured tree-wide over `git ls-files`, every literal occurrence of those three names is a READ of GitHub's own quota headers in scripts/pm tooling; no ObjectStack producer sets any of them on a response. Positive controls on the same probe: Retry-After 32 literal occurrences, X-Environment-Id 42. The signal that really is on the wire stays: Retry-After, plus the retryAfterSeconds / resetAt details already documented beside it. The QUOTA_EXCEEDED section carried a copyable five-member details bag. QUOTA_EXCEEDED has zero construction sites tree-wide; on the same probe nine declared catalog members light up (RATE_LIMIT_EXCEEDED 14, PERMISSION_DENIED 132, SERVICE_UNAVAILABLE 94) and the three retired members read zero. The section now states that the code is registered but unemitted, so a reader does not write a dead branch. Its heading and HTTP Status line are kept verbatim because check:error-status-conformance parses them; deleting them trips that gate's doc-removed branch against the unpinned baseline. The 503 Retry-After block is deliberately untouched: a 503 producer does set it (plugin-hono-server/src/current-user-endpoints.ts). Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
os-bill
marked this pull request as ready for review
September 11, 2026 17:16
os-bill
enabled auto-merge
September 11, 2026 17:16
os-bill
deleted the
claude/issue-17187-17188-error-handling-unproduced-fences
branch
September 11, 2026 17:38
This was referenced Sep 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #17187
Part of #17188
A folded pair, one file, two independent verdicts. Each card got its own
producer census and its own lit control; they did not land on the same branch of
the page-vs-platform fork.
Verdict 1 — #17187
QUOTA_EXCEEDED: the PAGE is wrong. Corrected here.premise_still_valid: true, re-measured onorigin/main2b6a207542(⛔ notinherited from the card's
fd5cff209f).Probe: construction sites spelled
code:followed by the quoted member, over a comment-stripped,whitespace-flattened corpus enumerated from
git ls-files(8436 tracked, 8429readable text;
content/excluded).QUOTA_EXCEEDEDPERMISSION_DENIEDSERVICE_UNAVAILABLEINTERNAL_ERRORNOT_IMPLEMENTEDVALIDATION_ERRORRESOURCE_NOT_FOUNDDATABASE_ERRORRATE_LIMIT_EXCEEDEDTIMEOUTBATCH_PARTIAL_FAILUREBATCH_COMPLETE_FAILURETRANSACTION_FAILEDNine declared members light, three retired members dark — so the zero is a
reading. The three bare
QUOTA_EXCEEDEDoccurrences tree-wide outsidecontent/are all non-producers: the catalog declaration, a deliberately-wrongdeliberately-wrong waiver case, and the
error-status-unpinned-baseline.jsonrow.Intended, or residue? Measured, per the ledger's own doctrine — "a
producerless row with no card behind it is the 'registered but unemittable'
retirement class" (
error-code-ledger.zod.ts).QUOTA_EXCEEDEDcarries nocard and no registered-ahead-of-its-producer note; the one implemented quota
surface answers
TOO_MANY_REQUESTSon purpose(
service-sms/src/sms-daily-quota.ts); and the declaredTenantQuotaSchema/QuotaEnforcementResultSchemahave no consumer outside spec's own tests andgenerated surface files. ⇒ residue, not a reserved split. So the platform is
not missing an implementation it intends — branch (a).
⛔ The code is not retired here: that is ADR-0049 enforce-or-remove, a
contract change, and per triage not a rider on a prose PR. Carded separately by
the report.
The section keeps its
#### QUOTA_EXCEEDEDheading and its**HTTP Status:**line. That choice is editorial, not gate-forced — I predicted the gate would
force it and the ablation below says otherwise, so the prediction is retracted
rather than shipped. The reason it stays: the code is still a registered member
of the published
StandardErrorCodecatalog anderror-catalog.mdxstill listsit, so dropping this page's entry alone would desynchronise the two pages while
the code's fate under ADR-0049 is undecided. Keeping the entry and marking it
unemitted rules out none of A / B / C.
Verdict 2 — #17188: split. Header block corrected, 503 block NOT touched.
2a.
X-RateLimit-*: the PAGE is wrong. Corrected here.premise_still_valid: trueProbe (corrected — see the discarded run below): every occurrence of the header
name inside a string literal in comment-stripped, non-doc source (6601 files).
X-RateLimit-Limitheaders.get(...)read inscripts/pm/X-RateLimit-Remainingheaders.get(...)reads inscripts/pm/X-RateLimit-Resetheaders.get(...)read inscripts/pm/Retry-AfterX-Environment-IdEvery hit is a read of GitHub's own quota headers in agent tooling. No
ObjectStack producer sets any
X-RateLimit-*header on any response. Thethree lines come out of both 429 blocks;
Retry-After: 45stays, because twoproducers really do set it (
runtime/src/endpoint-policy.ts,runtime/src/security/inbound-rate-limit.ts), and it is the signal the adjacentretryAfterSeconds/resetAtdetails already name.Sibling repos: objectui carries no
X-RateLimit-*set either and its owncontrol lights in that repo (
Content-Type269,Retry-After6), so thatzero counts.
cloudis not checked out here — NOT MEASURED.2b. 503
Retry-After: premise FALSIFIED. Block deliberately left alone.premise_still_valid: falseThe card states no 503 producer sets
Retry-After. One does:reached on the 503 arm of
resolveRequestContext(status defaults to 503, andthe module's own docblock reads "cloud's
KernelWarmingErroris a 503 +Retry-After"). I pulled that file at the card's own anchorfd5cff209fviathe contents API: the site was already there — the card's probe missed it,
the tree did not change under it.
Narrower reading, stated separately: the envelope that 503 returns is
{ error: 'environment_unavailable', … }, so no response carrying theSERVICE_UNAVAILABLEcode setsRetry-After. But the block as written isan HTTP-level block, and a 503 +
Retry-Afteris a real wire shape here. Pertriage — ⛔ do not delete a block documenting something we do send — it is
untouched and handed back.
Discarded runs, reported rather than silently re-run
res/reply/response/ctx.res/headers, soc.header('Retry-After', …)never matched and it returned 0 for the 503 site. Every header number above
comes from the corrected probe; the correction is also what turned up 2b.
unquoted, so the shell expanded the backticks inside the anchor string and the
replacement matched nothing. The guard caught it: the anchor count stayed at 1
and
git hash-objectwas unchanged, so it was reported as a no-op and redonewith a quoted heredoc rather than silently re-run until something landed.
check:partof-closing-keywordwas first invoked with onlyCOMMIT_MESSAGESset and answered
EXIT=2NOT WIRED — "a wiring or usage failure, NOT averdict". Discarded, then re-run correctly (below).
Commit-message guard
Grepped before the commit existed, bare stems counted separately with
grep -oiE | wc -l. All seven card-trailer patterns (the two relationspellings, the reference keyword, the three bare closing stems and a bare card
number) counted 0, with lit controls
Co-Authored-By1 andClaude-Session1. Then two-legged, wired withPR_BODY+PR_COMMITS_FILE:the real commit →
EXIT=0("its 1 commit message(s) carry no card-relationtrailer"), the same body with a commit whose message carries a relation trailer naming
this pair →
EXIT=1.This body was audited with the same grep. One hit survives and is deliberate:
resolveRequestContext, a real identifier in the 2b evidence, bound to no cardnumber and forming no relation.
Clause-②validated withreadClause2Line→{kind: "declared", value: "no"}, with a near-miss control (## Clause-② no)correctly reading
{kind: "near-miss"}.Changeset
skip-changeset— no.changeset/*.md.Rule (
pr-automation.yml, thechangeset-checkjob): the exemption means"this PR declares no release of its own".
Measurement: 81 tracked
package.json, 11 private, 70 published. Theone path in this diff,
content/docs/protocol/kernel/error-handling.mdx, sitsunder none of those 70 package directories, and
files[]entries arepackage-relative, so no published package can ship it; no published package is
rooted at the repo root either. Instrument control:
@objectstack/specdeclaresfiles: [dist, json-schema, liveness, prompts, llms.txt, README.md, src/**/*.zod.ts, CHANGELOG.md, api-surface, spec-changes.json]and really doescontain
src/api/errors.zod.ts, so the probe distinguishes shipped fromunshipped. ⇒ nothing released ⇒ the label applies.
Gates
Derived from the real change set with
scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(1 path; 41 commands). Exit codes capturedbefore any pipe; verdicts quoted from each gate's own line.
EXIT=0:check:error-status-conformance·check:nul-bytes(8429 text files,no raw control bytes) ·
check:doc-anchors·check:doc-authoring·check:docs-single-h1·check:corpus-claim-drift·check:docs-transcript-drift·
check:docs-audit-scope·check:docs-redirects·check:role-word·check:docs-spec-enumerations·check-doc-frontmatter(402 pages) ·check-docs-section-name·check-comment-mask-corpus·@objectstack/lint check:doc-formula-expressions·check:doc-security-posture.check:docs-transcript-driftfirst answeredEXIT=3PREREQUISITE NOT MET —"this is NOT a pass and NOT a finding: nothing was measured" (
@objectstack/lintunbuilt). Read as NOT MEASURED, not as a red; the closure was then built through
scripts/pm/os-verify-lock.sh(VERDICT command-exit 0, held 225s) and the gatere-run to a real
EXIT=0.Is that
error-status-conformancegreen a reading? Partly — measured both ways.Three ablations, each committed-first, each proven to reach disk by a
git hash-objectchange, each restored withgit checkout HEAD -- PATHunderan absolute-path
trapand verified by an emptygit diff HEAD:#### QUOTA_EXCEEDEDheading + status lineEXIT=0QUOTA_EXCEEDEDstatus429→503EXIT=0RATE_LIMIT_EXCEEDEDstatus429→418EXIT=1So the gate is live on this file — the lit control names it by path and line
— but it is structurally blind to
QUOTA_EXCEEDED, because with no derivableproducer nothing pins the claim on either side. That is the card's own "why
nothing catches it", now measured rather than assumed. ⇒ the green is a reading
for the rest of the page and is not a reading about the
QUOTA_EXCEEDEDedit. Nothing mechanical guards that section; review is the only check.
No package is touched by this diff, so there is no dependency closure owed and
no package test suite to run. Remaining families are declared to CI.
验收备注
Out of scope, noted, not carded here:
CONCURRENT_LIMIT_EXCEEDEDreads 0 construction sites on the same probethat lights nine siblings — same shape as
QUOTA_EXCEEDED, one row below itin the same catalog block. Successor: whoever takes the
QUOTA_EXCEEDEDretirement decision, since it is the same ADR-0049 question.
content/docs/api/error-catalog.mdx:413also carries aQUOTA_EXCEEDEDentry, but it is three bold labels of prose with no copyable fence, so it is
not this family and it is a different file from the declared face.
Generated by Claude Code