Skip to content

feat(cli): liveness author-warning lint — close the spec-liveness loop#1966

Merged
os-zhuang merged 1 commit into
mainfrom
feat/liveness-author-warnings
Jun 16, 2026
Merged

feat(cli): liveness author-warning lint — close the spec-liveness loop#1966
os-zhuang merged 1 commit into
mainfrom
feat/liveness-author-warnings

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Why

The spec-liveness ledgers (packages/spec/liveness/*.json) classify every authorable property live / experimental / dead with evidence, and the CI gate enforces that classification is complete. But that knowledge lived only in CI — it never reached the person (very often an AI generating templates) writing the metadata. So an author could keep setting enable.feeds: true or field.columnName forever, expecting them to do something, while they silently do nothing.

This closes the loop: the ledger now warns the author at build time.

What

New compile lint packages/cli/src/utils/lint-liveness-properties.ts, wired as an advisory stage in the compile pipeline (alongside the existing flow anti-pattern lint — never fails the build). It reads the shipped ledgers and warns when an authored object/field sets a misleading property, with a corrective hint:

⚠ object 'crm_account': sets `enable.feeds` but this object property has no runtime effect (liveness: dead).
    Comments/collaboration live on the dedicated sys_comment object (plugin-audit), not this flag.
    rule: liveness-dead-property
⚠ object 'crm_account' · field 'code': sets `columnName` but this field property has no runtime effect (liveness: dead).
    The physical column always equals the field key — a custom `columnName` is silently ignored by the driver.
    rule: liveness-dead-property

Signal over noise — opt-in per ledger entry

A property being merely dead is not enough to warn (plenty of dead props are benign display/doc metadata). Warnings opt in via a new annotation:

Field Effect
"authorWarn": true warn when authored (any experimental entry also warns by default — a declared-but-unenforced guarantee)
"authorHint": "…" the corrective one-liner (falls back to note)

Two rules keep it false-positive-free: (1) only genuinely misleading dead props are marked; (2) booleans warn only when set true and only default(false) flags are marked — so schema defaults like enable.trash/enable.searchable never trip it. (Discovered the hard way during verification: enable.searchable defaults true, so it's deliberately left unmarked — see its _authorWarnSkipped.) Documented in liveness/README.md.

The lint is ledger-driven: coverage grows by annotating more entries, not by touching lint code. v1 covers object (incl. enable.*) and field — the highest-signal surfaces.

Seeded annotations

  • object: enable.{files,feeds,activities,trackHistory} (dead default-false capability flags) + versioning/partitioning/softDelete/search/recordName/defaultDetailForm/keyPrefix (aspirational/duplicate blocks).
  • field: columnName, referenceFilters, index, maxRating, vectorConfig, fileAttachmentConfig (misleading dead — imply behavior that isn't wired).
  • @objectstack/spec now ships liveness/ in package files so the installed CLI can read the ledgers.

Verification

  • Real objectstack compile end-to-end: injected enable.feeds / versioning / columnName into a showcase object → all three warned with hints; default-on enable.trash correctly silent. The lint also caught two genuine pre-existing dead-prop usages already in app-showcase (f_rating.maxRating, f_vector.vectorConfig) — flagged as a follow-up.
  • 9 new unit tests (lint-liveness-properties.test.ts) run against the real shipped ledgers, so they double as a contract test (removing an authorWarn annotation fails the matching assertion).
  • @objectstack/cli 451 tests green; liveness gate green (new fields tolerated); @objectstack/spec ledger JSON validates.

This is the productization of the whole spec-liveness effort: measurement (ledger) + completeness gate (CI) + drift re-audit (monthly cron) + now author-facing prevention — directly serving the "templates are AI-authored; avoid AI mistakes" north star.

🤖 Generated with Claude Code

The liveness ledgers classify every authorable property live/experimental/
dead with evidence, and the CI gate enforces classification completeness —
but that knowledge never reached the author (very often an AI) writing the
metadata. This feeds it back at build time.

New `compile` lint (lint-liveness-properties.ts) reads the ledgers and warns
when an authored object/field sets a misleading property — e.g.
`object.enable.feeds` (no feed runtime), `object.versioning` (no engine),
`field.columnName` (driver ignores it), `field.maxRating`/`vectorConfig`
(renderer reads a different key) — with a corrective hint. Advisory only;
never fails the build, like the existing flow anti-pattern lint.

Signal-over-noise by design: opt-in per ledger entry via a new
`authorWarn`/`authorHint` annotation (experimental entries warn by default).
Booleans warn only when truthy and only `default(false)` flags are marked,
so schema defaults (enable.trash/searchable) never trip it. Coverage grows
by annotating more entries, not by touching lint code.

- spec: ledger entries gain optional authorWarn/authorHint; `liveness/` now
  shipped in package `files` so the CLI can read it. Seeded the misleading
  object capability flags + aspirational blocks and dead field props.
- README documents the authorWarn convention + the default-false caveat.

Verified end-to-end via a real `objectstack compile` (warnings fired for
enable.feeds/versioning/columnName; the lint also caught two genuine
pre-existing dead-prop usages in app-showcase). CLI 451 + 9 new lint tests
green; liveness gate green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file tests tooling labels Jun 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/cli, @objectstack/spec.

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

  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/cloud-artifact-api.mdx (via packages/cli, packages/spec)
  • content/docs/concepts/cluster-semantics.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/implementation-status.mdx (via @objectstack/cli, @objectstack/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/concepts/packages.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/concepts/setup-app.mdx (via @objectstack/spec)
  • content/docs/concepts/skills.mdx (via @objectstack/spec)
  • content/docs/concepts/webhook-delivery.mdx (via @objectstack/spec)
  • content/docs/getting-started/architecture.mdx (via @objectstack/spec)
  • content/docs/getting-started/cli.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/getting-started/core-concepts.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/guides/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/guides/ai-capabilities.mdx (via @objectstack/spec)
  • content/docs/guides/airtable-dashboard-analysis.mdx (via @objectstack/spec)
  • content/docs/guides/analytics-datasets.mdx (via @objectstack/spec)
  • content/docs/guides/api-reference.mdx (via @objectstack/spec)
  • content/docs/guides/authentication.mdx (via @objectstack/cli)
  • content/docs/guides/business-logic.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/error-catalog.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/field-type-gallery.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/field-validation-rules.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/protocol-diagram.mdx (via packages/spec)
  • content/docs/guides/cheatsheets/query-cheat-sheet.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/quick-reference.mdx (via @objectstack/spec)
  • content/docs/guides/client-sdk.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/guides/common-patterns.mdx (via @objectstack/spec)
  • content/docs/guides/contracts/auth-service.mdx (via packages/spec)
  • content/docs/guides/contracts/cache-service.mdx (via packages/spec)
  • content/docs/guides/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/guides/contracts/index.mdx (via @objectstack/spec)
  • content/docs/guides/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/guides/contracts/storage-service.mdx (via packages/spec)
  • content/docs/guides/data-modeling.mdx (via @objectstack/spec)
  • content/docs/guides/deployment-vercel.mdx (via @objectstack/spec)
  • content/docs/guides/driver-configuration.mdx (via @objectstack/spec)
  • content/docs/guides/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/guides/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/guides/formula.mdx (via @objectstack/spec)
  • content/docs/guides/hook-bodies.mdx (via packages/cli, packages/spec)
  • content/docs/guides/kernel-services.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/dashboard.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/field.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/flow.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/index.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/object.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/validation.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/workflow.mdx (via @objectstack/spec)
  • content/docs/guides/packages.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/guides/plugin-development.mdx (via @objectstack/spec)
  • content/docs/guides/plugins.mdx (via @objectstack/spec)
  • content/docs/guides/project-scoping.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/guides/public-forms.mdx (via @objectstack/spec)
  • content/docs/guides/runtime-services/data-service.mdx (via packages/cli)
  • content/docs/guides/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/guides/runtime-services/index.mdx (via packages/cli, packages/spec)
  • content/docs/guides/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/guides/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/guides/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/guides/security.mdx (via @objectstack/spec)
  • content/docs/guides/seed-data.mdx (via @objectstack/spec)
  • content/docs/guides/skills.mdx (via packages/cli, @objectstack/spec)
  • content/docs/guides/standards.mdx (via @objectstack/spec)
  • content/docs/guides/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/plugin-spec.mdx (via @objectstack/cli, @objectstack/spec)
  • content/docs/protocol/objectos/realtime-protocol.mdx (via @objectstack/cli)
  • content/docs/protocol/objectos/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via packages/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 packages/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v9.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.

@vercel

vercel Bot commented Jun 16, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jun 16, 2026 3:29pm

Request Review

@os-zhuang
os-zhuang merged commit 90108e0 into main Jun 16, 2026
14 of 16 checks passed
@os-zhuang
os-zhuang deleted the feat/liveness-author-warnings branch June 16, 2026 15:22
os-zhuang pushed a commit that referenced this pull request Jul 18, 2026
…uthor-lint)

The author-side liveness lint (lintLivenessProperties, #1966) auto-warns on
every `experimental` prop. enable.trash / enable.mru default to `true`, and
the lint cannot distinguish an authored `true` from the schema default — so
tagging them experimental warned on the default value of every object,
tripping the "does NOT warn on a default-on flag left alone (enable.trash)"
contract test in Test Core.

Revert those two to `dead` (their audit classification) with a ledger note
explaining why; drop the [EXPERIMENTAL] marker from their spec .describe().
Their #1893 disposition (prune-or-build) stays tracked in the sub-issue.
All other #1893 experimental markers are on ungoverned types the lint never
loads, so they are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LddW4NaQBdf5FTEnBPpnUJ
os-zhuang pushed a commit that referenced this pull request Jul 18, 2026
…uthor-lint)

The author-side liveness lint (lintLivenessProperties, #1966) auto-warns on
every `experimental` prop. enable.trash / enable.mru default to `true`, and
the lint cannot distinguish an authored `true` from the schema default — so
tagging them experimental warned on the default value of every object,
tripping the "does NOT warn on a default-on flag left alone (enable.trash)"
contract test in Test Core.

Revert those two to `dead` (their audit classification) with a ledger note
explaining why; drop the [EXPERIMENTAL] marker from their spec .describe().
Their #1893 disposition (prune-or-build) stays tracked in the sub-issue.
All other #1893 experimental markers are on ungoverned types the lint never
loads, so they are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LddW4NaQBdf5FTEnBPpnUJ
os-zhuang added a commit that referenced this pull request Jul 18, 2026
…props experimental + declare renderer-read props (#1878) (#3223)

* chore(spec): mark aspirational props experimental + declare renderer-read props (#1878)

Metadata-liveness audit follow-through (umbrella #1878). Resolves the
unambiguous framework-spec portions of the open P2 sub-issues.

#1893 (aspirational config — prune or mark experimental): add
[EXPERIMENTAL — not enforced] markers to properties that parse but have no
runtime consumer, so authors are not misled (ADR-0049):
- object enable.trash / enable.mru (ledger dead -> experimental)
- job retryPolicy / timeout
- theme spacing / breakpoints / rtl / density / touchTarget
- translation messageFormat:'icu' / cache
- webhook authentication (non-HMAC bearer/basic/api-key)
- PortalSchema (entire — not registered, no route/renderer)

#1891 / #1894 (naming drift + inverse drift — app cluster): declare the
props the objectui renderers already read so a strict Schema.parse() holds:
- app branding accentColor (ConsoleLayout)
- nav item badgeVariant (NavigationRenderer)
- nav item `separator` type (AppContent nav divider)
- agent knowledge.sources as the canonical key, topics kept as deprecated alias

Regenerated reference docs (content/docs/references) to match. All spec
gates green: check:liveness, check:docs, check:spec-changes; full spec
suite 6763 passing.

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

* fix(spec): keep object enable.trash/mru dead, not experimental (CI: author-lint)

The author-side liveness lint (lintLivenessProperties, #1966) auto-warns on
every `experimental` prop. enable.trash / enable.mru default to `true`, and
the lint cannot distinguish an authored `true` from the schema default — so
tagging them experimental warned on the default value of every object,
tripping the "does NOT warn on a default-on flag left alone (enable.trash)"
contract test in Test Core.

Revert those two to `dead` (their audit classification) with a ledger note
explaining why; drop the [EXPERIMENTAL] marker from their spec .describe().
Their #1893 disposition (prune-or-build) stays tracked in the sub-issue.
All other #1893 experimental markers are on ungoverned types the lint never
loads, so they are unaffected.

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

* docs(spec): mark ToolSchema as a read-only projection, not an execution entry point (#1892)

#1892 tool disposition (ADR-0049 line): the ledger already documents that
tool metadata is a one-way, write-only projection (no executor loads a
metadata-authored tool; the runtime uses a separate AIToolDefinition in
cloud service-ai). Surface that on the SPEC itself so an author/AI reading
the Zod schema — not just the ledger — knows a hand-authored tool will not
run in the open edition. Non-breaking describe-only change.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant