Repository navigation
Commit d8c7d38
fix(lint): one-line verdicts for the flow-credential, AI skill, flow-filter-token, managed-API, org-axis, retired-permission, seed and semantic-role rules, and a bounded field-no-consumers clause;
Part of #22161
Clause-②: no
Stage 2 of the card, slice 10: the last 10 `packages/lint` rule ids from
PR #22799's "Remaining for later slices" list, one whole source file
each, plus stage 1's `field-no-consumers` (the card's own subject),
whose "a consumer of the same name on …" clause listed every object
sharing the field name. The card stays open: the `packages/cli` ids, the
fenced ids and the hint call remain (below).
## What changes
- **One verdict line per finding.** Each finding of the 10 ids prints a
`message` of one verdict sentence, followed by `Did you mean "…"?` where
the rule already offered the nearest name. Every finding the rules' own
suites fire is now 194 characters or fewer; the longest of each id was
222 to 441 before. The runtime publish gate's suite (the whole
`@objectstack/metadata-protocol` suite) fires two of the 10, at 181 or
fewer (390 and 235 before). The `packages/cli` unit tier and `os
validate` on the four example apps fire none of the 10.
- **`field-no-consumers` names the first other object and counts the
rest.** `a consumer of the same name on "showcase_account" (and 9 more)
does not count`, where it listed all ten. On `os validate` of
`examples/app-showcase` its longest finding drops from 341 to 199
characters (that 341-character finding now prints 143). Its `inert` arm
is byte-identical, so the CLI pins and stage 1's pending changeset that
quote it are untouched. Its explanation is unchanged: the verdict cut
names, not reasoning, and the explanation already says "Verdicts are per
object".
- **Where a verdict keeps a name, it is the one the author needs.** A
verdict no longer repeats what `where` already names: the skill
(`ai-skill-tool-unresolved` now opens on the tool), the managed object,
the sharing rule's object and the semantic-role object.
`seed-value-outside-state-machine` names the first three declared
states, then `(and N more)`.
- **`flow-credential-literal` keeps a short route in the verdict**
(`move it to a connector's auth.credentialRef`, or `drop it` for a
connector input). Measured at the base: `os validate` and `os build`
print the hint as their `fix:` line (`authoringFindingDetailLines`), but
`os lint`'s text face prints it only under `--fix` (`commands/lint.ts`,
`flags.fix && issue.fix`). The module comment said both text faces
"leave the hint to `--json`", which is no longer true for `os validate`;
it now says what was measured. The route by auth variant (`bearer`, a
header `api-key`, `api-key` with `paramName`) stays the hint's,
unchanged.
- **`semantic-role-field-unprovisioned` keeps `ADR-0015` and the word
"storage".** `validate-expressions.test.ts` (the cross-site convergence
pin) holds this finding and the expression site's unprovisioned-anchor
warning to the same object, column and reason. The first cut dropped
both and that pin went red on the whole-suite run; the verdict was
fixed, not the pin, which is outside this slice's files.
- **The reasoning moves to `RULE_EXPLANATIONS`**
(`packages/lint/src/rule-explanations.ts`), 10 new entries, so `os
explain RULE_ID` prints it and the CLI's `rule:` line ends with the
pointer for these ids. No CLI source changes: `explainPointer()` and `os
explain` resolve any key the table holds
(`packages/cli/test/explain-rule-id.test.ts` iterates every key; it ran
in the unit tier below). `node bin/run.js explain
org-axis-cross-org-bu-grant` prints the entry. The 121 entries already
in the table are byte-equal (the diff of `rule-explanations.ts` is
additions only, +266 / -0).
- **Nothing else moves.** Rule ids, severities, `path`, `hint` (the
`fix:` line) and what each rule accepts or refuses are unchanged. No
condition, branch, skip or dedupe moved, and no export was added,
removed or renamed: every hunk in the eleven rule files is a `message`
expression, a comment, a verdict helper, or the removal of a value that
fed only the old message. The plumbing hunks, each message-only:
- `lint-flow-credential-literals.ts`: `SERVED` becomes one clause;
`STEER_HEADER` and `STEER_QUERY` become one `STEER_CONNECTOR` (both arms
now give the same short route; the hint keeps routing by shape);
`STEER_INPUT` is shortened; the query arm's position reads `url query
parameter`.
- `validate-managed-api-methods.ts`: the import of
`describeManagedApiMethodConflicts` (`@objectstack/spec/data`) goes; it
fed only the message. That spec sentence alone measured about 190
characters at three verbs, so no verdict built on it fits; see
Acceptance notes for what that leaves in `packages/spec`.
- `validate-seed-state-machine.ts`: `atMostThree()` (new,
module-private) bounds the known-state list.
- `validate-field-consumers.ts`: the `sharedClause` expression names
`sharedWith[0]` and counts the rest.
- No helper changed shape (string in, string out); no truth table is
involved.
- `.changeset/22161-lint-slice-10-one-line.md`: `@objectstack/lint`
`patch`, naming every door that prints the new text (below). `Clause-②:
no`, as line 2 says: every rule id these entries key already had an
exported constant on the root barrel (`index.ts` is untouched), and
slice 1's contract review (② of its record) reads `RULE_EXPLANATIONS`
entries as data in an existing export.
- **The declared rider went unused:** the `packages/cli` and
`packages/metadata-protocol` tests that assert these ids' text pin
fragments the new verdicts keep (`auth.credentialRef` and `every member
who can read flows` for `flow-credential-literal`; the unchanged `inert`
arm of `field-no-consumers`), and they ran green unedited (below).
The verdict forms, the longest of each arm the rule suites fired (the
last line is `os validate` on the showcase app):
```text
connector input 'headers.Authorization' holds a credential-shaped literal, readable by every member who can read flows; drop it: the connector authenticates through `auth.credentialRef`
url query parameter 'api_key' holds a credential-shaped literal, readable by every member who can read flows; move it to a connector's `auth.credentialRef`
Agent "helper" (surface 'ask') references skill "metadata_authoring" (surface 'build'), so the runtime refuses the binding and chatting with this agent fails at request time
Tool family "crm_*" matches no tool this stack can resolve, so the subscription contributes zero tools at runtime
Tool "action_send_invoice" names action "send_invoice", which does not become an AI tool (it needs `ai.exposed` and a headless path), so the runtime drops the reference
Tool "knowledge_base" resolves to no stack, platform or materialised action tool, so the runtime drops it and the agent cannot call it. Did you mean "search_knowledge_base"?
Filter value "{DATEADD(day, -45)}" calls 'DATEADD', which neither the flow template dialect nor the filter placeholders define, so the evaluator raises a guard refusal and this node cannot run
`enable.apiMethods` advertises [create, update, delete], which `managedBy: 'better-auth'` affordances refuse (needs userActions create/edit/delete), so the registry strips them
Recipient `unit_and_subordinates` (a business unit AND every descendant unit) on an object that opted out of tenancy spans EVERY organization: a cross-org business-unit grant ADR-0105 D6 forbids
sets `allowRestore: false`, the retired default of a key removed in @objectstack/spec 17, which the parse strips in silence, so this line grants nothing
`mode: 'insert'` re-inserts every record on each replay boot (dev-server restart, package re-publish) with no existing-row check, so every restart duplicates its rows
seeds 'status=complete', which the 'proj' state machine does not declare (known states: active, cancelled, completed (and 2 more)), and seed writes skip that rule, so a typo lands silently
highlightFields points at "organization_id", an injected system column with no storage on this external object (ADR-0015), so every consumer renders it empty on every record
declared, but nothing in this stack displays or reads it (carrier-only: 4 site(s) name it without reading it; a consumer of the same name on "showcase_project_membership" (and 1 more) does not count)
```
## Shared prose: written once (the dispatch's route, measured)
| shared text | ids | where it is written once | held to its source by |
|---|---|---|---|
| who can read a flow's literal (`readable by every member who can read
flows`) | `flow-credential-literal`, all three positions | `SERVED` in
the rule file (verdict); the explanation's first paragraph (reasoning) |
the rule's test, every position fired |
The dispatch expected shared paragraphs across ids, as slices 3 to 9
found. Measured: none of the 10 explanations shares a paragraph with
another. The two AI rules judge different references (an agent's
`skills`, a skill's `tools`) against different universes; the two seed
rules answer different questions (replay idempotence, the state-machine
exemption); `org-axis-cross-org-bu-grant`'s sibling
`org-axis-permission-inheritance` is not converted (174 at its longest)
and has no entry to share with. So the table holds the one in-file
shared clause.
The explanation module imports nothing, so each fact it writes out is
held to its source by the rule's own test: the credential positions and
the three region kinds by firing each; the surface defaults, the `both`
arm and the skipped unresolved skill by the existing cases; the three
tool sources, the wildcard and the four headless paths by each one
silencing the rule; the flow template functions to
`FLOW_TEMPLATE_DATE_FUNCTIONS` and `FLOW_TEMPLATE_VALUE_FUNCTIONS`, and
the placeholders by each one staying silent; the verb table to
`MANAGED_WRITE_VERB_AFFORDANCE`, verb by verb through the rule; the
business-unit recipients to the `ShareRecipientType` partition the suite
pins, each fired; the retired keys and their residue value to the spec's
capture; the declared-state union by each of its three sources silencing
the rule; and the external-object provenance by the existing cases.
## Census (taken first, before any edit, at the base `23419bafb7`)
Method: slice 4 to 9's scratch preload (`NODE_OPTIONS=--import`, never
committed), which patches `Array.prototype.push` to record every
finding-shaped object (`rule` + `message`), deduped by (rule, message)
per process, with the push site for the 11 ids and the other ids in
their files. Lengths are `message` alone; the printed line adds `where`
and `: `. Rows from `os lint`'s own printer (`commands/lint.ts:761`,
which pushes `where: message`) are named as such. Positive control in
every run: `field-no-consumers`, plus `sort-field-unknown` and
`searchable-field-unknown` in the runtime-gate suite, recorded in every
run.
- **`packages/lint` suite**, base 137 files / 6,416 tests, after the
CLI, metadata-protocol and example closure build at the base: **all 10
ids fire**, every one over 200 at its longest; each longest equals PR
#22799's list. `field-no-consumers`: 14 messages, longest 167.
- **`packages/cli` unit tier**, the whole project (281 files, 4,174
tests): fires none of the 10, before and after; `field-no-consumers`
fires 7 messages, the longest 142 as `os lint`'s printer row, all on the
unchanged `inert` arm.
- **Runtime publish gate:** the whole `@objectstack/metadata-protocol`
suite (228 files, 225 run, 28,129 passed), which reads
`@objectstack/lint` from its built `dist/`, before on the base-built
dist and after on the rebuilt one: fires `flow-credential-literal` (3
messages, `runtime-authoring-gate.flow-credential-literal.test.ts`) and
`permission-retired-lifecycle-residue` (2); none of the other 9.
- **Example apps:** `os validate` (the built CLI) on `app-crm`,
`app-todo`, `app-showcase` and `app-multi-package`, exit 0 each, before
and after: **none of the 10 fire**; `field-no-consumers` fires on all
four, and every app prints the same number of `rule:` lines before and
after (9 / 7 / 70 / 3).
- **Extra, outside the dispatch's list:** the four `packages/cli` e2e
files that name these ids (`flow-credential-literal-doors.e2e.test.ts`,
`rule-line-explain-pointer.e2e.test.ts`,
`starter-field-consumers.e2e.test.ts`,
`generate-scaffold-gates.e2e.test.ts`; in no per-PR vitest project, run
here under `OS_TEST_TIERS=nightly`) and the one integration file
(`union-fold-command-parity.test.ts`).
- **After:** the lint `dist/` rebuilt from the slice at `dfec085942`
(marker `holds a credential-shaped literal` 1 hit each in
`dist/index.js`, `index.cjs`, `runtime.js`, `runtime.cjs`; the old
`holds a literal value that reads as a credential` 0 in each; `why
business-unit trees stay inside one organization` 1 in
`dist/rule-explanations.js` and `.cjs`). Every after-run except the
final lint suite read that dist; the commits after it add the changeset
and the `origin/main` merge, which touches no `packages/lint` file.
| rule id | severity | lint suite: before (msgs · range) → after |
runtime-gate suite | examples `os validate` | cli e2e (4 files) |
|---|---|---|---|---|---|
| `flow-credential-literal` | warning | 10 · 282–390 → 10 · 144–185 | 3
· 294–390 → 3 · 148–181 | — | 3 · 294–390 → 3 · 148–181 (printer rows
346–430 → 188–233) |
| `ai-skill-surface-mismatch` | error | 6 · 259–281 → 6 · 151–173 | — |
— | — |
| `ai-skill-tool-unresolved` | warning | 24 · 238–441 → 23 · 113–173 | —
| — | — |
| `flow-filter-token-unknown` | error | 8 · 267–278 → 8 · 181–192 | — |
— | — |
| `object/managed-api-method-unaffordable` | error | 3 · 392–412 → 7 ·
146–176 | — | — | — |
| `org-axis-cross-org-bu-grant` | error | 2 · 331–365 → 2 · 160–194 | —
| — | — |
| `permission-retired-lifecycle-residue` | warning | 2 · 233–235 → 2 ·
150–152 | 2 · 233–235 → 2 · 150–152 | — | — |
| `seed-insert-mode-duplicates-on-replay` | warning | 1 · 222 → 1 · 166
| — | — | — |
| `seed-value-outside-state-machine` | warning | 3 · 288–320 → 4 ·
161–188 | — | — | — |
| `semantic-role-field-unprovisioned` | warning | 4 · 285–291 → 3 ·
163–173 | — | — | — |
| `field-no-consumers` | warning | 14 · 64–167 → 19 · 64–178 | — | crm 4
· 202 → 4 · 184; todo 2 · 109 → 2 · 109; showcase 21 · 341 → 19 · 199;
multi-package 1 · 64 → 1 · 64 | 4 · 64–108 → 4 · 64–108 |
The cli unit tier column is not drawn: it fires `field-no-consumers`'
unchanged `inert` arm alone (7 · 64–142), before and after. The cli
integration file fires `field-no-consumers` only (2 · 64–140, before and
after). `org-axis-permission-inheritance` (not converted): lint suite 4
· 167–174 before and after. A message count that falls after
(`ai-skill-tool-unresolved`, `semantic-role-field-unprovisioned`,
showcase's `field-no-consumers`) is two findings whose old texts
differed only in a name the verdict no longer repeats or a list it now
cuts; a count that rises is a case this slice added. Every push site of
the 11 ids fires before and after (one each,
`semantic-role-field-unprovisioned`'s helper reached from its two
roles).
## Doors that print the new text
Read from the registry (`authoring-rules.ts`):
`lintFlowCredentialLiterals` and `validateFlowFilterTokens` are CLI and
runtime on `['flow']`, `validateManagedApiMethods` on `['object']`,
`validateRetiredPermissionResidue` on `['permission']`;
`validateOrgAxisRedLines`, `validateFieldConsumers`,
`validateSemanticRoles` and the two seed rules are CLI only; the two AI
rules are members of the reference-integrity suite on its default
`['flow']`. The CLI's callers: `commands/validate.ts`, `compile.ts` (`os
build` is its alias), `lint.ts`, `verify.ts` through
`judgeAuthorTimeRules` on the `validate` set, and `init.ts` through
`validateScaffold` on the `build` set (one caller,
`commands/init.ts:1314`; `commands/generate.ts` calls no authoring
rule). Measured at the runtime door with `runRuntimeAuthoringRules` on
the rebuilt dist (scratch probe): a `flow` write answers
`flow-filter-token-unknown` in `errors` and `flow-credential-literal` in
`advisories`; an `object` write answers
`object/managed-api-method-unaffordable` in `errors`; a `permission`
write answers `permission-retired-lifecycle-residue` in `advisories`; an
`agent`, `skill` or `sharing_rule` write runs no rule.
| door | ids | what changes |
|---|---|---|
| `os validate`, `os build` (and `os compile`, which `os dev` runs at
startup and, under `--verbose`, per watch recompile), `os lint` (`where:
message`), the scaffold check `os init` runs | all 11 | the text-face
verdict line; the `rule:` line gains the `os explain` pointer for the
10; `os validate --json` `errors` and `os build --json` author-time
`issues` carry the new `message` |
| `os verify` | the four errors: `ai-skill-surface-mismatch`,
`flow-filter-token-unknown`, `object/managed-api-method-unaffordable`,
`org-axis-cross-org-bu-grant` | its refusal list; it counts advisories
without printing them |
| runtime publish gate, `flow` write | `flow-filter-token-unknown`
(error), `flow-credential-literal` (warning) | the 422 issue's `message`
and the `OS_ALLOW_UNLINTED_METADATA_WRITES` refusal log line; the 2xx
`advisories` entry and the `[Protocol] authoring advisory` log line |
| runtime publish gate, `object` write |
`object/managed-api-method-unaffordable` (error) | the 422 issue's
`message` and the refusal log line |
| runtime publish gate, `permission` write |
`permission-retired-lifecycle-residue` (warning) | the 2xx `advisories`
entry and the advisory log line |
| never at the runtime gate | `org-axis-cross-org-bu-grant`, both seed
ids, `semantic-role-field-unprovisioned`, `field-no-consumers` (CLI-only
rules); both AI ids (run there only for a `flow` write, whose snapshot
carries no agents or skills) | |
| unchanged | every `hint`; every other rule id | |
Every row is named in the changeset.
## `field-no-consumers` across doors (the dispatch's item 6, measured on
`examples/app-showcase`)
- `os validate`: 51 findings, each printed once (verdict, `fix:`,
`rule:` with the pointer); the longest printed verdict line 391 → 253
characters.
- `os build`: 37 of them, each once, inside the 50-entry advisory cap
(`… and 20 more author-time warning(s) not shown (50 of 70)`); longest
printed verdict line 257 → 253.
- `os dev --compile --fresh`: the startup compile (`stdio: 'inherit'`)
prints the same 37, once; a watch recompile prints its output only under
`--verbose`, else only stderr on failure (`commands/dev.ts`,
`startWatchRecompile`). Nothing is printed again at serve.
- **No second paragraph remains.** `Give … a consumer` and `Roots
scanned` appear 0 times in the `os validate`, `os build` and `os dev`
output, before and after: stage 1 moved that text into the explanation.
No `packages/cli` printer was edited.
## Tests
The rule suites import the rule source; the CLI, runtime-gate, example
and e2e runs read the rebuilt `@objectstack/lint` dist. Every heavy run
went through `scripts/pm/os-verify-lock.sh`; its VERDICT lines are
quoted.
- **Each rule's own suite pins the new shape**, as in slices 2 to 9. The
eleven touched suites wrap their rule import and record every finding
their direct calls fire; the LAST case in each file holds every recorded
verdict of its id to one line of at most 200 characters, behind a
coverage control (each arm: all three credential positions; both surface
directions; all three tool arms; one and several refused verbs; both
business-unit recipients; both retired keys; a cut and an uncut state
list; both semantic roles; and `field-no-consumers`' `inert`,
`carrier-only`, one-name and counted arms). Exact pins hold one verdict
per arm; explanation pins hold, per id, that `explainRule(id)` exists
and names what the verdict stopped saying, and the source-held facts
above. Every refusal assertion, rule id, severity, `path`, `where` and
hint pin is unchanged. The pins that read the old text moved to the new
verdict: the credential route fragments to `move it to a connector's
auth.credentialRef` (and the hint's `type: 'bearer', credentialRef`,
newly pinned beside the existing `headerName` one); the AI tool suite's
`/references tool "…"/` matcher to `/^Tool "…"/` and `tool family` to
`Tool family`.
- **Lint suite** at the final head: `pnpm --filter @objectstack/lint
exec vitest run --maxWorkers=2`: Test Files 137 passed (137), Tests
6,455 passed (6,455); VERDICT command-exit 0 (base 137 / 6,416; no new
file, 39 new cases).
- **Lint build + typecheck:** `pnpm --filter @objectstack/lint build`
(VERDICT command-exit 0, `check-dts-emitted` 6/6), and after the merge
`turbo run build --filter=@objectstack/lint` (4 tasks, VERDICT
command-exit 0). `pnpm --filter @objectstack/lint run typecheck` at the
final head: VERDICT command-exit 0; `check:test-typecheck` OK, 2 files /
6 errors / 2 pinned signatures held. `tsc -p
packages/lint/tsconfig.test.json --listFiles` lists all eleven touched
test files.
- **CLI unit tier**, against the rebuilt lint dist: `pnpm --filter
@objectstack/cli exec vitest run --project unit --maxWorkers=2`: Test
Files 281 passed (281), Tests 4,174 passed (4,174), before and after;
VERDICT command-exit 0 both. It includes `test/explain-rule-id.test.ts`.
The integration tier is declared to CI (no file in it asserts these ids'
text, and the diff touches no spawn entry); its one file naming these
ids ran as the census extra: Test Files 1 passed (1), Tests 9 passed
(9), before and after.
- **CLI e2e files naming these ids** (`OS_TEST_TIERS=nightly`): Test
Files 4 passed (4), Tests 56 passed (56), before and after; VERDICT
command-exit 0 both. `flow-credential-literal-doors.e2e.test.ts` asserts
the message carries `auth.credentialRef` at every door; it does.
- **Runtime gate:** the whole `@objectstack/metadata-protocol` suite,
before and after: Test Files 225 passed | 3 skipped (228), Tests 28,129
passed | 19 skipped (28,148), both runs; VERDICT command-exit 0.
`runtime-authoring-gate.flow-credential-literal.test.ts` asserts `every
member who can read flows` and `auth.credentialRef`; both are kept.
- **Corpus pins over `packages/lint/src`:** `git grep lint/src --
packages/spec/src` names two that walk it,
`src/shared/retired-key-migrate-sentence.test.ts` (judges every `os
migrate meta --from N` sentence) and
`src/identity/position-delegatable-enforcer.pin.test.ts` (reads
`security-*` rule-id lines); the other hits name single files outside
this slice or are comments. No new text carries the migrate marker (the
residue explanation names `os migrate meta` without `--from`), and no
rule-id line moved. `pnpm --filter @objectstack/spec test:repo` at the
final head: Test Files 55 passed (55), Tests 971 passed (971); VERDICT
command-exit 0. Both pins are in its `vitest.repo-tests.json` list.
- **Tracker ids:** no `#` plus digits in any printed message or
explanation (`rule-explanations.test.ts` refuses one in every
paragraph); the citations stay in `//` comments and test names.
- **Ablation** (one-shot, from the committed state `92a7bebbb4`, through
`scripts/ablation-replace.mjs` wrap mode under the verify lock, plus a
shell `trap` restoring `HEAD` by absolute path). The rule suites import
the rule source, so there is no dist leg.
`seed-insert-mode-duplicates-on-replay`'s pre-slice message tail was
restored verbatim through the anchor of its new tail: anchor x1 → x0,
blob `2e2c47f4e718` → `9acef22a58d3`. Predicted before the run: exactly
two cases red, the id's exact-text pin and the 200-character bound pin;
the suite's other assertion on the message (`toContain('replay')`) holds
on the old text too. Observed: `src/validate-seed-replay-safety.test.ts`
Test Files 1 failed (1), Tests 2 failed | 7 passed (9); the two red
cases are exactly those two (the bound pin read 222 characters).
Restored: blob `2e2c47f4e718` == blob at HEAD, `git diff HEAD` empty,
`git status --porcelain` empty.
- **ESLint, narrowed:** `npx eslint --no-inline-config --format json`
over the 23 changed `.ts` files gave 23 files in the report, 0 errors, 0
warnings. The population is `eslint.config.mjs`'s `files:
['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}']` block plus the
`packages/**/*.{ts,tsx,mts,cts}` blocks, which all 23 are in (the
changeset is not a linted kind). `eslint.config.mjs` never enables
type-aware linting (no `parserOptions.project`, no typed
`@typescript-eslint` rules; the config's own note says so), so no
untouched file's verdict can move. A control-character scan of every
changed file found no match.
- **Gates:** `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` with no paths, at the final head
`82555d5ca8` (merge base `b7cd1af9df`, after one merge of `origin/main`
through `scripts/pm/os-regen-merge.sh`; the merge touched no
`packages/lint` file, and its step 2 took `origin/main`'s side of the
generated artifacts main moved, none of which this branch edits),
derived 62 commands. I ran 60 of them sequentially at that head, each
with its own log and its exit code captured before any pipe: all 60
exited 0. Two are NOT MEASURED, as the dispatch directs: `pnpm
check:dual-build-cjs-loads` (it needs every package's `dist/`, and the
dispatch excludes a whole-workspace build) and `pnpm
check:type-check-debt` (`--re-measure`, which builds every package); the
non-re-measuring half, `check:type-check-coverage`, ran and exited 0.
`--ran` over the exit-coded record: `Run reconciliation — 62 derived, 60
run, 2 NOT-MEASURED, 0 UNRUN.` The artifact-roster block the no-path run
prints (51 families) ran too: the 48 that need no pull request all
exited 0, `check:published-readme-exports` only after one `PREREQUISITE
NOT MET` (exit 3: `client-react`, `embedder-openai`, `knowledge-ragflow`
and `organizations` had no `dist/`; I built exactly those four with
their closure under the lock, VERDICT command-exit 0, and it then exited
0). The four rostered under a directory this diff touches
(`check-changeset-fixed`, `check:authz-resolver`,
`check:error-code-casing`, `check:filter-alias-parity`) are among the
48. The three PR-context guards (`check-closing-target-claim`,
`check-partof-closing-keyword`, `check-single-claim-paths`) run against
this PR once it exists, and their verdicts go to the card report (this
body is written once). The long ones on the shared box:
`check:query-options-erasure` 249s, `check:slot-lookup` 135s,
`check-comment-mask-corpus` 112s.
## Remaining for this card
- **`packages/lint`:** no author-time id outside the fenced files fires
over 200 characters in the lint suite after this slice. Measured over
the final census: 28 ids still exceed 200; 26 are PR #22448's fenced
ids, and 2 are `lint-startup-registry-verdict.ts`'s
(`startup-open-vocabulary-verdict` 779,
`startup-verdict-assertive-wording` 731), which PR #22448 records as the
repo gate `check:startup-registry-verdict`'s, not an author's.
`react-prop-deprecated` fires in no suite, as PR #22700 notes.
- **The fenced ids, as PR #22448 lists them (26):**
`lint-flow-patterns.ts` (15), `validate-flow-template-paths.ts` (3),
`validate-component-props.ts` (2), `validate-page-field-bindings.ts`
(2), `validate-expressions.ts` (1, `expression-invalid`),
`validate-filter-tokens.ts` (1, `filter-token-unknown`),
`validate-form-layout.ts` (1), `validate-print-page-blocks.ts` (1).
- **The 9 ids owned by `packages/cli`,** as PR #22448 lists them:
`hook-body/not-lowerable`, `hook-body/unparseable`,
`hook-body/bundled-fallback`, `hook-body/extraction-failed`
(`packages/cli/src/lint/hook-body-lowering.ts`);
`docs/uncollected-directory`, `docs/duplicate-name`, `docs/nav-target`,
`docs/frontmatter-tags` (`packages/cli/src/utils/collect-docs.ts`);
`picklist-reference-unverified`
(`packages/cli/src/utils/picklist-references.ts`). Also the
`action-governance.ts` boot-log lines (`packages/objectql`), stage 1's
own later slice.
- **The open hint call:** the four items slice 8's landing record lists,
plus one from this slice: `field-no-consumers`' hint lists every carrier
site, unbounded; on `os validate` of `examples/app-showcase` its `fix:`
line runs to 629 characters (16 carrier sites on
`showcase_account.website`). Unchanged here, as every slice leaves
hints. None of the other 10 ids' hints points at text the verdicts cut:
each was read against the cut text and is self-contained. The nearest
case: `seed-value-outside-state-machine`'s hint says "The exemption lets
a seed be born mid-lifecycle", and the verdict still names that
exemption (`seed writes skip that rule`).
- **`field-no-consumers` printers (item 6):** no second paragraph exists
outside `packages/lint`; `os build` and `os dev` print each finding
once, inside the 50-entry advisory cap. Nothing owed to the `domain:cli`
half from this measurement.
## Acceptance notes
- **`describeManagedApiMethodConflicts` keeps no caller outside its own
test.** Its docblock
(`packages/spec/src/data/managed-api-affordance.ts`) calls it "the one
sentence both consumers say", the registry's strip warning and this lint
finding. Measured at the base: objectql's `reconcileManagedApiMethods`
already spells its own sentence and never called it, so the lint verdict
was its only caller, and this slice's verdict cannot carry it under 200
characters. The export is a published `@objectstack/spec` symbol and
outside this slice's files; not removed and not filed (an observation
about a docblock and a now-uncalled export). Carrier: none.
- **A long name can still lengthen a line.** The 200 bound holds on
every variant the suites and the four example apps fire; the names in a
verdict (object, field, skill, tool, recipient, state, input key) are
the author's. The closest measured case is the showcase's
`field-no-consumers` on `showcase_client_brief.project` at 199, whose
one named object is 27 characters long.
- **`field-group-shadowed`** (`validate-semantic-roles.ts`, not one of
this slice's ids) lists the group's members unbounded; the suites fire
it at 168 at its longest. Observation only.
- **`os lint` printer rows** prefix `where: ` to the message, so
`flow-credential-literal`'s connector-input row prints at 233 in the e2e
run while its message is 181; slice 9 recorded the same shape (188
against 175).
---
_Generated by [Claude
Code](https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF)_
---------
Co-authored-by: Claude <noreply@anthropic.com>os explain RULE_ID carries their reasoning (#22828)1 parent 048f389 commit d8c7d38
24 files changed
Lines changed: 1211 additions & 86 deletions
File tree
- .changeset
- packages/lint/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
30 | | - | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
31 | 35 | | |
32 | 36 | | |
33 | 37 | | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
34 | 51 | | |
35 | 52 | | |
36 | 53 | | |
| |||
201 | 218 | | |
202 | 219 | | |
203 | 220 | | |
204 | | - | |
205 | | - | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
206 | 225 | | |
207 | | - | |
| 226 | + | |
208 | 227 | | |
209 | 228 | | |
210 | 229 | | |
211 | 230 | | |
212 | 231 | | |
213 | | - | |
| 232 | + | |
214 | 233 | | |
215 | 234 | | |
216 | 235 | | |
217 | 236 | | |
218 | | - | |
| 237 | + | |
219 | 238 | | |
220 | 239 | | |
221 | 240 | | |
| |||
358 | 377 | | |
359 | 378 | | |
360 | 379 | | |
361 | | - | |
| 380 | + | |
362 | 381 | | |
363 | 382 | | |
364 | 383 | | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
68 | 68 | | |
69 | 69 | | |
70 | 70 | | |
71 | | - | |
72 | | - | |
73 | | - | |
74 | | - | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
75 | 76 | | |
76 | 77 | | |
77 | | - | |
78 | | - | |
79 | | - | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
80 | 82 | | |
81 | | - | |
82 | | - | |
83 | | - | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
84 | 86 | | |
85 | 87 | | |
86 | 88 | | |
87 | 89 | | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | | - | |
93 | | - | |
94 | | - | |
| 90 | + | |
| 91 | + | |
95 | 92 | | |
96 | 93 | | |
97 | 94 | | |
| |||
172 | 169 | | |
173 | 170 | | |
174 | 171 | | |
175 | | - | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
176 | 176 | | |
177 | 177 | | |
178 | 178 | | |
| |||
182 | 182 | | |
183 | 183 | | |
184 | 184 | | |
185 | | - | |
| 185 | + | |
186 | 186 | | |
187 | 187 | | |
188 | 188 | | |
189 | 189 | | |
190 | 190 | | |
191 | | - | |
| 191 | + | |
192 | 192 | | |
193 | 193 | | |
194 | 194 | | |
| |||
0 commit comments