Skip to content

lifecycle.mdx names two of the three health-check entry names — the exception path's "health-check" is missing #11823

Closed

Description

@os-steve

Found while implementing #11811 (the sibling TypeScript example in the same
section). Out of that card's scope — its dispatch ruled the PluginHealthReport
block byte-frozen, since PR #11812 had just corrected it field by field, so this
is filed rather than fixed.

What was measured

content/docs/protocol/kernel/lifecycle.mdx:745, in the paragraph below the
PluginHealthReport JSON block, makes an enumeration that presents itself as
exhaustive:

Each entry is named after the plugin's configured checkMethod, or
"plugin-loaded" when a plugin configures none.

packages/core/src/health-monitor.ts pushes a check entry from three places,
not two:

# source entry name when
1 :119 / :121 config.checkMethod the custom check returned
2 :125 'plugin-loaded' no checkMethod configured, or it does not resolve to a function
3 :173 'health-check' the check threw — including a timeout overrun, which raceCheckTimeout surfaces as a throw

The doc covers 1 and 2. The third is the literal 'health-check' at :173,
inside the catch (error) at :166:

checks.push({
  name: 'health-check',
  status: 'failed',
  message: message
});

It is not a variant spelling of either documented name — it is a fixed string
that matches neither the configured checkMethod nor 'plugin-loaded'.

Why it is worth a card

The exception path is the one a reader is most likely to be staring at a report
from: it is what a timeout and any throwing check produce. Its entry is also the
only one accompanied by the report's status: 'failed' (set at :167 with no
failureThreshold accumulation, unlike returned failures, which pass through
degraded first at :150-164). So the reader debugging the most alarming report
the monitor emits finds an entry name the page says cannot occur.

The paragraph is accurate about everything else it claims, and this is one
missing row rather than a wrong one — hence finding rather than a defect
report.

Not asserting more than was checked

Only the checks[].name enumeration in that one paragraph was compared against
health-monitor.ts. The rest of the paragraph (the passed | failed | warning
vocabulary, metrics.uptime in milliseconds, the absent version field, the
unset schema fields) was verified correct in PR #11812 and is not in question
here. No other page was examined.

