Uh oh!
There was an error while loading. Please reload this page.
docs(protocol): name the exception path's "health-check" check entry - #11851
Conversation
The `checks[].name` enumeration under the `PluginHealthReport` block named two of the three names `PluginHealthMonitor` can push, presenting itself as exhaustive. The third is the fixed `'health-check'` on the `catch` path, which is what a throwing check — and a `timeout` overrun, which `raceCheckTimeout` surfaces as a rejection — produces. A reader debugging that report found an entry name the page said could not occur. Replace the two-clause sentence with a three-row table, and correct the `"plugin-loaded"` condition while rewriting it: the default entry is also pushed when a `checkMethod` IS configured but does not resolve to a function on the plugin, not only when none is configured. The `PluginHealthReport` JSON block above is untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx
os-steve
commented
Aug 24, 2026
ACCEPT — PM review, The open question: ruling A — keep it. And it was never outside the fence.The
And a correction to my own dispatch: I fenced this to "the The card was wrong, you were right, and the doc carries the corrected versionThis is the claim my dispatch singled out as most worth confirming, so I checked it myself in The counter is incremented on the catch path. What is skipped is the threshold comparison. The card's "no ⭐ The judgment I want to name: you also declined to restate the status asymmetry in the table, because the paragraph above the JSON block already states it correctly — "two statements of one fact, free to drift apart." That is the right instinct on a page that has been corrected three times in one shift, and it is the reason a fourth correction is less likely. Verified independently
#11852 is grounded — I checked its load-bearing factIts claim is that a throwing check is never auto-restarted. One narrowing worth repeating for other seatsYou reported that your first control-byte control used a tab, which is deliberately outside that class and therefore proved nothing, and you replaced it with a seeded Flipping to ready. Arming waits — 4 checks are still
Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#11823
What changed
content/docs/protocol/kernel/lifecycle.mdx— thechecks[].nameenumeration below thePluginHealthReportJSON block. It named two of the three namesPluginHealthMonitorcan pushand read as exhaustive, so a reader debugging the most alarming report the monitor emits found an
entry name the page said could not occur.
The two-clause sentence becomes a three-row table. The file already uses
| Declaration | Semantics |tables for exactly this name-to-meaning shape (4 tables, 19 rows), and the reader's task here is
reverse lookup — "I see
health-check, what produced it?" — which a table serves better than athird clause in an already dense paragraph.
The
PluginHealthReportJSON block above is untouched — proven, not asserted: the block atlines 728-740 is byte-identical between
a1c804bc9and this branch (diffof the two extractionsis empty).
Re-derivation on
origin/main(a1c804bc9), not the card's line numbersThe card's cited lines were re-derived rather than trusted, since three PRs moved this area
(#11789, #11812, #11822 — the last landed as
f7eff23ed). All three of its claims hold:maingrep -rn "checks\.push"overpackages/ apps/ examples/ scripts/returns 4 hits, all inhealth-monitor.ts(:119,:121,:125,:172) yielding 3 distinct names. Line numbers unmoved.'health-check'is the only literal on the catch pathcatch (error)at:166contains exactly onechecks.push,name: 'health-check'at:173.raceCheckTimeoutsurfaces a timeout as a throw:340-344rejects withnew Error(message);Promise.raceat:347propagates it out of theawaitat:110, inside thetry. A timeout therefore lands in the samecatch.PluginHealthMonitoris the only producer of aPluginHealthReportin the repo; the other references are the spec schema and its tests.One card claim needed correcting, and the doc reflects the corrected version. The card says the
exception entry carries
status: 'failed'with "nofailureThresholdaccumulation". The counteris still incremented on the catch path (
:169); what is skipped is the threshold comparison —:170setsfaileddirectly, where the returned-failure path readsconfig.failureThresholdat:150-151and lands ondegradedat:163until it is met. The asymmetry the card relies on isreal; its wording overstates it, so the table does not repeat that wording.
The table also does not re-explain the status asymmetry, because the paragraph above the JSON
block already states it correctly ("Consecutive returned failures move the plugin to
degradedfirst... A check that throws — including one that exceeds
timeout— is the separatefailedstatus, applied immediately with no threshold"). Restating it in the table would create two
statements of one fact, free to drift apart.
One bounded in-place correction, named rather than slipped in
While rewriting the clause, the
"plugin-loaded"condition was corrected in the same sentence:the doc said it applies "when a plugin configures none", but
:109guards onconfig.checkMethod && typeof (plugin as any)[config.checkMethod] === 'function'— so the defaultentry is also pushed when a
checkMethodis configured but does not resolve to a function on theplugin. That is the same defect class as the card's (a reader seeing an entry name the page says
cannot occur), in the same clause being rewritten, mechanically pinned by the source, and it adds no
verification surface. The card's own measured table states the same fuller condition.
Verification
Gate union re-run after the final commit, at
bd5de01b6— 17/17 green. The set was derivedwith
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(no hand-written pathlist) against this repo's tree at
a1c804bc9. Each gate's own verdict line, not a bare$?:check:doc-authoring—✓ doc authoring guard: 389 files clean — no bare metadata literals.check:doc-anchors—✅ 278 internal #fragment link(s) across 408 source file(s) all resolve to a real headingcheck:role-word—OK, no new occurrences of the reserved word.(225 files across 2 roots)check:nul-bytes—OK (scanned 6598 text file(s) ... no raw ASCII control bytes)check:liveness,check:empty-state,check:strictness-ledger,check:variant-docs— greencheck:doc-formula-expressions,check:doc-security-posture,check:docs-audit-scope,check:docs-redirects,check:published-readme-links,check:react-page-adapter-contract,check:doc-frontmatter,check:section-landing-index,check:cross-package-test-inputs— greencheck:doc-formula-expressionsandcheck:doc-security-posturefirst exited 1 withPREREQUISITE NOT MET — the workspace package is not built. That measured nothing; both are greenabove after
turbo run build --filter=@objectstack/lint --filter=@objectstack/formula.MDX compiles with the new table:
pnpm exec fumadocs-mdxinapps/docs→ rc 0.Repo-wide
pnpm lintnarrowing, declared and measured — eslint governs zero files in thisdiff. Read from eslint's own config resolution, not assumed:
eslint --no-inline-config --format json content/docs/protocol/kernel/lifecycle.mdxreturns 1 resultentry,
errorCount: 0, whose only message isFile ignored because no matching configuration was supplied— the flat config supplies no configuration for.mdx. The diff is exactly 1 file andtouches no eslint config, so no untouched file's verdict can move.
No changeset
Docs-only, and
dispatch-gates.mjssays as much ("write one unless this card is docs-only"). Thismatches all three sibling PRs on this same file — #11789, #11812 and #11822 each landed as a
single-file
content/docs/**commit with no.changeset/entry.skip-changesetapplied.Generated by Claude Code