Skip to content

fix(spec): liveness stale-evidence check was ~100% false positives — and was burying a real one - #3857

Merged
os-zhuang merged 1 commit into
mainfrom
claude/action-undoable-experimental-j8g24i
Jul 28, 2026
Merged

fix(spec): liveness stale-evidence check was ~100% false positives — and was burying a real one#3857
os-zhuang merged 1 commit into
mainfrom
claude/action-undoable-experimental-j8g24i

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Follow-up to #3845. The verifiedAt clock landed there is only useful if the gate's warnings are trusted — and one of them was pure noise.

The bug

The stale-evidence check was one line:

const file = String(led.evidence).split(':')[0];
if (/\//.test(file) && !existsSync(join(repoRoot, file)))  flag

It assumes every evidence string is exactly path/to/file.ts:123. Almost none are:

Real evidence string What split(':')[0] produced
packages/spec/src/stack.zod.ts (mergeActionsIntoObjects stable-sorts each group) the whole string, incl. the prose
objectui: packages/app-shell/src/views/RecordDetailView.tsx:573 objectui — the attribution eaten as a filename
packages/objectql/…/rule-validator.ts (UPDATE strip); packages/metadata-protocol/… first path + prose; second path never checked

48 of 227 entries flagged, and every one was a parse artefact or a deliberate cross-repo pointer. I checked all 48 by hand: 11 were objectui paths that already said objectui:, 27 were packages/services/service-ai/… (the closed cloud runtime — packages/services/ here ships every sibling except it), and the rest were paths with a parenthetical after them that do exist.

The real one it was burying

object.enable.clone cited packages/objectql/src/protocol.ts:2259. That file is gone — cloneData()'s enable.clone gate now lives at packages/metadata-protocol/src/protocol.ts:2938 (verified; behaviour still guarded by protocol-clone-real-engine.test.ts). The claim never stopped being true, the pointer rotted, and the check that exists to catch exactly this could not be heard over 47 false ones. Pointer repaired and dated.

That's the same failure this ledger keeps documenting, one level up: a signal nobody trusts is a signal nobody reads.

The fix

New evidence.mts extracts repo-rooted paths properly and honours the cross-repo attribution entries were already writing in prose:

  • a realm marker (objectui, cloud, ee) attributes the paths that follow it, up to the next clause boundary (; or )) — so one string can cite both repos, and framework switches back explicitly;
  • packages/services/service-ai/… is always foreign, wherever it appears;
  • non-repo-rooted tokens (app-shell/MetadataProvider.tsx, action-button/-group) read as prose — neither resolved nor reported.

Both parenthesised ((objectui packages/core/…)) and colon (objectui: packages/…) marker forms are handled; the corpus uses both.

evidence paths: 156 resolved against this checkout, 36 attributed to another repo (objectui / cloud — not resolvable here).
✓ all governed-type properties are classified; all bound high-risk proofs resolve.

Down from 48 warnings that said nothing, to zero — with the two counts printed every run, so the parser silently degrading to "extracts nothing" is visible rather than vacuously green. A unit test asserts that too (expect(local).toBeGreaterThan(100)).

The ledger README's advice to write objectui paths "as prose to avoid false stale-flags" was a workaround for this bug; it now documents the realm-prefix convention.

Verification

  • Parser validated against all 227 real evidence strings before wiring it in — 158 local paths resolved, 38 attributed foreign, exactly 1 genuinely missing (the enable.clone rot). All 11 objectui paths confirmed present in objectstack-ai/objectui@732b1bf; packages/services/service-ai/ confirmed absent from this repo.
  • pnpm --filter @objectstack/spec check:liveness — exits 0, zero stale evidence.
  • pnpm --filter @objectstack/spec test — 262 files, 6796 tests passed (17 new in evidence.test.ts, incl. a contract test over the shipped ledgers).

Not in this PR

The gate's other permanently non-empty warning — 13 unregistered dogfood @proof: tags — is left alone deliberately. Registering them is not clerical: several (attachments-permission-matrix, flow-runas-schedule) look like they should be bound to a ledger entry rather than merely registered as unbound, and deciding that per-proof is its own reviewable change.


Generated by Claude Code

…ce warnings were ~100% false