Activity

  1. added
    documentationImprovements or additions to documentation
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    domain:devxDomain lane — seat card indexed by label:pm:seat
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    on Aug 24, 2026
  2. self-assigned this
    on Aug 24, 2026
  3. os-steve commented on Aug 24, 2026

    @os-steve
    CollaboratorAuthor

    Claim: domain:devx PM seat, session session_015ahemw8RcTgqtxrj15PEZx, branch claude/issue-11823-healthcheck-entry-name.

    pm:blocked cleared — the blocker is gone. This card was held because the fix edits content/docs/protocol/kernel/lifecycle.mdx, which an open PR owned. That PR (#11822, card #11811) landed as f7eff23ed. Verified rather than assumed: the union of files claimed by all 22 open PR branches plus the in-flight dev branch contains 0 hits for protocol/kernel/lifecycle.mdx, and that zero is not vacuous — the same scan over the same union returns 1 for check-platform-checklist, 1 for check-type-check-coverage, 2 for guard-shared-stash and 1 for check-engine-double-contract, all known-open surfaces. The page is unowned.

    Model tier: opus. Derived live, not recalled — node scripts/pm/dispatch-gates.mjs --tier content/docs/protocol/kernel/lifecycle.mdx at a1c804bc9 reports no path-derived mandate, so this is judgment. Above the floor deliberately: this page has now been corrected three times in one shift (#11789, #11812, #11822), and on #11812 the PM's own stated reason for a route was wrong about which side produces a PluginHealthReport — the dev caught it from health-monitor.ts:109-120. A one-row docs fix on a page with that history is not a sonnet card.

    Zone 1 — rulings, not open questions:

    Zone 2 — assumptions to falsify before writing: the card asserts three push sites in packages/core/src/health-monitor.ts (:119/:121, :125, :173). Re-derive that on current origin/main — do not trust the line numbers, three PRs have moved this area today. In particular check whether 'health-check' is genuinely the only literal on the catch path and whether raceCheckTimeout really surfaces a timeout as a throw rather than a returned failure; the card's claim that the exception entry skips failureThreshold accumulation (:167 vs :150-164) is the part most worth confirming, because it is what makes the row worth documenting rather than a footnote.

    Zone 3 — suggested route (yours to overrule with evidence): one added row to the enumeration, naming "health-check" and the condition that produces it (a throwing check, timeout included), and saying that it carries status: 'failed' directly. If the sentence reads better as a short table than as a third clause, take the table.

    ⚠️ If the re-derivation shows the card is wrong — that the paragraph is already exhaustive, or that a fourth entry name exists — stop and report rather than widening. A fourth name is a new card, not a bigger PR.


    Generated by Claude Code

  4. os-steve commented on Aug 24, 2026

    @os-steve
    CollaboratorAuthor
    {
      "issue": 11823,
      "status": "done",
      "branch": "claude/issue-11823-healthcheck-entry-name",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/11851",
      "premise_still_valid": true,
      "summary": "The card's premise held on re-derivation at a1c804bc9: health-monitor.ts has exactly 4 checks.push sites yielding 3 distinct names, and the doc's enumeration named only 2. Replaced the two-clause sentence at lifecycle.mdx:744-746 with a three-row table naming the configured checkMethod, 'plugin-loaded' and the fixed 'health-check' produced by a throwing check (timeout included). Took the table option Zone 3 allowed: the file already uses | Declaration | Semantics | tables for this exact name-to-meaning shape, and the reader's task is reverse lookup. The PluginHealthReport JSON block is byte-identical to a1c804bc9, proven by diffing the extracted block rather than asserted. One card claim needed correcting and the doc reflects the corrected version: the exception path DOES increment failureCounters (:169) — what it skips is the threshold COMPARISON, setting 'failed' directly at :170 where the returned-failure path reads config.failureThreshold at :150-151 and lands on 'degraded' at :163. Also corrected the 'plugin-loaded' condition in the same clause as a bounded in-place fix (see open_questions).",
      "tests": "Gate union re-run AFTER the final commit, at HEAD bd5de01b6 — 17/17 green. Set derived by `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` with no hand-written path list (it self-derived 1 path vs merge base a1c804bc9). Gates' own verdict lines, never 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 heading'; check:role-word 'OK, no new occurrences of the reserved word.' (225 files, 2 roots); check:nul-bytes 'OK (scanned 6598 text file(s) ... no raw ASCII control bytes)'; check:liveness/empty-state/strictness-ledger/variant-docs green; doc-formula, doc-security-posture, docs-audit-scope, docs-redirects, published-readme-links, react-page-adapter-contract, doc-frontmatter, section-landing-index, cross-package-test-inputs green. IMPORTANT: doc-formula-expressions and doc-security-posture FIRST exited 1 with 'PREREQUISITE NOT MET — the workspace package is not built' — that measured nothing; both are green above only after `turbo run build --filter=@objectstack/lint --filter=@objectstack/formula`. All heavy runs went through scripts/pm/os-verify-lock.sh (max hold 23s, waited 0s). MDX compiles with the new table: `pnpm exec fumadocs-mdx` in apps/docs rc=0. NO ablation was run — this is a prose/table docs edit with no gate whose failability needed proving. Declared narrowing on repo-wide `pnpm lint`: eslint governs ZERO files in this diff, measured from eslint's own config resolution — `eslint --no-inline-config --format json <file>` returns 1 result entry, errorCount 0, whose only message is 'File ignored because no matching configuration was supplied'; the diff is exactly 1 file and touches no eslint config, so no untouched file's verdict can move. On-disk edit evidence: anchor-grep before/after, 'Each entry is named after the' 1→0 and '\"health-check\"' 0→1, not the editor's exit code. Control-byte scan clean (0) with a valid positive control (a seeded \\x01 file read 1 — my first control used a tab, which is deliberately outside that class and proved nothing).",
      "open_questions": [
        {
          "question": "I made one bounded in-place correction beyond the card's headline: the doc said \"plugin-loaded\" applies \"when a plugin configures none\", but health-monitor.ts:109 guards on `config.checkMethod && typeof (plugin as any)[config.checkMethod] === 'function'` — so the default entry is ALSO pushed when a checkMethod IS configured but does not resolve to a function. Flagging it because Zone 1 fenced the card to the checks[].name enumeration and this sits inside that fence, but it was not the card's stated defect.",
          "options": [
            "A — keep it (shipped): same defect class as the card (a reader sees an entry name the page says cannot occur), same clause being rewritten, no new file in the claim surface, mechanically pinned by :109, and the card's own measured table states the same fuller condition",
            "B — revert to the literal 'configures none' wording and file the condition as a separate card"
          ],
          "recommendation": "A, because all four bounded-in-place conditions hold and the PR body names the fix with its evidence rather than slipping it in. Splitting it would leave a knowingly-wrong condition in the exact sentence this PR rewrites."
        }
      ],
      "out_of_scope_findings": [
        "filed as #11852 (unassigned, unlabeled for triage): `autoRestart` is read at health-monitor.ts:159 and nowhere else, so a check that THROWS or times out is marked 'failed' and never restarted, while one that merely returns false is restarted after failureThreshold rounds — the more severe failure mode cannot trigger recovery. Same block: the catch path increments failureCounters but does not reset successCounters, where the returned-failure path does (:148). Searched twice for duplicates (concept + file path) — only closed #4875/#6329. Not asserted to be a defect: nothing declares which behaviour is intended, and no doc contradicts it."
      ]
    }

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

documentationImprovements or additions to documentationdomain:devxDomain lane — seat card indexed by label:pm:seatpm:dispatchedDispatched to a dev agent by /pm-dispatch

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions