docs(spec): the liveness README's author-warning section states the verdict model the lint ships - #21450
Merged
objectstack-fleet[bot] merged 1 commit intoOct 2, 2026
Conversation
…erdict model the lint ships A dead, live-elsewhere or experimental ledger row warns its author on its own; authorWarn only opts a planned row in. Every warning shows the row's authorHint or the verdict's default hint, never the note. Rule 1 is restated for the verdict, rule 2 for any materialized schema default, and the coverage paragraph names the walk's real reach. Two sentences elsewhere in the README that tied a dead row's warning to authorWarn are corrected. Claude-Session: https://claude.ai/code/session_01UtnxvdiN376GF3sgXwAw4d Co-authored-by: Claude <noreply@anthropic.com>
Contributor
📓 Docs Drift Check
What this run could not see
Coarse fallback — 138 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
objectstack-fleet
Bot
deleted the
claude/issue-21135-liveness-readme-author-warnings
branch
October 2, 2026 19:13
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #21135
Clause-②: no
What this changes
packages/spec/liveness/README.md, section "Author warnings — closing the loop", now describes the modelpackages/lint/src/lint-liveness-properties.tsships onmain:dead,live-elsewhereorexperimentalverdict warns on its own.authorWarnopts in aplannedrow, and nothing else.authorHint, or else the verdict's default hint. The ledgernoteis never shown.Two sentences outside the section restated the old opt-in model, so they are fixed too (dispatch Zone 2, item 4). There is also one
@objectstack/specpatchchangeset.⛔ No ledger row changes. ⛔ No lint code change. The paragraph "And one the gate enforces for you: never on a
liverow" was re-checked against the code and is unchanged (see Premise check, item 1).Premise check (dispatch Zone 2, measured at
535d1d25ab)shouldWarn(lint-liveness-properties.ts:173-176) admits a row whose status isdead,live-elsewhereorexperimental, or whoseauthorWarnistrue.checkItem(:407-410) builds the hint asentry.authorHint ?? defaultHint, for every row. Commit327391c3af(PR fix(lint): a liveness finding's fix text never comes from the ledger note, for any row class #21169, landed for [finding]os lintprints a liveness row's internal ledgernote, tracker ids included, as the fix text on opted-in rows (object.externalSharingModelcites #2696 on app-showcase) #21096) removed thenotefallback that opted-in andexperimentalrows still had. So triage's chain ("noteonly for opted-in andexperimentalrows") no longer holds. The README now says thenoteis never shown, on any row.live+authorWarn:describe()throws its integrity sentinel (:283).check:livenessrefuses the combination at every depth (scanAuthorWarnRowsincheck-liveness.mts, from commit9bdc6d3faa, PR fix(spec): a stack whose mapping authorsconnectorSourcevalidates and lints again — theliveledger row carries no author warning (#21127) #21176, landed for [finding]os lint/os validatecrash on any stack whose mapping authorsconnectorSource: the ledger row islivewithauthorWarn: true, and the liveness rule throws its integrity sentinel #21127). The README paragraph fix(spec): a stack whose mapping authorsconnectorSourcevalidates and lints again — theliveledger row carries no author warning (#21127) #21176 added says exactly that, so it stays as it is.src, against the shipped ledgers):name/label→liveness-dead-property, hint "Remove it — it is declared in the spec but not consumed at runtime."rowLevelSecurity[].label/.description→ the same.object.externalSharingModel(planned+authorWarn, noauthorHint) →liveness-planned-property, default "Keep it — …" hint.action.outcomeMessages(planned+authorWarn+authorHint) → itsauthorHint.agent.lifecycle/tool.outputSchema(experimental) → the default hint, not thenote.manifest.runtime(live-elsewhere) is admitted to the warn map, butlintLivenessProperties({ manifest })gives 0 findings: no walk visitsmanifest.dashboard's warn map holds no depth-2 key (widgets.chartConfig.*aredead), so the lint reads one level ofchildren.deadrow gives byte-identical findings with and withoutauthorWarn. Alive+authorWarnrow throws.535d1d25abthey sit atREADME.md:560("opt-in per entry"),:564(theexperimental-only default),:565("falls back tonote"),:569-572(rule 1),:573-577(rule 2) and:587-591(coverage). The same section also had two false statements the card did not list:enable.searchable's_authorWarnSkipped)": no ledger row at any depth carries_authorWarnSkipped(census over all 41 ledgers, 940 rows), andenable.searchableislive.manifest,connector,analytics_cube,query,realtime_subscription,apiand the fiveRestServerConfigfamilies).pnpm --filter @objectstack/spec run check:generatedpasses with "All 15 generated artifacts are up to date",check:docsincluded. The README's only programmatic readers arecheck-liveness.mts,build-state-counts.mtsandreadme-table.mts, and all three read the Current state table only.git grepfor the section's phrases overcontent/,apps/andpackages/spec/scriptsgives 0 hits.git grep -n "opt-in per entry\|authorWarn"overpackages/spec/liveness/andcontent/docs/:README.md:379("the honest status isdead+authorWarn") and:966-969("every misleading entry carriesauthorWarn", plus "must also be registered inTYPE_COLLECTIONS") state the old model as current. Both are fixed here.:348/:360(the dated 2026-07 tally),:434, and the Current state Notes cells are dated history, not the current model. They are unchanged.content/docs/: the only hits arereleases/v14.mdx:245andreleases/v17/index.mdx:175. Both are release-owned history, so there is no owner to notify.permissions/authorization.mdx:524andui/translations.mdx:390describe warnings in terms that still hold.Every sentence changed, before → after
Section heading
## Author warnings — closing the loop (authorWarn)## Author warnings — closing the loop (verdicts, andauthorWarn)Intro paragraph
compilelint (packages/lint/src/lint-liveness-properties.ts) reads these ledgers and emits an advisory warning when an authored object/field sets a property that is misleading — "you set this expecting it to do something; at runtime it does nothing / isn't enforced" — with a corrective hint. Never fails the build."packages/lint/src/lint-liveness-properties.ts) reads these ledgers and emits a warning when an authored item sets a property whose row warns — "you set this expecting it to do something; at runtime it does nothing / isn't enforced / isn't read yet" — under a rule id per verdict, with a corrective hint.os lint,os validateandos build/os compilerun it, and so does the runtime metadata write door foremail_template,mappinganddatasourceitems. It is never an error and never failsos build, butos lint --strictandos validate --strictturn every warning into exit 1."AUTHORING_COMMANDSandruntimeTypesinauthoring-rules.ts, andfailing = errors.length + (strict ? warnings.length : 0)inlint.ts. feat(lint): a ledger-dead or live-elsewhere key warns without an authorWarn opt-in, and never shows the ledger note (#16094) #21092 measured the--strict0 → 1 flip at both doors.The opt-in line
authorWarnonly opts in a row whose verdict does not warn on its own:" It is followed by a new five-row table:dead,live-elsewhereandexperimentalalways warn,plannedonly withauthorWarn, andlivenever. Each row names its rule id.authorWarnfield rowexperimentalentry warns by default — it's a declared-but-unenforced guarantee)."plannedrow warn. On adead,live-elsewhereorexperimentalrow it changes nothing, because the verdict already warns. So leaving it off never keeps such a row quiet."authorHintfield rownote)."dead's says "Remove it",planned's andlive-elsewhere's say "Keep it", andexperimental's says the guarantee is not yet enforced. The row'snoteis never shown to an author, on any row: it is written for this ledger's maintainers."Rules intro
Rule 1
versioning,field.columnName,softDelete). Benign display/doc metadata that's "dead" (no runtime reader) —description,tags,icon— must NOT be marked; an author isn't misled by them."dead,live-elsewhereorexperimentalis an author-facing act. The verdict warns every author who sets the key, and fails their--strictrun. No marker keeps it quiet. That includes benign display or doc metadata with no runtime reader (description,tags,icon): gradeddead, it warns "Remove it" like any other dead key." It is followed by three bullets:dead. A key that something shows to a person islive, and that measurement, not a missing marker, is what keeps it quiet.deadafter that warns, and the warning is the measurement speaking. ⛔ Never grade a rowliveorplannedto silence it.authorHint. Thenotecannot do that job.Rule 2
default(false)flags. The lint warns on a boolean only when settrue, and it can't tell author-set-truefrom a schema default. Adefault(true)flag (enable.searchable) would then warn on every object that has anenableblock — so leave those unmarked (seeenable.searchable's_authorWarnSkipped). Object/string/array props warn when merely present, so this caveat is boolean-only."true, and on any other value when it is present at all. Where the stack it reads has been parsed (os validate,os build, anydefineStackconfig), schema defaults have already filled in, so it cannot tell an authored value from a default. Adefault(true)flag, or any key whose default materializes, would warn on every item that carries the default, whoever wrote it. SoauthorWarnstays off such a row, and a warning verdict on one warns every such item.enable.searchable(default(true)) is the shape; it is gradedlive, so the question does not arise for it.mapping.errorPolicyandbatchSizewere dead keys of this kind, and no warning could reach their authors truthfully, so they were retired instead (themappingrow below)."mappingrow, which calls those two keys "the non-boolean instance of the default(true) rule". On parsing: the rule's input tier isparsed, anddefineStackrunsObjectStackDefinitionSchema.safeParseby default.os lint's own comment says itsparsedtier fills no defaults for a raw config, which is why the sentence names the doors where defaults do fill.Coverage paragraph
authorWarn, not by touching the lint code. It covers every governed type: objects (incl.enable.*) and their fields walk bespoke nesting; flows/actions/agents/tools/skills/datasets/permissions/hooks/pages are checked as flat stack collections, and container properties fan out over arrays (each flow node, each dataset measure)."dead,live-elsewhereorexperimental) and withauthorWarnopt-ins onplannedrows, not by touching the lint code. That holds only inside the types its walk visits: objects (incl.enable.*) and their fields walk bespoke nesting, translation bundles walk their locale entries, and every type listed in the lint'sTYPE_COLLECTIONSis checked as a flat stack collection, with container properties fanning out over arrays (each flow node, each dataset measure). It reads a row and its directchildren, no deeper. A governed type the walk does not visit (manifest,connectorandrealtime_subscriptionare three) warns no author through this lint, whatever its rows say."Outside the section,
README.md:379dead+authorWarn: an author who gets a"dead, which warns its authors with noauthorWarnmarker (see Author warnings below): an author who gets a"Outside the section, below the Current state table
deadset across types is the enforce-or-remove worklist (ADR-0049); every misleading entry carriesauthorWarnso authors hear about it at compile time (governed types with warn entries must also be registered in the CLI lint'sTYPE_COLLECTIONS— see lint-liveness-properties.ts)."deadset across types is the enforce-or-remove worklist (ADR-0049); adeadrow warns its authors at compile time by its verdict alone, with noauthorWarnmarker (see Author warnings above). That reaches an author only in a type the lint walks: a governed type's warning rows are heard only once the lint visits the type, which for a flat stack collection means registering it in the lint'sTYPE_COLLECTIONS(see lint-liveness-properties.ts)."deadrows carryauthorWarntoday. The onlyauthorWarnrows are 3plannedones:action.outcomeMessages,object.externalSharingModelandtranslation.flows. The old "must also be registered" did not hold either:manifestis deliberately unwalked, and feat(lint): a ledger-dead or live-elsewhere key warns without an authorWarn opt-in, and never shows the ledger note (#16094) #21092 pinned that.Changeset: measured, not assumed
npm pack --dry-run --json --ignore-scriptsinpackages/speclists 2077 files, andliveness/README.mdis one of them (control:liveness/view.json, also listed). The README ships, so it gets apatchchangeset for@objectstack/spec:.changeset/21135-liveness-readme-author-warnings.md, carryingClause-②: no.Check Changesetjob (pr-automation.yml) has no path exemption. It is skipped only by theskip-changesetlabel or the release PR.Verification (all at head
a3aed7da52)pnpm --filter @objectstack/spec build(under the verify lock)pnpm --filter @objectstack/spec run check:generatedpnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2(lock)pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 src/lint-liveness-properties.test.ts(lock)node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands(no paths; 2 paths vs merge base535d1d25a), each command run, exit captured before any pipedispatch-gates.mjs --ran ran.listcheck-changeset-fixed,check:spec-changes,check:authz-resolver,check:error-code-casing,check:filter-alias-paritycheck:liveness,check:empty-state,check:strictness-ledger,check:variant-docs(in the derived set)pnpm check:nul-bytes, and agrep -Pcontrol-byte scan over both filesNOT MEASURED (declared to CI). Both report
PREREQUISITE NOT MET: they load builtdist/entry points this worktree does not have. Neither reads anything this diff changes, since a markdown file and a changeset are emitted into nodist/. TheLint & Repo Gatesjob measures both on a fresh full build.pnpm check:dual-build-cjs-loadsneeds every package'sdist.pnpm check:lean-entry-closureneeds the@objectstack/objectqlclosure'sdist.Typecheck not run (declared). The diff has no TypeScript, and neither
.mdfile is in any tsc program.eslint, narrowed and measured.
pnpm exec eslint --no-inline-config --format jsonwas run over both changed paths.**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}).eslint.config.mjsenables no type-aware linting (noparserOptions.project/projectService), so this diff cannot move the verdict on any file it does not touch.Acceptance notes
These are observations, not filed.
_authorWarnSkippedmentions elsewhere. No ledger carries the field any more. It is still named in spec source comments (data/mapping.zod.ts:30,ui/app.zod.ts:1153,conversions/registry.ts:3013) and in two dated Current state Notes cells (mapping,qa). These are comments and history, with no runtime or author-facing effect. Carrier: none.view.label,rowLevelSecurity.labeland.descriptionend with "Not authorWarn'd" / "Benign, not authorWarn'd". That is true of the marker, but no longer means silence: the verdict warns. This card touches no ledger JSON, so they are unchanged. The rows themselves were measured under the designer-previews ruling (objectuidb11afd4967), so the restated rule 1 implies no re-grade. Carrier: none.kernel/metadata-plugin.zod.ts:947reasons from "datasource.jsoncarries 0authorWarnrows". Its conclusion still holds, becausedatasourcehas no warning-verdict row, but the reason is the old model. The lint's own docblock (lint-liveness-properties.ts:720) says "Covers every governed metadata type", which the coverage measurement above contradicts. Both are comments, and lint code is out of this card's scope. Carrier: none.535d1d25ab.origin/mainmoved 2 commits, touchingliveness/analytics_cube.jsonandliveness/dataset.jsononly. Neither touches a sentence here or a file in this diff.Generated by Claude Code