The check assumed every `evidence` string is exactly `path/to/file.ts:123`:

    const file = String(led.evidence).split(':')[0];
    if (/\//.test(file) && !existsSync(join(repoRoot, file))) -> flag

Almost none are. Entries carry prose ("packages/spec/src/stack.zod.ts
(mergeActionsIntoObjects stable-sorts each group)"), several pointers, or a
cross-repo attribution ("objectui: packages/app-shell/..."). Taking everything
before the first colon turns the prose into the filename, which never exists —
so 48 of 227 entries were flagged and every one was a parse artefact or a
deliberate cross-repo pointer.

A permanently non-empty, ~100%-false warning is a warning nobody reads, and that
is how the one real rot in the list went unnoticed: object.enable.clone cited
packages/objectql/src/protocol.ts:2259, but cloneData()'s enable.clone gate had
moved to packages/metadata-protocol/src/protocol.ts:2938. The claim stayed true,
the pointer rotted, and the check meant to catch exactly this couldn't be heard.
Pointer repaired and dated.

New evidence.mts extracts repo-rooted paths and honours the realm attribution
entries already write in prose: `objectui`/`cloud`/`ee` attribute the paths that
follow, up to the next clause boundary, so one string can cite both repos;
`framework` switches back. packages/services/service-ai/ is always foreign (the
closed cloud runtime — the one sibling absent from this repo's
packages/services/). Non-repo-rooted tokens read as prose.

The gate now resolves 156 paths, attributes 36 cross-repo, reports ZERO stale,
and prints both counts each run so the parser degrading to "extracts nothing" is
visible rather than silently green — a unit test asserts that too.

README: the advice to write objectui paths "as prose to avoid false stale-flags"
was a workaround for this bug; documents the realm-prefix convention instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ajwvrmd1hDC9RBofYBhGuR
@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 28, 2026 11:55am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/m labels Jul 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

104 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via packages/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang marked this pull request as ready for review July 28, 2026 12:52
@os-zhuang
os-zhuang merged commit d77d1b7 into main Jul 28, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/action-undoable-experimental-j8g24i branch July 28, 2026 12:52
os-zhuang added a commit that referenced this pull request Jul 28, 2026
…just skills/ (#3882)

check:skill-examples compiles the TypeScript in prose against the built spec, so
an example that stops compiling fails CI instead of quietly teaching code that no
longer works. It does its job — it caught the broken defineTool example in #3876.
But it only ever walked skills/:

    skills/          9 files with ts blocks,   9 compiled
    content/docs/  124 files with ts blocks,   0 compiled

The identical break in a docs page ships. Docs examples are copied verbatim by
humans and AI exactly like skill examples — the same shape as the stale-evidence
and orphan-proof warnings fixed in #3857 / #3868: a gate covering a fraction of
the surface it appears to cover reads as coverage.

The walker is now a SOURCE_ROOTS list, and this lands the first batch: 164 docs
blocks across 63 pages, taking the gate from 32 to 196 checked examples.
content/docs/references/ is excluded — build-docs.ts regenerates it from the
schemas, so it cannot drift independently.

THE MARKER IS NOW PER-FORMAT. MDX has no HTML comments: `<!-- os:check -->` in a
.mdx does not degrade, it fails the fumadocs build outright ("Unexpected
character `!`… to create a comment in MDX, use `{/* text */}`"). Found by building
the docs site after the first attempt broke 60+ pages; nothing in the type-check
gate would have said so, because block extraction is regex-level and never parses
MDX. skills/**/*.md keeps `<!-- os:check -->`; content/docs/**/*.mdx uses
`{/* os:check */}`. Both spellings are recognised for ORPHAN detection, so a
wrong-format marker fails loudly instead of silently checking nothing.

The batch was measured, not guessed: marking all 780 docs blocks and compiling
showed which are self-contained. One subtlety — a block that "passes" inside a
780-file program can be leaning on globals declared by OTHER blocks (a file with
no import/export is a global script), so the first pass's 564 "passing" blocks
collapsed to 164 once compiled as the real, smaller set. Converged by recompiling
and dropping newly-failing blocks until green.

The remaining blocks are mostly fragments, which the opt-in design anticipates.
Whether any are genuine rot is now answerable for the first time.
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/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants