Repository navigation
feat(spec): an email field declares itself a correspondent address for inbound mail (FieldSchema.mailAssociation) - #22943
Conversation
…elf a correspondent address for inbound mail (WIP) Claude-Session: https://claude.ai/code/session_019G5ZnRrTMiwcHG6iZM19zf Co-authored-by: Claude <noreply@anthropic.com>
…trol, the changeset (WIP) Claude-Session: https://claude.ai/code/session_019G5ZnRrTMiwcHG6iZM19zf Co-authored-by: Claude <noreply@anthropic.com>
…r mailAssociation authorable-surface/data.json (written by the spec build), the reference docs (gen:docs) and liveness/state-counts/field.md (gen:liveness-counts). Claude-Session: https://claude.ai/code/session_019G5ZnRrTMiwcHG6iZM19zf Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 4 package(s): 11 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 140 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 80f8d66a607562d2acbf19dcec3771903b4be343 && git checkout 80f8d66a607562d2acbf19dcec3771903b4be343
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e357a441c6423798a673d4a39ecee4b31adacf79 1986ccb7414e85791d12961955aa9c83d90b1af2 && git checkout -B drift-repro e357a441c6423798a673d4a39ecee4b31adacf79 && git merge --no-ff 1986ccb7414e85791d12961955aa9c83d90b1af2
node scripts/docs-audit/affected-docs.mjs --json e357a441c6423798a673d4a39ecee4b31adacf79
|
… for the mailAssociation rows Each touched published package names itself: the FIELD_KEY_STORAGE_CLASS row, the FIELD_REFERENCE_POSITIONS scrub row, and the field designer's new row label and help text in four locales. spec stays minor. Claude-Session: https://claude.ai/code/session_019G5ZnRrTMiwcHG6iZM19zf Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: PR #22943, PR 1 of 2 on card #22870 ( ① Derived judgmentsEach accept-set or public-surface change the diff implies, judged.
Obligation carried forward, not a finding: the describe promises an effect ("associates the message with every record of this object…") that nothing in this repo delivers yet. The card sanctions that window ("Liveness rows start ② Semver level
Clause-②: yes (widening) Right: the diff widens one accept set (① 1) and narrows none;
③ Boundary flagsEvery dev flag and every Round-1 report 6117670284,
Round-1 report, Patch-round report 6117732579:
Check-runs on this head, as read in this actThe conclusions are the gate verdicts. At this reading every check-run on the head is 39 check-runs on the head.
Implemented-by: VERDICT: PASS read 2026-10-12T04:49Z. Generated by Claude Code |
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 38205616907 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 38206500175 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Part of #22870
Clause-②: yes (widening)
PR 1 of 2 on #22870, as ruled in
6116489638(Q1 → A) and built from the stage-0 report6115941082§3.3, §3.5 and §4. PR 2 (packages/services/service-mailbox: the three objects, both read gates,manage_mailboxesand themailboxtoken) stays on the card, so #22870 remains open after this lands. The key's runtime reader is the sibling ingest card #22871, which isBlocked-by: #22870.The ruling executed (verbatim, not re-ruled)
The card's constraint, verbatim: "Declared = enforced. Every key this card adds must get its runtime reader in the sibling ingest card, or it is not added. Liveness rows start
plannedand leave it when the reader lands."What
FieldSchema(packages/spec/src/data/field.zod.ts)mailAssociation: z.boolean().optional()besidereferenceVia. The TSDoc and the describe state the input, the output, the organization scope and the one legal type. No default, soundefinedalways means "not authored".FieldSchema'ssuperRefine, right after the tworeferenceViachecks. On any type butemailthe key (TRUE or FALSE) gets onecustomissue at[mailAssociation]. The message names the field's type and the remedy: change the type toemail, or drop the key.field.form.ts: one row, "Match Inbound Mail",visibleWhen: "data.type == 'email'". This is a parse gate, so the row shows exactly where the key parses. The help text says where the key takes effect ("only where a mailbox service is set up"), not when. The zh-CN, ja-JP and es-ES leaves are hand-authored.liveness/field.json:planned,authorWarn: true,authorHint"Keep it: no mailbox service reads this yet; it takes effect when the mailbox capability ships." The note names the flip condition: the row goeslivewhen the #22871 ingest lands, citing its matcher, and dropsauthorWarn/authorHintin the same edit..changeset/22870-field-mail-association.md:@objectstack/specminor,Clause-②: yes (widening);@objectstack/driver-sql,@objectstack/metadata-coreand@objectstack/platform-objectspatch(added in the patch round, seat order 6117707461).Nothing outside
packages/specreads the key yet. No object, gate, capability name or capability token is added; those belong to PR 2.Declared = enforced
FieldSchemasuperRefineemail@objectstack/lintliveness-planned-property, atobjectstack validate/objectstack lintand the metadata save doormailAssociation: truewarns with theauthorHintplanneduntil that reader landsMechanism assumptions, measured at
origin/main018b077fbgit grep -c mailAssociation -- packagesanswers 0 hits (exit 1). Control:git grep -c referenceVia -- packages/spec/src/data/field.zod.tsanswers 12.referenceViatype check sits atfield.zod.ts:2173–:2200, insideFieldSchema'ssuperRefine. The pinexpect([...authorWarnedProperties('field')]).toEqual(['conditionalRequired'])sits atlint-liveness-properties.test.ts:1624.dropped-refinements.baseline.jsongains no site.data/Fieldalready carries the root site"", the file is unchanged, andcheck:authorable-surfaceis green.authorable-surface/data.json(+1 line, written by the spec build),content/docs/references/data/field.mdx,.../data/object.mdxand.../system/migration.mdx(fromgen:docs), andliveness/state-counts/field.md(fromgen:liveness-counts). The predictedjson-schema.manifest/shards did not move. After regenerationcheck:generatedanswers "All 14 generated artifacts are up to date".Files beyond the claim's declared surface (each demanded by a pin; none governed)
The claim fenced
field.zod.ts,field.form.ts, one new spec test,liveness/field.json, theauthorWarnedProperties('field')pin, the generated artifacts and one changeset. A newFieldSchemakey and a new form row are also counted by the ratchets below, and each one is red until the key is classified. ThedueLikeprecedent (215e662) paid the same set.packages/drivers/driver-sql/src/builtin-column-collision.ts:FIELD_KEY_STORAGE_CLASSis pinned againstFieldSchema.shape. The new entry ismailAssociation: 'presentation'.packages/metadata-core/src/object-schema-fls-references.ts:FIELD_REFERENCE_POSITIONSis pinned againstFieldSchema.shape. The new entry iskeep, because the key is a boolean that names no field.packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts: the servedfieldproperty count moves 78 → 79.packages/lint/src/validate-predicate-path-refs.test.ts: the shipped-form predicate census moves 87 → 88, and the literal-comparison census moves 64 → 65.packages/platform-objects/src/apps/translations/*.metadata-forms.generated.ts: the row's label and help text, in en plus three hand-authored locales. Inobject-lifecycle-panel-echo-decisions.test.ts, the translated-label control moves 669 → 670.Neither classification table changes runtime behaviour for a boolean. An unclassified key was already silent in driver-sql, and the FLS scrub already passed a boolean through. The changeset first named
@objectstack/specalone, as the precedent's did; the patch round (seat order 6117707461) added apatchentry for each of the three touched published packages, because every touched published package names itself in a changeset.Tests (HEAD
52bcc8429)packages/spec/src/data/field-mail-association.test.ts(16 cases):email(TRUE and FALSE, fullsafeParsegreen), and no default materializes.invalid_type.text,url,phone,textarea,lookup,formula,numberandboolean. Each case gets exactly onecustomissue at path['mailAssociation'], namingtype: 'email'and the offending type.falseis refused.booleanmailAssociation.data.type == 'email'.scripts/ablation-replace.mjswith atraprestore):if (false && field.mailAssociation …, and the tool reported the anchor 1 → 0, blob2a3049530→a290fcd02.2a30495308c6andgit diff HEADempty, both from the tool and from the trap's owngit hash-objectcomparison../field.zodfrom source, so no dist sits on the path.@objectstack/spec, rebuilt at52bcc8429: the full suitepnpm --filter @objectstack/spec testpassed 649 files / 19410 tests (1 todo), andpnpm --filter @objectstack/spec typecheckexits 0.pnpm --filter 'PKG^...' buildfor the five packages):@objectstack/lint: full suite, 137 files / 6529 tests passed.@objectstack/metadata-core: full suite, 17 / 386 passed.@objectstack/platform-objects: full suite, 69 / 1083 passed.@objectstack/driver-sql:builtin-column-collision.test.ts, 11 passed.@objectstack/metadata-protocol:protocol.meta-types-degenerate-derivation.test.ts, 18 passed.typecheckexits 0 on all five.eslint --no-inline-config --format json): 13 files linted, 0 errors, 0 warnings.eslint.config.mjs'sfiles: ['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}']; no changed file is ignored.eslint.config.mjs: "noparserOptions.project, no typed@typescript-eslintrules"), so this diff cannot move a verdict on an untouched file.pnpm lintis CI's.Gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no paths) derived 114 commands at merge base018b077fb. Each one was run with its exit code recorded before any pipe, then reconciled with--ran: 114 derived, 112 run, 2 NOT-MEASURED, 0 UNRUN.pnpm check:i18nfirst answered PREREQUISITE NOT MET. It was re-run after building its stated prerequisite closure, and answered "all bundles in sync, no undeclared authoring keys" for 9 packages. That makes the hand-authored locale leaves byte-identical to the extractor's output, with the*.source-hashes.generated.tsfiles untouched.check:lean-entry-closurelikewise went exit 3 → exit 0.check:skill-examples. Reason: the client SDK packages are not built with declarations, and this diff touches no skill or example.check:dual-build-cjs-loads. Reason: it needs every package built with declarations, while the local i18n prerequisite build was JS-only.check:platform-checklistexits 1 with 8 ABSENT SYMBOL / ANCHORS LOST problems indocs/qa/platform-checklist/areas/*.json. The control run on a detached018b077fbcheckout answers the identical 8, so this is not this diff. The gate is kept out of per-PR CI bychecklist-status.yml's own note.Acceptance notes
packages/spec/liveness/README.md'sfieldcell still says the type "carries NOauthorWarnrow at any depth". That was already untrue before this PR (conditionalRequired'sdeadrow has warned on its own since the verdict-warns ruling), and it is further from true now. Prose only; no gate reads it. Carrier: none.lint-liveness-properties.test.ts's "field walk: a malformedfieldsarray …" block could takemailAssociationas a real subject. It is not re-subjected here because aplannedrow is scheduled to fliplive, which is exactly the subject loss that block's neighbour exists to survive.content/docs/data-modeling/fields.mdx) is added. The reference page carries the describe, and a narrative page belongs with the reader, so no page advertises a capability that nothing delivers yet.Review
Clause-②: yes (widening)onpackages/spec, so a review atCONTRACT_REVIEW_TIERis owed before landing. No governed surface is in the file list.Generated by Claude Code