Skip to content

docs(protocol): drop the unproduced rate-limit headers and the quota wire fence from error-handling - #17703

Merged
os-bill merged 1 commit into
mainfrom
claude/issue-17187-17188-error-handling-unproduced-fences
Sep 11, 2026
Merged

os-bill merged 1 commit into
mainfrom
claude/issue-17187-17188-error-handling-unproduced-fences

Conversation

@os-bill

@os-bill os-bill commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

Part of #17187
Part of #17188

  • Clause-②: no

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 on origin/main 2b6a207542 (⛔ not
inherited 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, 8429
readable text; content/ excluded).

code construction sites
QUOTA_EXCEEDED 0 the reading
PERMISSION_DENIED 132 lit control
SERVICE_UNAVAILABLE 94 lit control
INTERNAL_ERROR 87 lit control
NOT_IMPLEMENTED 83 lit control
VALIDATION_ERROR 48 lit control
RESOURCE_NOT_FOUND 27 lit control
DATABASE_ERROR 23 lit control
RATE_LIMIT_EXCEEDED 14 lit control
TIMEOUT 3 lit control
BATCH_PARTIAL_FAILURE 0 dark control (retired)
BATCH_COMPLETE_FAILURE 0 dark control (retired)
TRANSACTION_FAILED 0 dark control (retired)

Nine declared members light, three retired members dark — so the zero is a
reading. The three bare QUOTA_EXCEEDED occurrences tree-wide outside
content/ are all non-producers: the catalog declaration, a deliberately-wrong
deliberately-wrong waiver case, and the error-status-unpinned-baseline.json row.

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_EXCEEDED carries no
card and no registered-ahead-of-its-producer note; the one implemented quota
surface answers TOO_MANY_REQUESTS on purpose
(service-sms/src/sms-daily-quota.ts); and the declared TenantQuotaSchema /
QuotaEnforcementResultSchema have no consumer outside spec's own tests and
generated 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_EXCEEDED heading 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 StandardErrorCode catalog and error-catalog.mdx still lists
it, 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: true

Probe (corrected — see the discarded run below): every occurrence of the header
name inside a string literal in comment-stripped, non-doc source (6601 files).

header non-test literal occurrences
X-RateLimit-Limit 1 a headers.get(...) read in scripts/pm/
X-RateLimit-Remaining 8 all headers.get(...) reads in scripts/pm/
X-RateLimit-Reset 1 a headers.get(...) read in scripts/pm/
Retry-After 18 lit control
X-Environment-Id 17 lit control

Every hit is a read of GitHub's own quota headers in agent tooling. No
ObjectStack producer sets any X-RateLimit-* header on any response.
The
three lines come out of both 429 blocks; Retry-After: 45 stays, because two
producers really do set it (runtime/src/endpoint-policy.ts,
runtime/src/security/inbound-rate-limit.ts), and it is the signal the adjacent
retryAfterSeconds / resetAt details already name.

Sibling repos: objectui carries no X-RateLimit-* set either and its own
control lights in that repo (Content-Type 269, Retry-After 6), so that
zero counts. cloud is not checked out here — NOT MEASURED.

2b. 503 Retry-After: premise FALSIFIED. Block deliberately left alone. premise_still_valid: false

The card states no 503 producer sets Retry-After. One does:

packages/plugins/plugin-hono-server/src/current-user-endpoints.ts:204
    c.header('Retry-After', String(Math.ceil(err.retryAfterSeconds)));

reached on the 503 arm of resolveRequestContext (status defaults to 503, and
the module's own docblock reads "cloud's KernelWarmingError is a 503 +
Retry-After"
). I pulled that file at the card's own anchor fd5cff209f via
the 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 the
SERVICE_UNAVAILABLE code sets Retry-After. But the block as written is
an HTTP-level block, and a 503 + Retry-After is a real wire shape here. Per
triage — ⛔ do not delete a block documenting something we do send — it is
untouched and handed back.

Discarded runs, reported rather than silently re-run

  1. First header probe — void. It required the receiver to be spelled
    res/reply/response/ctx.res/headers, so c.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.
  2. First ablation attempt — void, no-op. The heredoc feeding the mutation was
    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-object was unchanged, so it was reported as a no-op and redone
    with a quoted heredoc rather than silently re-run until something landed.
  3. check:partof-closing-keyword was first invoked with only COMMIT_MESSAGES
    set and answered EXIT=2 NOT WIRED — "a wiring or usage failure, NOT a
    verdict"
    . 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 relation
spellings, the reference keyword, the three bare closing stems and a bare card
number) counted 0, with lit controls Co-Authored-By 1 and
Claude-Session 1. Then two-legged, wired with PR_BODY + PR_COMMITS_FILE:
the real commit → EXIT=0 ("its 1 commit message(s) carry no card-relation
trailer"
), 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 card
number and forming no relation. Clause-② validated with readClause2Line
{kind: "declared", value: "no"}, with a near-miss control (## Clause-② no)
correctly reading {kind: "near-miss"}.

Changeset

skip-changesetno .changeset/*.md.

Rule (pr-automation.yml, the changeset-check job): the exemption means
"this PR declares no release of its own".
Measurement: 81 tracked package.json, 11 private, 70 published. The
one path in this diff, content/docs/protocol/kernel/error-handling.mdx, sits
under none of those 70 package directories, and files[] entries are
package-relative, so no published package can ship it; no published package is
rooted at the repo root either. Instrument control: @objectstack/spec declares
files: [dist, json-schema, liveness, prompts, llms.txt, README.md, src/**/*.zod.ts, CHANGELOG.md, api-surface, spec-changes.json] and really does
contain src/api/errors.zod.ts, so the probe distinguishes shipped from
unshipped. ⇒ 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 captured
before 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-drift first answered EXIT=3 PREREQUISITE NOT MET —
"this is NOT a pass and NOT a finding: nothing was measured"
(@objectstack/lint
unbuilt). 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 gate
re-run to a real EXIT=0.

Is that error-status-conformance green a reading? Partly — measured both ways.

Three ablations, each committed-first, each proven to reach disk by a
git hash-object change, each restored with git checkout HEAD -- PATH under
an absolute-path trap and verified by an empty git diff HEAD:

ablation on this file gate
delete the #### QUOTA_EXCEEDED heading + status line EXIT=0 green
QUOTA_EXCEEDED status 429503 EXIT=0 green
RATE_LIMIT_EXCEEDED status 429418 EXIT=1 ✅ lit control fires

So 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 derivable
producer 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_EXCEEDED
edit. 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_EXCEEDED reads 0 construction sites on the same probe
    that lights nine siblings — same shape as QUOTA_EXCEEDED, one row below it
    in the same catalog block. Successor: whoever takes the QUOTA_EXCEEDED
    retirement decision, since it is the same ADR-0049 question.
  • content/docs/api/error-catalog.mdx:413 also carries a QUOTA_EXCEEDED
    entry, 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

…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 os-bill added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 11, 2026 — with Claude
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Sep 11, 2026
@os-bill
os-bill marked this pull request as ready for review September 11, 2026 17:16
@os-bill
os-bill enabled auto-merge September 11, 2026 17:16
@os-bill
os-bill added this pull request to the merge queue Sep 11, 2026
Merged via the queue into main with commit 98c904a Sep 11, 2026
37 checks passed
@os-bill
os-bill deleted the claude/issue-17187-17188-error-handling-unproduced-fences branch September 11, 2026 17:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants