Repository navigation
AGENTS.md documents the broken related-list filter spelling as correct, cites a rejected line as the example, and forbids fixing it #1257
Description
Activity
- addedbugSomething isn't workingSomething isn't workingdocumentationImprovements or additions to documentationImprovements or additions to documentation
on Aug 23, 2026 - addedpm:dispatchedDispatched to a dev agent by /pm-dispatchDispatched to a dev agent by /pm-dispatch
on Aug 23, 2026 Claim: PM loop round 4 (hotcrm seat)
Session:session_61f28fb9-8e31-4a6f-b4d8-10f749a092d2
Branch:claude/issue-1257-agents-md-filter-spelling
Worktree:hotcrm-issue-1257
Domain: (hotcrm has nodomain:*taxonomy — repo-wide seat)
File surface:AGENTS.mdonly (stop on breach; explain in the report)
Container & model:M,mode:subagent,model: opus
Clause-②: no — instruction-file correction; no contract behaviour change.
Serial constraints cleared: the AGENTS.md serial queue is drained — #856/PR #1232 merged at65fdf424(verified on origin/main); base your worktree on a ref that includes it. No other in-flight claim touches AGENTS.md.Authorization — maintainer direct-dispatch, verbatim (未译): 「加种子互动,顺带把 #1257 排了」
{ "issue": 1257, "status": "done", "branch": "claude/issue-1257-agents-md-filter-spelling", "pr": "https://github.com/objectstack-ai/hotcrm/pull/1263", "premise_still_valid": true, "summary": "Corrected the ObjectQL paragraph in AGENTS.md (one paragraph, +14/-5, plus a changeset). Re-measured on base d9a9aaf9: #1254 did NOT remove the cited line — it fixed the record:activity `types` binding on the same page (#1209), and src/pages/lead_detail.page.ts:217 still reads `filter: [['status','!=','completed']]`. Re-measured the PM's mechanism premise and it HELD: no record:related_list filter in src/pages/ passes validation on this base, so the paragraph now cites the SCHEMA rather than a repo line — RecordRelatedListProps.filter in @objectstack/spec/ui is z.array(strictObject({field, operator, value})) with operator = z.preprocess(normalizeFilterOperator, z.enum(VIEW_FILTER_OPERATORS)), which is why the bare AST array and the `op:` key are both refused. Falsified one detail of the card's table: case_detail.page.ts:188 and opportunity_detail.page.ts:222 carry the SAME shape ({field, op, value}) and each raises BOTH diagnostics (unrecognized key `op` AND operator unset) — not two different spellings across two files. The prohibition is narrowed to the flow-`filter:`-vs-hook-`where:` hazard, prefaced by the boundary it lacked: a different surface never licenses a shape that surface's own schema rejects. The three broken pages are untouched (#1248's scope, still open).", "tests": "Full eight-gate `pnpm verify` chain + the CI first-step gate, re-run on the COMMITTED head 5fcddb47 with a clean tree (`git status --porcelain` empty), each exit captured by redirect (never through a pipe), each gate run individually so no && short-circuit could hide a later one. All 0: check-stackblitz-lock ('package-lock.json is in sync with package.json (v3).'), validate, typecheck, lint ('76 warning(s), 10 suggestion(s)'), lint:i18n-gate ('OK i18n lint gate: 0 `i18n/missing-*` issues'), hygiene ('OK source hygiene clean'), hygiene:tokens ('OK source token ratchet clean'; authored total ~134,680 / ceiling ~140,000), build ('Artifact: dist/objectstack.json'), test ('Test Files 121 passed (121)', 'Tests 2851 passed | 2 skipped (2853)'). AGENTS.md consumers enumerated and checked rather than assumed: test/docs-drift.test.ts (inline src/<dir>/ guard + drawn-tree guard), docs-role-hierarchy, docs-object-coverage, source-hygiene-header-position, source-hygiene-scan-surface, scripts/check-source-hygiene.mjs root text-file list. Grepped test/ scripts/ docs/ content/ .changeset/ for 'AST-array', \"one surface's spelling\", '44 occurrences', 'lead_detail.page.ts:217' — zero hits, so no drift guard pinned the old wording and none needed updating. Token ratchet is structurally unmoved (its surface is src/**/*.ts; this diff contains no src/ path). Base-tree measurement that drove the edit came from `pnpm build` on d9a9aaf9, which printed component-props-invalid / component-props-unknown-key on all three sites. Flow-side counts re-derived and corrected in the file: filter: 47 code occurrences across 18 of 22 flow files (was 44/17/21; 48 raw grep hits, one a comment at demo-bootstrap.flow.ts:296), where: still 0. Both touched files scanned for control bytes — clean.", "open_questions": [], "out_of_scope_findings": [] }PM review — ACCEPT. PR #1263. The instruction file stops lying, and it now explains why instead of pointing at a line.
Verified against the diff and the installed spec
- The corrected paragraph cites the schema, not a repo line — and I confirmed the schema claim in the shipped
@objectstack/specdist:normalizeFilterOperatorexists exactly as described, preprocessing aliases (neq→not_equals,nin→not_in, …) into the closedVIEW_FILTER_OPERATORSvocabulary. Notably that meansop:fails not becauseneqis unknown — the normalizer would have accepted it as a value — but because the key is wrong; the dev's wording ("no other key accepted") captures precisely that. - Grepped the added lines for
page.ts:<line>citations: zero. The card's own defect — a citation to a rejected line — is not recreated with a different line number, and the paragraph says on purpose why no in-repo example is cited (every currentrecord:related_listfilter is a rejected form, The Open Tasks related lists never filter out completed tasks:op/neqare not the spellingsrecord:related_listaccepts #1248). - The narrowed prohibition gains the boundary it lacked: "a different surface never licenses a shape that surface's own schema rejects" — the flow-
filter:-vs-hook-where:hazard stays protected, the blessing of schema-rejected spellings is gone.
Two of my dispatch inputs corrected by measurement — both in the right direction
- I wrote that fix(pages): filter the lead Activity timeline by feed kind, not object name (#1209) #1254 "may already be gone" for the cited line. Re-measured: fix(pages): filter the lead Activity timeline by feed kind, not object name (#1209) #1254 fixed the
record:activitybinding on the same page;lead_detail.page.ts:217still carries the AST array. The dev re-derived instead of inheriting. - My card's three-spelling table was wrong in one detail:
case_detail:188andopportunity_detail:222carry the same{field, op, value}shape, each raising both diagnostics — two spellings across the repo, not three. Corrected in the changeset's account, and it slightly simplifies The Open Tasks related lists never filter out completed tasks:op/neqare not the spellingsrecord:related_listaccepts #1248's eventual fix.
Flow-side occurrence counts re-derived and updated (44/17/21 → 47/18/22, one raw hit excluded as a comment) — the numbers in the file are now measurements with a dated base, not folklore.
Verification
Full eight-gate chain green on the committed head with a clean tree, each gate run individually with redirected exit codes. AGENTS.md consumers enumerated (six candidate guards read; greps for the old wording across
test/ scripts/ docs/ content/ .changeset/— zero hits), so no drift guard pinned the old text. That check is what stops this PR from being the next #1252-shaped surprise.Disposition
Ready + merge queue.
Fixes #1257closes the card on merge. With this, every AI author who reads the repo's instruction file before their first edit — which is all of them, it is the binding file — reads the correct shape and the reason the wrong ones are wrong.- The corrected paragraph cites the schema, not a repo line — and I confirmed the schema claim in the shipped
AGENTS.mddocuments the broken related-list filter spelling as correct, cites a rejected line as the canonical example, and forbids fixing itThe instruction
AGENTS.mdlines 79–81, in the ObjectQL section every metadata author is required to read before their first edit:The line it cites
src/pages/lead_detail.page.ts:217:What the build says about that line
record:related_listdeclaresfilteras an array of rule objects ({ field, operator, value }), withoperatordrawn from a closed enum. The AST-array form is not a second legal spelling — it is rejected, dropped, and the list renders unfiltered. On that page the consequence is user-visible: an "Open Tasks" list that lists completed tasks.Why this is worse than the three defects it produced
Three different broken spellings of the same filter now exist across the record pages, each dropped silently:
case_detail.page.tsop: 'neq'Unrecognized key(s) on this filter rule: opopportunity_detail.page.tsoperatorunsetfilter.0.operator: Invalid option … (nothing is set there)lead_detail.page.ts:217filter.0: Invalid input: expected object, received arrayThey were filed as #1248 and, for the third, as a comment on it. But the spellings are not three independent typos — the instruction file blesses one of them and tells the next author not to correct it. An AI author who reads
AGENTS.md, follows it exactly, and copies the cited line will reproduce this defect and believe they got it right. The prohibition (Do not "fix" one surface's spelling into another's) is aimed at a real hazard — flowfilter:versus hookwhere:genuinely are different surfaces — but it was extended to a third case where the two spellings are not two surfaces, just one correct shape and one rejected one.This repo's charter makes that expensive: 「展品撒谎等于教每个照抄的 AI 撒谎」. Here the exemplar's instruction manual is the thing that lies, which reaches further than any single page — every fork inherits
AGENTS.md.What needs to change
filter:takes rule objects, not the AST array. Keep the flow/hook half of the paragraph — it is right and it is load-bearing.PageComponentSchema.propertiesis an open bag and the build's rejection is advisory only (objectstack#5068). Until that closes,AGENTS.mdis doing the enforcement — which is exactly why it has to be right.Found while dispatching #1209; the third spelling was measured by the dev on that card, and the
AGENTS.mdcitation was verified by the PM againstorigin/main@a0bc5e8d.