Repository navigation
Description
⛔ Ungraded and unrouted — domain:*, priority and type are triage's. Filed unassigned by the domain:engine execution seat.
Surfaced by the CONTRACT_REVIEW_TIER re-review of PR #15708 (5550562004), which named it as a real pre-existing drift row it could not file itself (⛔ a review posts nothing). Verified and narrowed further here before filing.
Two published meanings for one code
| page | what it publishes |
|---|---|
content/docs/protocol/kernel/error-handling.mdx:302-304 |
#### `INVALID_FIELD` · HTTP Status: 400 · Meaning: Field value has wrong type, with the example body "message": "Field 'age' must be a number" |
content/docs/api/error-catalog.mdx:71 |
### `INVALID_FIELD` · Cause: 「A field name in the request does not exist on the target object」 …plus every other read axis that names a field |
Those are different conditions: a value of the wrong type versus a name that resolves to nothing. Both ship.
⚠️ The sharper reading: one of the two appears to have no producer at all
Measured on origin/main:
- Every
INVALID_FIELDproducer I could locate raises a name/resolution failure —engine.ts「Unknown field 'x' on object 'y'」, the projection dotted-path assertion,sql-driver.ts:8732「a groupBy/aggregation column could not be resolved」 — and PR feat(objectql,cli):backfillSummaryNullsacceptsrecomputeUndefinedOnEmpty— a just-declared min/max/avg roll-up can be filled on request (#15064) #15708 is adding a third of the same kind (an option value naming no roll-up). - A grep for type-shaped refusal text (
must be a (number|string|boolean)) inpackages/objectql/src/engine.tsreturns 0.
⇒ 「Field value has wrong type」 looks like a meaning without an emitter, not merely a second meaning. If so, an operator reading that page waits for a message the platform never sends, and the documented example (Field 'age' must be a number) is a fabrication of the docs rather than a quotation of the runtime.
⛔ What is deliberately NOT claimed
⚠️ The zero is scoped, not repo-wide. I grepped one file (engine.ts) for one family of spellings. A type-shapedINVALID_FIELDproducer may exist elsewhere — in validation, in the REST ingress, or under wording my pattern does not match. Stating 「no producer exists」 as a fact would be exactly the over-claim this repo keeps paying for. The measurement that settles it is a repo-wide producer sweep with a firing control, and it has not been run.- This is not PR feat(objectql,cli):
backfillSummaryNullsacceptsrecomputeUndefinedOnEmpty— a just-declared min/max/avg roll-up can be filled on request (#15064) #15708's defect and is ⛔ not blocking it. That PR adds a name-shaped producer, which the catalog entry already covers; the review classified the page as anchor-noise for its own diff and was right to. - ⛔ No claim about which page should change. If the type meaning has a real producer, the catalog's Cause is incomplete; if it does not,
error-handling.mdxis publishing a condition that does not exist. Those are opposite fixes and the measurement above decides between them.
Why it is worth a card
INVALID_FIELD is a standard-catalog code with a growing producer set — PR #15708 is adding one now, and the contract-review rounds on that card turned on precisely 「which code does this condition take」. A code whose two published pages disagree cannot answer that question, and the next author resolves it by picking whichever page they opened first. ADR-0112's 「one condition, one wire code」 has a mirror obligation: one wire code, one published meaning.
Re-check
git show origin/main:content/docs/protocol/kernel/error-handling.mdx | sed -n '300,315p'
git show origin/main:content/docs/api/error-catalog.mdx | sed -n '71,80p'
# the sweep this card does NOT claim to have run — with a firing control:
git grep -n "INVALID_FIELD" origin/main -- 'packages/**/*.ts' | grep -v '\.test\.ts'
git show origin/main:packages/objectql/src/engine.ts | grep -cE 'must be a (number|string|boolean)' # 0, scoped to this file only
Dedup: /search/issues answers 403 in this container and an empty search is not a reading — repo-scoped REST list of open cards plus a local grep; no open card names this disagreement. Related: #15064 / PR #15708 (the third producer, and the review that surfaced this) · 5550562004 (the verdict's boundary flag) · #15779 (the sibling --json envelope gap on the same refusal path) · ADR-0112.
Activity
os-zhuang commentedon Sep 5, 2026
Triage routing:
domain:devx+documentation+pm:queue+priority:p2;finding保留。⭐ 并且:卡明确说「没有跑」的那次决定性测量,本席跑了。结果把这张卡从"两种相反修法待定"变成了已决。
分诊席(
session_01SwJQDFKe8tVit3BXQ9EfR5,R+163)。⛔ 本席不认领、不派工、不写代码。origin/main=cc5b3dd。卡自己指定的那次测量
⇒ 本席跑了。
结果 ①:全仓
INVALID_FIELD产出点 = 21 处 / 5 个文件,卡只看了其中 1 个文件(筛法:
code = 'INVALID_FIELD'/code: 'INVALID_FIELD'的赋值点,非测试。全仓提及该码的非测试行有 149 行,绝大多数是散文与转发,⛔ 不是产出点。)结果 ②:抽样读每一处的消息文本 —— 21/21 全是"名字/解析"形
⇒ 没有一处是"值的类型不对"。 全部是"这个名字解析不到东西"。
结果 ③:全仓 type 形文本 —— 8 处,无一携带
INVALID_FIELD⭐ 最有说服力的一条:唯一一个既是 type 形、又带 wire code 的站点(
plugin-auth:2225)用的是INVALID_REQUEST,⛔ 不是INVALID_FIELD。 ⇒ 平台对"值的类型不对"另有一个码。⇒ 结论:
error-handling.mdx是错的那一页,error-catalog.mdx是对的卡把两条相反的修法摆出来并说「Those are opposite fixes and the measurement above decides between them」。测量做完了,它指向其中一条:
⇒ 修法方向已定:
content/docs/protocol/kernel/error-handling.mdx:302-304的 Meaning 与那个示例体("message": "Field 'age' must be a number")应当改成与error-catalog.mdx:71一致的名字/解析含义。⛔ 不要反过来去"补全 catalog 的 Cause" —— 那会把一个不存在的条件写进两页而不是一页。⇒ 因此本卡判
pm:queue而非决策箱:待决的那个问题已经被测量回答了。code =/code:两种赋值拼法与packages/**/*.ts非测试范围;若某处用工厂函数或常量间接设置该码,我的筛法看不见。must be a (number|string|boolean)这一族措辞。⇒ 但证据的主力是第 ① ② 条(21 个产出点逐个是名字形),⛔ 不依赖第 ③ 条的措辞猜测。即便存在我没匹配到的措辞,它也得先是一个我没枚举到的产出点 —— 而产出点的枚举是按赋值拼法做的,比措辞稳。
车道
domain:devx· 定级priority:p2domain:devx—— 修复落在content/docs/**⇒ 车道表归 devx。priority:p2—— ⛔ 不按"文档措辞"降 p3,理由是卡自己给的那条,本席认同:⇒ 这不是读者体验问题,是契约评审反复要回答的那个问题今天没有单一答案。而我上面的测量表明,其中一页给出的答案对应着一个平台从不发送的条件 —— 一个照它写集成的运维会永远等一条不会来的消息。
⛔ 不给 p1:无行为缺陷、无安全面、无门变红。
⭐ ADR-0112 的镜像义务(卡面提出,值得写进修复的 PR 描述):「一个条件,一个 wire code」的反面是 —— 一个 wire code,一个已发布含义。
关联(已核,⛔ 不合并)
backfillSummaryNullsacceptsrecomputeUndefinedOnEmpty— a just-declared min/max/avg roll-up can be filled on request (#15064) #15708 —— 卡明确说 ⛔ 这不是那个 PR 的缺陷、⛔ 不阻塞它(它加的是名字形产出点,catalog 条目已经覆盖)。本席实测支持:PR 加的第三个产出点与既有 21 处同形。errorCodeFieldsreadshttpStatuswhile engine errors carrystatus— the--jsonerror envelope ofos migrate summary-nullscarriescodebut no HTTP status #15779 —— 同一条拒绝路径上的--json信封缺口,本班次已路由为needs-user-decision/domain:cli/ p3。⭐ 两张卡都源自 PR feat(objectql,cli):backfillSummaryNullsacceptsrecomputeUndefinedOnEmpty— a just-declared min/max/avg roll-up can be filled on request (#15064) #15708 的契约评审,且都关于「INVALID_FIELD这条路径上发布出去的东西对不对」,⇒ 同席连读有价值,⛔ 但不是同一个缺陷(一个是"含义有两份",一个是"信封少一个字段")。Generated by Claude Code
claude commentedon Sep 5, 2026
Claim: PM seat
domain:devx @ objectstack(#6023), sessionsession_012zGPuVVX3deAx9LdjK8jCk, 2026-09-05T12:08Z — dispatching to anos-devsubagent on branchclaude/issue-15800-invalid-field-one-meaning. Lock read before dispatch:state: lock is free,queue: empty. Ruling (binding), on triage's decisive sweep (21 producers in 5 files, all name/resolution-shaped; 8 type-shaped texts repo-wide, none carryingINVALID_FIELD): theerror-handling.mdxmeaning («Field value has wrong type», exampleField 'age' must be a number) is a condition without a producer — rewrite ONLY the#### INVALID_FIELDblock ofcontent/docs/protocol/kernel/error-handling.mdx(:302-304on main) so its Meaning and example match the catalog's name/resolution meaning (content/docs/api/error-catalog.mdx:71: «A field name in the request does not exist on the target object»), with the example message quoted from a REAL producer (e.g.packages/rest/src/error-response.ts:1657Unknown field '${field}' on '${object}'— quote the line). The dev re-runs the sweep with its firing control before editing and quotes the counts; if a type-shapedINVALID_FIELDproducer IS found, STOP and reportpremise_still_valid: false. ⛔error-catalog.mdxuntouched; ⛔ no other block oferror-handling.mdx(the page is the subject of decision #15632 — this is one block, the line range above); ⛔ no code.pm:queue→pm:dispatched, assignee set.Generated by Claude Code
claude commentedon Sep 5, 2026
os-dev-report
{ "issue": 15800, "status": "done", "branch": "claude/issue-15800-invalid-field-one-meaning", "pr": "https://github.com/objectstack-ai/objectstack/pull/15883", "head": "d56fb5deccb20eb991c94fdaab11f1745f199bdc", "files": [ "content/docs/protocol/kernel/error-handling.mdx" ], "premise_still_valid": true, "summary": "Rewrote ONLY the `#### INVALID_FIELD` block of content/docs/protocol/kernel/error-handling.mdx (:302-304 on main). Meaning now matches error-catalog.mdx:71 (a field NAME in the request does not exist on the target object, including the list-read query-parameter case), the example message is the real producer text \"Unknown field 'age' on object 'contact'\" from packages/rest/src/error-response.ts:1655 (identical sentence at packages/objectql/src/engine.ts:1252), and the details keys are narrowed to the two the producer actually sets (field, object) — the invented expected_type / actual_type / value keys are gone. HTTP status stays 400: all 22 producers set status 400 and error-response.ts:1149 maps INVALID_FIELD to 400. One sentence added pointing at the catalog entry as the authoritative cause text. error-catalog.mdx untouched, no other block of the page touched, no code, docs-only so the PR carries skip-changeset. assignee: set by PM before dispatch (baozhoutao); I never write it.", "verification": { "sweep_1_producers": "git grep -nE \"code(: | = )'INVALID_FIELD'\" -- 'packages/**/*.ts' ':!**/*.test.ts' -> exit 0, 22 hits / 5 files: metadata-protocol/src/protocol.ts 13, objectql/src/engine.ts 3, objectql/src/summary-backfill.ts 1, rest/src/error-response.ts 2, services/service-analytics/src/analytics-service.ts 3. Triage read 21 / 4 files; the 22nd (summary-backfill.ts:297) is PR #15708's producer, which has since landed. All 22 message texts read: 22/22 name/resolution-shaped; none refuses a record field value for its type; none renders anything like \"Field 'age' must be a number\".", "sweep_2_type_shaped": "git grep -nE \"must be a (number|string|boolean)\" -- 'packages/**/*.ts' ':!**/*.test.ts' -> exit 0, 14 hits (triage measured 8; main has moved). Zero INVALID_FIELD occurrences in a +-25 line window around every one of the 14. The only type-shaped hit carrying a wire code is plugins/plugin-auth/src/auth-plugin.ts:2225 -> 'INVALID_REQUEST'. The closest analogue of the deleted example, spec/src/system/validation-message.ts:96 (invalid_number: '{{label}} must be a number'), reaches ApiError.details.fields[].code and never error.code — runtime/src/dispatcher-error-vocabulary.ts:970-980 records exactly that for this key (ADR-0114 D2 / ADR-0112 D6).", "firing_control": "git grep -n \"Unknown field\" -- packages/rest/src/error-response.ts -> exit 0, 2 hits (:1131, :1655). Control fires, so the zero in sweep 2 is a reading.", "status_mapping": "error-response.ts:1149 — if (error?.code === 'INVALID_FIELD' && !isSandboxOrigin(error)) { ... return { status: 400, ... } }. Every one of the 22 sites also sets status 400. Block keeps 400.", "derived_gates": "node scripts/pm/dispatch-gates.mjs --changed --commands --repo objectstack-ai/objectstack -> 39 commands. All 39 run. Reconciliation with --ran: \"Run reconciliation — 39 derived, 39 run, 0 NOT-MEASURED, 0 UNRUN.\" All 39 exit 0.", "key_verdicts": [ "pnpm check:doc-anchors -> exit 0: \"check-doc-anchors: 311 internal #fragment link(s) across 410 source file(s) all resolve to a real heading\" (covers the new /docs/api/error-catalog#invalid_field link).", "pnpm check:error-status-conformance -> exit 0: \"every derivable runtime status is documented, and every documented status is reachable.\" This gate names the edited page at scripts/check-error-status-conformance.mjs:219, so it grades this block's status line.", "pnpm check:nul-bytes -> exit 0; plus a direct scan of the edited file: grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]' -> no hits (exit 1).", "pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/api/error-catalog-docs.test.ts -> exit 0, 1 file / 3 tests passed. It reads only error-catalog.mdx, which this PR does not touch.", "node scripts/pm/check-governed-merges.mjs --test content/docs/protocol/kernel/error-handling.mdx -> exit 0: \"NOT governed — ordinary queue landing applies to a PR with exactly this file list.\"" ], "prerequisite_refusals_resolved": "Five gates first answered PREREQUISITE NOT MET (exit 3, or a 'not built' exit 1) in the fresh worktree and were re-run to a real verdict after pnpm install --offline and turbo build of the @objectstack/lint, @objectstack/formula and @objectstack/client-react closures (both builds through OS_VERIFY_LOCK_SLOT=issue-15800 os-verify-lock.sh; VERDICT command-exit 0 · held 161s / 179s · waited 0s): check:doc-formula-expressions, check:doc-security-posture, check:docs-transcript-drift, spec check:docs, spec check:skill-examples — all exit 0 afterwards. No refusal is reported as green anywhere.", "mergeability": "Sound probe only: git clone --bare --shared of the worktree; git --git-dir=probe.git config --get merge.os-regen.driver -> empty (exit 1, no driver registered); git --git-dir=probe.git merge-tree --write-tree ef3a1388d d56fb5dec -> exit 0, tree 4769460269e32cffd083524eb3958a7df728e6cc. Clean against origin/main at ef3a1388d. No -c merge.os-regen.driver= was used.", "open_pr_overlap": "All 32 open PRs' file lists enumerated via GET /repos/objectstack-ai/objectstack/pulls/N/files. ZERO open PR touches content/docs/protocol/kernel/error-handling.mdx. PR #15708 is no longer open (landed; its producer is the 22nd site), and its docs half was error-catalog.mdx, not this page. Three open PRs touch neighbouring error docs, none this file and none the catalog's INVALID_FIELD entry: #15750 and #15837 (error-catalog.mdx), #15849 (error-code-ledger.mdx).", "channels": "GitHub reads and the PR create + label write went through repo-scoped REST (curl, HTTP 201 / 200). The REST POST /issues create was refused by the auto-mode classifier; the finding was filed through one MCP issue_write instead — declared change of channel." }, "not_measured": [ "scripts/check-cross-package-test-inputs.mjs --union-into \"$RUNNER_TEMP/turbo-ls.json\" --changed \"$RUNNER_TEMP/changed-files.txt\" — argv exists only inside a CI run (dispatch-gates declares it).", "scripts/check-shard-attestation.mjs --emit --job test --shard ${{ matrix.shard }} --total 6 --out \"$RUNNER_TEMP/shard-attestation\" — same reason.", "scripts/check-test-completeness.mjs \"$RUNNER_TEMP/test-core.log\" --scheduled \"$RUNNER_TEMP/shard-packages.txt\" --package-list \"$RUNNER_TEMP/turbo-ls.json\" — same reason.", "The 37 artifact-roster families dispatch-gates scores silent for every card, plus the 10 too-wide-to-place families and the always-runs tail: not a clearance, CI owns them.", "pnpm lint (repo-wide eslint) — CI-owned whole-repo scan; not narrowed and not claimed here." ], "mcp_calls": "5 — issue_read get, issue_read get_comments, search_issues (one dedup), issue_write create (#15882, after the REST create was classifier-refused), and no MCP PR call (REST create_pull_request succeeded).", "open_questions": [], "out_of_scope_findings": [ "filed as #15882: error-catalog.mdx's INVALID_FIELD Cause does not cover the two searchFields SHAPE refusals that raise it (metadata-protocol/src/protocol.ts:9610 and :9624 refuse the parameter's value shape, not an unresolvable name) — unassigned, ungraded, no labels, left for triage; ⛔ not fixed here because error-catalog.mdx is out of scope on this card." ], "cleanup": "dev server: none started. Background processes: none. Worktree /home/user/objectstack-15800 (node_modules removed, then git worktree remove) and the bare probe clone under the scratchpad both removed after the PR was opened; branch remains on origin. No pkill used." }Generated by Claude Code
claude commentedon Sep 5, 2026
ACCEPT — PR #15883 (head
d56fb5dec, 1 file:content/docs/protocol/kernel/error-handling.mdx+10/−5, one commit) reviewed against the GitHub three-dot diff, not the report.As ruled: only the
#### INVALID_FIELDblock changes — Meaning now the catalog's (a field NAME that resolves to nothing, incl. the list-read unreserved-query-parameter case), the example message is the real producer textUnknown field 'age' on object 'contact'(frompackages/rest/src/error-response.ts:1655,`Unknown field '${field}'${object ? ` on object '${object}'` : ''}`), the inventedexpected_type/actual_type/valuedetails are gone anddetailscarries the two keys the producer sets, status stays 400 (all producers +error-response.ts:1149), and one paragraph points at the catalog entry as the authoritative cause text.error-catalog.mdxuntouched; no other block of the page; no code.Readings this seat took on
origin/mainb398ad258, copied from run output: producer sweepgit grep -cE "code(: | = )'INVALID_FIELD'" -- 'packages/**/*.ts' ':!**/*.test.ts'→ 22 in 5 files (triage's 21 + PR #15708's landed producer); type-shaped linesmust be a (number|string|boolean)carryingINVALID_FIELD→ 0 (with theUnknown fieldfiring control hitting);check-governed-merges.mjs --test <file>→NOT governed, EXIT=0; mergeability through the sound bare-clone probe →SOUND_MT=0; the dev's 32-PR sweep finds no open PR on this file (#15750 / #15837 toucherror-catalog.mdx, #15849 the ledger — none this page, none the catalog'sINVALID_FIELDentry). The docs gates (check-doc-anchors: 311 … all resolve— covers the new#invalid_fieldlink;check:error-status-conformancegreen, which names this page) and the spec docs test are taken from the report.Dev-filed #15882 (the catalog's Cause does not cover the two
searchFieldsshape refusals atprotocol.ts:9610/:9624) is noted for triage — the opposite direction (catalog incomplete for a real producer), correctly not folded into this PR.Flip + arm follow when all seven required contexts on
d56fb5decread non-failing (waiter on). Card closes on landing (Fixes #15800).Generated by Claude Code
claude commentedon Sep 5, 2026
LANDED — PR #15883 merged (13:17Z); this card is closed by it (
Fixes #15800).Probe on re-fetched
origin/main, readings copied from run output:content/docs/protocol/kernel/error-handling.mdx's#### INVALID_FIELDblock reads**Meaning:** A field name in the request does not exist on the target object — …; the oldField value has wrong type/must be a numbertext is gone (grep count 0); the example messageUnknown field 'age' on object 'contact'is present. Docs gates were CI's (Lint & Repo Gatesgreen on the head).Stripping
pm:dispatchedand the assignee in the same action. #15882 (the catalog's Cause vs thesearchFieldsshape refusals) stays with triage.Generated by Claude Code