Repository navigation
docs: stop teaching the retired reference_to key and an object shape the repo never uses - #1809
Conversation
…erence_to` key
`FieldSchema` has no `reference_to` key; the canonical spelling is `reference`,
and every lookup in this tree is authored through `Field.lookup(...)`. AGENTS.md
named the retired key at three sites, which is why the brief derived from it
taught the same key.
Measured at this commit against the pinned @objectstack/spec 17.3.0:
`FieldSchema.safeParse({type:'lookup',reference_to:'crm_account'})` fails with
`unrecognized_keys` and the engine's own "Did you mean `reference_to` ->
`reference`?" receipt. `reference_to` has zero occurrences in
`src/objects/*.object.ts` (18/18 files use `Field.lookup(...)`).
Sites re-derived by text at this commit: `:62`, `:141`, `:348` (the ruling's
`:284` had moved to `:348`). Control: `masterDetail` returns 3 lines, so the
zero on `reference_to` is a real zero. No other sentence in the file moves.
Refs #1686
Ruling: decision batch #74
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017FzrA1G4U89KEMf7wfLmqq
…sing factory, named export
The Data Modeler brief taught a definition shape this repo does not have and the
spec does not accept: a root import, an `as ObjectSchema` cast, a default export,
and a raw `reference_to` key on the lookup example.
Measured at this commit against the pinned @objectstack/spec 17.3.0, with tsc
itself as the witness for the import half:
$ tsc --noEmit # on the brief's own two lines
error TS2305: Module '"@objectstack/spec"' has no exported member 'ObjectSchema'.
The root exports neither a value nor a TYPE by that name, so the taught `as`
cast could never have compiled. `@objectstack/spec/data` exports it as a
function whose `.create` is a function. The ruled replacement type-checks clean
(tsc exit 0) and `ObjectSchema.create(...)` parses at runtime.
All 18 `src/objects/*.object.ts` files agree, and agree exactly: 18/18 carry the
identical line `import { ObjectSchema, Field } from '@objectstack/spec/data';`,
18/18 call `ObjectSchema.create(`, 18/18 export a named `const`, and 0/18 use a
default export. `src/objects/index.ts` re-exports each by name — `export {
Contract } from './contract.object';` — which is why the export must be named.
The lookup example now teaches `Field.lookup('crm_account', { ... })`, the only
form in the tree (58 calls). It emits `reference`, never `reference_to`.
Refs #1686
Ruling: decision batch #74, items 1 and 3
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017FzrA1G4U89KEMf7wfLmqq
…he schema accepts DECLARED DEVIATION (forced by the ruled change; not in the ruling's enumeration). The ruling converts this example to `ObjectSchema.create(...)`, a factory that PARSES. Under the `as ObjectSchema` cast it replaced, nothing in the example was ever checked, so `enable.search` was inert. Under `.create(...)` the very example the brief leads with now THROWS: Unrecognized key(s) on `enable`: `search`. Until this shape was closed an unknown flag was dropped without a word — the object shipped as if the author had never written it; `enable` is a closed vocabulary in which every flag carries an enforcement contract. So applying the ruling's three named edits and stopping would have shipped a brief whose flagship example fails on first use — the exact defect class this card exists to remove, newly made load-bearing by the card's own fix. `searchable` is the canonical spelling and preserves the author's intent, so this is a rename, not a deletion: probed against the closed vocabulary, `trackHistory` `apiEnabled` `activities` `searchable` `files` `apiMethods` are accepted and `search` `notes` `tasks` `events` `fullTextSearch` are rejected. Note the engine offers NO "did you mean" for this one — unlike `reference_to`, it is caught only by running the parse. The whole corrected example now parses end to end. Refs #1686 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017FzrA1G4U89KEMf7wfLmqq
… are both rejected
DECLARED DEVIATION (same defect class as the ruled fix, same file; NOT in the
ruling's enumeration, and NOT forced by it — this snippet sits outside the block
the ruling converts, so it was broken before this PR and would stay broken).
Reported rather than assumed, and isolated in its own commit so the seat can drop
it without touching the ruled work.
Measured at this commit, the brief's field example 4:
Unrecognized key(s) on this field: `formula`, `return_type`. Did you mean
`formula` -> `expression`, `return_type` -> `returnType`? Until this shape was
closed these were dropped silently — the field was still created, minus
whatever the key was meant to constrain, protect or compute.
It is a TRAP beyond the spelling: taking the engine's hint literally and writing
`returnType: 'currency'` still fails, because the closed enum is
`number|text|boolean|date` — currency is a field TYPE, not a formula return type.
An author who followed both the brief and the fix-up hint would land on a second
rejection.
The tree answers it directly and identically in two places: `quote_line_item`
and `opportunity_line_item` both write this exact field as
`Field.formula({ label: 'Total', expression: F\`...\` })` and omit `returnType`
altogether — 19 `Field.formula` calls in `src/objects/`, exactly one `returnType`
anywhere in `src/`.
Not a governance question: `AGENTS.md` names neither `return_type` nor
`returnType` (zero occurrences; its single `formula` hit at :525 is the product's
formula builder), so unlike the `reference_to` half this touches no ruled surface.
Refs #1686
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017FzrA1G4U89KEMf7wfLmqq
…leases nothing Refs #1686 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017FzrA1G4U89KEMf7wfLmqq
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
维护者速读(终稿 · 席位复核后)
改了什么 —— 两个文件不再教
为什么改 —— 规范里根本没有
⇒ AI 照着简报写,写出来的是少了一条约束的字段,而且不报错。 风险与代价(含回滚) —— 很低。全是给 AI 看的文档, 席位意见 —— ⭐ 建议两处申报的越界(D1、D2)都保留,理由如下,但它们各自独立成 commit,您想丢哪个都能单独丢:
你要做的 —— ⇒ 您只需要一个动作:认可 D1 / D2 保留(或指出要丢哪个),然后合并。 ⭐ 顺带一提,dev 还测出第四处教这个退役键的地方—— Generated by Claude Code |
…etired `reference_to` key The copy-paste brief that puts an agent in the Autonomous Development role closed with a self-correction bullet telling it to check that "all foreign keys (`reference_to`) point to valid objects". `FieldSchema` has no `reference_to` key; before that shape was closed the key was dropped silently, so an author following the bullet got a field created minus the constraint they wrote. This was the fourth and last teaching site in the repo. The other three (`AGENTS.md` `:62`, `:141`, `:348` and `.github/instructions/metadata.md`) were corrected by PR #1809, which recorded this one as outside its file surface. The replacement mirrors what landed rather than coining a third phrasing: "lookup / master-detail targets" and "name real objects" are AGENTS.md's Phase-3 checklist entry verbatim, and the `Field.lookup(...)` / `Field.masterDetail(...)` parenthetical is the spelling AGENTS.md uses at `:62` and `:348`. The brief's own "defined in Phase 2" anchor is kept. The bullet is the only line that moves. Measured at this commit, zeros paired with controls: `reference_to` has 0 matches under `.github/` (control `Field.lookup` returns 2 there) and 0 teaching sites repo-wide. The 10 remaining matches are non-teaching and untouched: 3 in PR #1809's changeset, 2 under `docs/archive/2026-02/`, and 5 lines across 4 `test/*.ts` files reading the key as a tolerant alias. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017FzrA1G4U89KEMf7wfLmqq
Fixes #1686
Also declared: the ruling's line numbers were stale and I used the tree's. Ruling item 2 names
AGENTS.mdsites:62,:284,:141. Re-derived by text at my own commit, the three sites are:62,:141,:348— matching the dispatch's own re-measurement. The count still balances at three. Control for the grep:masterDetailreturns 3 lines inAGENTS.md, so the post-edit zero onreference_tois a real zero and not a broken search. No fourth site exists, and no other sentence in the file moves.The two engine measurements, re-run at this commit
The ruling's pre-dispatch condition, explicitly not discharged by the triage seat. Both re-run against the pinned
@objectstack/spec17.3.0 (package.jsonpins it; the installed package self-reports it).1.
reference_tois not a key onFieldSchemaVerbatim receipt:
Positive control — the canonical key parses, so the rejection is about the key and not the fixture:
reference_to -> reference. The engine actually emits a Unicode arrow→. Immaterial to the ruling; recorded because the receipt was quoted as verbatim.2.
@objectstack/specdoes not exportObjectSchemaat the rootA runtime
incheck cannot see type-only exports, and the brief used the name in a type position (as ObjectSchema), so the runtime reading alone would not have settled it. TypeScript settles it — on the brief's own two lines:⇒ the root exports neither a value nor a type by that name; the taught cast could never have compiled. The ruled replacement type-checks clean (
tscexit 0).Zone 2 assumptions, tested
import { ObjectSchema, Field } from '@objectstack/spec/data';; 18/18 callObjectSchema.create(; 18/18 export a namedconst; 0/18 use a default export.src/objects/index.tsre-exports each by name (export { Contract } from './contract.object';).:141is the only checklist entry naming the keyAGENTS.md.One refinement on assumption 2: 17 of the 18 files do import from the root
@objectstack/specas well — forF,P,cel,expression. So the brief's error was never "the root import is wrong"; it was that the root import does not carry this symbol. The corrected example imports both entry points exactly as the tree does.Declared deviations — a question for the seat
D1 —
enable.search→enable.searchable(commita679c3c) · forced by the ruled changeThe ruling converts the example to
ObjectSchema.create(...), a factory that parses. Under theascast it replaces, nothing in the example was ever checked. Under.create(...)the brief's flagship example now throws:⇒ applying the ruling's three named edits and stopping would have shipped a brief whose lead example fails on first use — the exact defect class this card exists to remove, newly made load-bearing by the card's own fix. I judged shipping that indefensible, so I fixed it and am telling you rather than asking first.
searchableis the canonical spelling, so this preserves the author's intent — a rename, not a deletion. Probed against the closed vocabulary:trackHistoryapiEnabledactivitiessearchablefilesapiMethodsaccepted;searchnotestaskseventsfullTextSearchrejected. Note the engine offers no "did you mean" here, unlikereference_to— this one is caught only by running the parse.D2 — field example 4 to
Field.formula(...)(commit3b12fb6) · same class, NOT forcedThis snippet sits outside the block the ruling converts, so it was broken before this PR and would have stayed broken. Same defect class, same file, and the engine names the fix:
returnType: 'currency'still fails, because the enum isnumber|text|boolean|date— currency is a field type, not a formula return type. An author who followed both the brief and the engine's own fix-up hint lands on a second rejection.The tree answers identically in two places:
quote_line_itemandopportunity_line_itemboth write this exact field asField.formula({ label: 'Total', expression: F`…` })and omitreturnTypeentirely (19Field.formulacalls insrc/objects/; exactly onereturnTypein all ofsrc/).Why I judged this in-bounds rather than a new card: it is not a governance question.
AGENTS.mdnames neitherreturn_typenorreturnType— zero occurrences (its singleformulahit,:525, is the product's formula builder). So unlike thereference_tohalf, nothing ruled is touched. It is inside my declared file surface, mechanical, and its shape is pinned by the tree. Filing it instead would have needed another serial slot on a hot governed file behind three already-queued cards.If you disagree with either, drop the commit —
a679c3cand3b12fb6are independent and the ruled work is complete without them. Dropping D1 does mean shipping an example that throws.Proof the corrected example actually works
Not "it looks right" — the two code blocks were extracted from the shipped file, spliced, and run:
tsc --noEmit --strict→ exit 0, no diagnostics.ObjectSchema.create(...)accepted it at runtime, emittingreference: 'crm_account'for the lookup, a CELexpressionfor the formula, andsearchable: trueinenable.The before/after is a real reversal: every one of the four shapes this PR touches was rejected at the parent commit and is accepted now.
维护者速读(草稿)
改了什么 —
AGENTS.md三处、.github/instructions/metadata.md一份简报,都不再教reference_to这个键。规范(@objectstack/spec)里根本没有这个键,正确写法是Field.lookup('crm_account', ...)工厂函数——仓库里 18 个对象文件 100% 都这么写。同时把简报里那段对象定义示例改成树里真实存在的形状:从/data导入、用会做校验的ObjectSchema.create()构造、具名导出。为什么改 — 这是「文档教了一个运行时会拒收的形状」。更关键的是引擎的原话:这个形状被关掉之前,这类键是被静默丢弃的——字段照建,但作者写下的那条约束凭空消失,没有任何报错。AI 按简报写代码,写出来的就是这种少了约束的字段。
AGENTS.md是源头,简报只是忠实照抄,所以两个文件必须同一笔改,单改简报会让它和真相源文件打架。风险与代价(含回滚) — 风险很低:改动全部是给 AI 看的文档,不含任何⚠️ 需要您留意的是两处主动申报的越界(D1、D2):简报里另有两个同类错键,其中一个是被本次修复「激活」的——因为把不做检查的
src/代码,产品行为零变化,不发布给用户(changeset 用空 frontmatter 声明「不发布任何东西」)。回滚就是 revert 本 PR,不影响运行中的应用。as强转换成了会校验的工厂,原本潜伏的错键现在会直接报错。这两处各自独立成一个 commit,您若不认可可以单独丢弃。席位意见 —
你要做的 — 本 PR 触及受管面(
AGENTS.md+.github/instructions/**),按AGENTS.md:578 §How a green PR lands必须保持 draft、不入队、不开自动合并,由维护者手动合并。请先就 D1 / D2 两处申报表态,再合并。Acceptance notes
Out of scope, reported not fixed —
.github/tasks/autonomous_feature_dev.md:20still instructs the agent: "Ensure all foreign keys (reference_to) point to valid objects defined in Phase 2." That is the same defect class as this card and is a genuine fourth teaching site for the retired key — but it is outside my declared file surface (.github/instructions/metadata.md,AGENTS.md,.changeset/), so I did not touch it. It needs its own card. Flagging it because Zone 2 assumption 3 asked whether:141was the only checklist entry naming the key: insideAGENTS.mdit is, but this file has one too.Noted, not filed — carriers exist for each, so none is orphaned:
test/*.tsfiles readreference_toas a tolerant alias (f.reference ?? f.reference_to ?? f.referenceTo). They pass today and this PR does not move them; the alias branch is simply dead tolerance. Carrier: whoever takes theautonomous_feature_dev.mdcard. ⛔ Not filed — dead code, not a reproducible defect.test/opportunity-line-item-add-picker.test.ts:91carries a comment callingreference_to"the authoring-side word AGENTS.md uses". This PR makes that sentence false. It is a stale comment in a test outside my file surface, and the test's behaviour is unaffected. Carrier: same card.Field.text(...)/Field.select(...). Both parse clean, so they are a style divergence from the tree, not a defect — deliberately left alone rather than polished into scope.Guard not extended — per ruling item 5 and
AGENTS.md, the#1233guard reads paths, not identifiers, and extending it was refused twice. This PR adds no gate. The accepted cost stands: this class is caught by reading.Gates
Full chain, run at
10bb230(the final commit). Exit code captured into a variable before any pipe, and the heavy pair run under the shared verify lock.pnpm validatepnpm typecheckpnpm lintpnpm lint:i18n-gatepnpm hygienepnpm hygiene:tokenssrc/**/*.tsand this diff touches no file undersrc/pnpm build && pnpm testos-verify-lock: VERDICT command-exit 0; 165 test files, 3453 passed, 1 skippedThat is every gate in
pnpm verify(validate && typecheck && lint && lint:i18n-gate && hygiene && hygiene:tokens && build && test). Nothing narrowed, nothing deferred to CI.Token ratchet reading, for the record: business semantics ~84,609 · interaction layer ~37,837 · authored total ~137,092 — identical to the parent commit.
Landing posture
⛔ Draft. ⛔ Not queued. ⛔ Auto-merge not armed. Maintainer merges.
🤖 Generated with Claude Code
https://claude.ai/code/session_017FzrA1G4U89KEMf7wfLmqq
Generated by Claude Code
Generated by Claude Code