Skip to content

spec + service-mailbox: sys_mail_message (the inbound mirror object) and the association seam — phase 1 of #8998 inbound mail, spec seat #22870

Closed

Description

@objectstack-fleet

Ruled: 6116489638 · letter Q1 A · Q3 C (staged) · Q4 A; Q2 C3 · Q5 A confirmed · 2026-10-12T02:08Z
Filing gate: ④ — a per-layer child of the coordination node #8998. Part of #8998.

Provenance:

This card carries phase 1's spec-facing half: the object and the seam. Its sibling, the ingest half (connector-imap + minimal service-mailbox), is filed beside it and is Blocked-by: this card.

Scope

Taken from the design record. ⛔ Nothing here re-decides it.

  • Placement (ruling 6116489638, Q2 → C3): the three objects (sys_mailbox identity half, sys_mail_message, sys_mail_association), their registration and both read gates live in a new packages/services/service-mailbox, which this card's PR 2 creates. ⛔ Not packages/platform-objects (ADR-0029 D1/D2/D6). PR 1 is the spec field key FieldSchema.mailAssociation (Q1 → A). The build sheet is the stage-0 report 6115941082 §3 and §4.
  • sys_mail_message: a platform-owned faithful mirror (decision 1, Mirror, not Adopt). The external standard (RFC 5322) dictates its schema: participants, message id and threading references, subject, body parts, and attachments by file reference. It follows the outbound precedent sys_email (packages/platform-objects/src/audit/sys-email.object.ts). Apps keep their own business objects and link through a declared association. ⛔ Apps never extend sys_mail_message with business keys.
  • The association seam: one closed extension point, "given these participant addresses, which record(s) does this message belong to?", answered by the app. The platform never learns what a contact is.
  • Association is stamped at ingest, plus an explicit re-match action (decision 2).
  • Unmatched messages go to an unassociated pool, retro-matched when a matching record appears (decision 3). The pool's visibility defaults to restricted, meaning mailbox admins and owners only, not organization-wide.
  • Phase-1 privacy is "forwarding is consent" (decision 5). ⛔ The known-contact strictness knob is not in phase 1. When it lands, it is opt-in and default OFF.
  • Out of scope: sys_calendar_event, per-user mailbox policy keys, and write-back.

Constraints

  • This adds a new packages/spec accept surface, so it goes to the spec seat at contract-review tier (the tier the record names for this leg). Clause-②: yes.
  • 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 planned and leave it when the reader lands.

Acceptance

  • sys_mail_message is registered, and a showcase boot serves it through the data API.
  • The seam contract is published in packages/spec, schema-validated, and has a liveness ledger row.
  • The pool's restricted default is pinned with a negative test: an ordinary member reading an unassociated message is refused (assert code and status).

Who acts

The domain:spec seat.

Dedup

MCP issue search over objectstack-ai/objectstack, open and closed:

Activity

  1. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Release: #22870 goes back to pm:queue with the ruling and its execution plan intact. This seat stands down before the build

    domain:spec seat 3 (#18883) · zhuangjianguo · session session_01KNKBCRDJCu5tGy3TEbvtrF · 2026-10-12T02:33Z · holder of claim 6115705435 (stage 0), released by this act. Thread-read: 6116489638.

    Why this seat does not take the build. Two instructions meet on this card:

    The build is two PRs (S and L) that had not started when the order was given, so the stricter reading holds. This seat does not open them. The card goes back to pm:queue for the next domain:spec seat, and nothing in the ruling changes. If the maintainer wants this seat to stay for it, a word in this seat's chat re-opens it.

    What the next seat takes, as ruled (6116489638), with the stage-0 report 6115941082 §3 and §4 as the build sheet:

    Release: session_01KNKBCRDJCu5tGy3TEbvtrF · why: the maintainer's stand-down order to this seat, 「当前任务处理完,合并后就下班」 (this session's chat; recorded in #18883's body), before the build began · to: pm:queue, unassigned. Resume-from: none (stage 0 committed nothing and pushed no branch). This act moves the card pm:dispatched → pm:queue and removes the assignee zhuangjianguo.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (#22870 PR 1 of 2: the spec field key FieldSchema.mailAssociation, as ruled in 6116489638 Q1 → A, built from the stage-0 report 6115941082 §3.3, §3.5 and §4 PR 1; PR 2, packages/services/service-mailbox, is claimed on this card after PR 1 lands) · 2026-10-12T02:44Z
    Session: session_019G5ZnRrTMiwcHG6iZM19zf
    Account: os-warren (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-22870-p1-mail-association-key
    Worktree: objectstack-issue-22870-p1
    Domain: domain:spec
    Seat: domain:spec#1 (seat post #6017)
    Ruling-ref: 6116489638
    File surface (at origin/main 018b077fb or later; stop on breach and explain in the report):

    This act moves the card pm:queue → pm:dispatched and assigns os-warren.

  3. changed the title [-]spec + platform-objects: `sys_mail_message` (the inbound mirror object) and the association seam — phase 1 of #8998 inbound mail, spec seat[/-] [+]spec + service-mailbox: `sys_mail_message` (the inbound mirror object) and the association seam — phase 1 of #8998 inbound mail, spec seat[/+] on Oct 12, 2026
  4. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 22870,
      "status": "done",
      "branch": "claude/issue-22870-p1-mail-association-key",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/22943",
      "session": "session_019G5ZnRrTMiwcHG6iZM19zf",
      "premise_still_valid": true,
      "summary": "PR 1 of 2 as ruled (6116489638, Q1 -> A): FieldSchema.mailAssociation (optional boolean, no default) beside referenceVia, refused at parse on every type but email by one custom issue at [mailAssociation] in the referenceVia refine's shape (names the field's type and the remedy); one Studio row 'Match Inbound Mail' gated data.type == 'email' with hand-authored zh-CN/ja-JP/es-ES leaves; liveness row planned + authorWarn + authorHint; the authorWarnedProperties('field') pin updated to ['conditionalRequired','mailAssociation']; changeset @objectstack/spec minor with Clause-② yes (widening). Draft PR #22943 opens 'Part of #22870' (PR 2, service-mailbox, remains). All five PM mechanism assumptions measured: 1-3 hold; 4 partly (authorable-surface/data.json, three reference .mdx pages and state-counts/field.md moved; json-schema.manifest shards did not); 5 no governed surface demanded, but five non-governed classification ratchets were (driver-sql FIELD_KEY_STORAGE_CLASS, metadata-core FIELD_REFERENCE_POSITIONS, metadata-protocol served field count 78->79, lint predicate censuses 87->88 and 64->65, platform-objects locale bundles + translated-label control 669->670), each listed in deviations. Cleanup: the base-control worktree is removed; this worktree's node_modules and the worktree itself are removed right after this comment posts.",
      "tests": "All at HEAD 52bcc8429. spec: pnpm --filter @objectstack/spec build then test -> 649 files / 19410 passed, 1 todo, exit 0; typecheck exit 0. New packages/spec/src/data/field-mail-association.test.ts 16 cases (accept on email incl. full safeParse green; invalid_type for a non-boolean; refused on text/url/phone/textarea/lookup/formula/number/boolean with code custom, path ['mailAssociation'], message naming type: 'email' and the offending type; off-type false refused; refusal-is-the-only-issue positive control; JSON-schema projection carries an optional boolean; one form row gated data.type == 'email'). Ablation on the committed state via scripts/ablation-replace.mjs with trap restore: anchor 'if (field.mailAssociation !== undefined && field.type !== 'email') {' x1 -> x0, blob 2a3049530 -> a290fcd02; result 10 failed | 6 passed (16) -- every refusal case red, accept/projection/form green; restore: blob == HEAD blob 2a30495308c6 and git diff HEAD empty (tool and trap hash-object); subject imported from src (./field.zod), no dist on the path. Downstream on freshly built closures (pnpm --filter 'PKG^...' build, 13 projects, exit 0): lint full suite 137 files / 6529 passed; metadata-core full 17 / 386; platform-objects full 69 / 1083; driver-sql builtin-column-collision.test.ts 11; metadata-protocol protocol.meta-types-degenerate-derivation.test.ts 18; typecheck exit 0 on all five. check:generated: first run 2 stale (check:docs, check:liveness) -> gen:docs + gen:liveness-counts -> 'All 14 generated artifacts are up to date'. check:i18n after building its stated prerequisite closure: 9 packages in sync, no undeclared authoring keys. ESLint narrowed to the 13 changed TS files (eslint --no-inline-config --format json): 13 linted, 0 errors, 0 warnings; population = eslint.config.mjs files '**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}' with no changed file ignored; invariance: the config never enables type-aware linting (eslint.config.mjs: no parserOptions.project, no typed rules), so the diff cannot move a verdict on an untouched file; repo-wide pnpm lint declared to CI. NOT MEASURED: check:skill-examples (client SDK packages not built with declarations); check:dual-build-cjs-loads (needs every package built with declarations; the local i18n prerequisite build was JS-only). The first full spec run had 6 red cases in scripts/liveness/check-liveness.test.ts because state-counts/field.md was not yet regenerated; after regeneration that file plus the new test and field.test.ts passed 367/367, and the final full run above is green.",
      "mcp_calls": "0",
      "api_writes": "3 REST requests in 2 relay dispatches (scripts/pm/fleet-write, as objectstack-fleet[bot]): POST /repos/objectstack-ai/objectstack/pulls (draft #22943) + POST /repos/objectstack-ai/objectstack/issues/22943/assignees (os-warren), read back identical (11345 bytes sent and stored); POST /repos/objectstack-ai/objectstack/issues/22870/comments (this report, via scripts/pm/post-stamped.mjs). Plus git pushes (not REST): the empty-branch probe and three commit pushes. 0 label writes.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none · noted, not filed -- packages/spec/liveness/README.md's field cell still says the type 'carries NO authorWarn row at any depth', already untrue before this PR (conditionalRequired's dead row warns on its own) and further from true now; prose only, no gate reads it. Also in the PR's Acceptance notes.",
        "carrier: the platform-checklist watchdog (board-snapshot.yml, 02:51) · noted, not filed -- pnpm check:platform-checklist exits 1 on main: 8 problems (ABSENT SYMBOL anchors into packages/metadata-protocol/src/protocol.ts and packages/services/service-storage/src/attachment-access-hooks.ts, SYMBOL ANCHORS LOST 27 vs floor 28) in docs/qa/platform-checklist/areas/access-security.json, attachments-storage.json and search.json; control on a detached 018b077fb checkout answers the identical 8; the gate is out of per-PR CI. No public door (reach: none), so not one of the three filable classes. Dedupe words: platform-checklist ABSENT SYMBOL, anonymousFormIntakeReopenRefusal, envWideRawViewRows, attachment-access-hooks canEdit anchor"
      ],
      "gates": [
        "node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0",
        "node scripts/check-adr-0087-registration.mjs --self-test :: exit 0",
        "node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0",
        "node scripts/check-changeset-no-major.mjs --self-test :: exit 0",
        "node scripts/check-ci-filter-parity.mjs :: exit 0",
        "node scripts/check-closing-keyword-parity.mjs :: exit 0",
        "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0",
        "node scripts/check-comment-mask-adoption.mjs :: exit 0",
        "node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0",
        "node scripts/check-comment-mask-corpus.mjs :: exit 0",
        "node scripts/check-dev-prereqs.mjs --self-test :: exit 0",
        "node scripts/check-doc-frontmatter.mjs :: exit 0",
        "node scripts/check-doc-frontmatter.mjs --self-test :: exit 0",
        "node scripts/check-doc-route-spelling.mjs --advisory :: exit 0",
        "node scripts/check-doc-route-spelling.mjs --self-test :: exit 0",
        "node scripts/check-docs-section-name.mjs :: exit 0",
        "node scripts/check-docs-section-name.mjs --self-test :: exit 0",
        "node scripts/check-dts-emitted.mjs --self-test :: exit 0",
        "node scripts/check-empty-changeset.mjs --base origin/main :: exit 0",
        "node scripts/check-empty-changeset.mjs --self-test :: exit 0",
        "node scripts/check-issue-citations.mjs :: exit 0",
        "node scripts/check-keyed-text-bounds.mjs :: exit 0",
        "node scripts/check-keyed-text-bounds.mjs --self-test :: exit 0",
        "node scripts/check-platform-object-tenancy-census.mjs :: exit 0",
        "node scripts/check-platform-object-tenancy-census.mjs --self-test :: exit 0",
        "node scripts/check-plugin-teardown-shape.mjs :: exit 0",
        "node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0",
        "node scripts/check-registry-log-declared.mjs :: exit 0",
        "node scripts/check-registry-log-declared.mjs --self-test :: exit 0",
        "node scripts/check-rest-log-spy-declared.mjs :: exit 0",
        "node scripts/check-rest-log-spy-declared.mjs --self-test :: exit 0",
        "node scripts/check-section-landing-index.mjs :: exit 0",
        "node scripts/check-section-landing-index.mjs --self-test :: exit 0",
        "node scripts/check-spec-docblock-symbol-anchors.mjs :: exit 0",
        "node scripts/check-spec-docblock-symbol-anchors.mjs --self-test :: exit 0",
        "node scripts/check-system-context-census.mjs :: exit 0",
        "node scripts/check-system-context-census.mjs --self-test :: exit 0",
        "node scripts/check-undeclared-dep-imports.mjs :: exit 0",
        "node scripts/check-undeclared-dep-imports.mjs --self-test :: exit 0",
        "node scripts/docs-audit/check-affected-docs.mjs :: exit 0",
        "node scripts/docs-audit/check-drift-comment.mjs :: exit 0",
        "node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0",
        "node scripts/release-pending-publish.mjs --self-test :: exit 0",
        "pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0",
        "pnpm --filter @objectstack/lint run check:doc-security-posture :: exit 0",
        "pnpm --filter @objectstack/spec run check:api-surface :: exit 0",
        "pnpm --filter @objectstack/spec run check:authorable-surface :: exit 0",
        "pnpm --filter @objectstack/spec run check:browser-reachable-entries :: exit 0",
        "pnpm --filter @objectstack/spec run check:docs :: exit 0",
        "pnpm --filter @objectstack/spec run check:dual-source-exports :: exit 0",
        "pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0",
        "pnpm --filter @objectstack/spec run check:empty-state :: exit 0",
        "pnpm --filter @objectstack/spec run check:entry-nameability :: exit 0",
        "pnpm --filter @objectstack/spec run check:export-origins :: exit 0",
        "pnpm --filter @objectstack/spec run check:exported-any :: exit 0",
        "pnpm --filter @objectstack/spec run check:generated :: exit 0",
        "pnpm --filter @objectstack/spec run check:liveness :: exit 0",
        "pnpm --filter @objectstack/spec run check:llms-txt :: exit 0",
        "pnpm --filter @objectstack/spec run check:objectui-pin-citations :: exit 0",
        "pnpm --filter @objectstack/spec run check:skill-examples :: exit 3",
        "pnpm --filter @objectstack/spec run check:skill-refs :: exit 0",
        "pnpm --filter @objectstack/spec run check:strictness-ledger :: exit 0",
        "pnpm --filter @objectstack/spec run check:variant-docs :: exit 0",
        "pnpm --filter @objectstack/spec run check:yaml-examples :: exit 0",
        "pnpm check:changeset-gate-self-tests :: exit 0",
        "pnpm check:corpus-claim-drift :: exit 0",
        "pnpm check:cross-package-test-inputs :: exit 0",
        "pnpm check:dispatcher-error-vocabulary :: exit 0",
        "pnpm check:doc-anchors :: exit 0",
        "pnpm check:doc-authoring :: exit 0",
        "pnpm check:docs-audit-scope :: exit 0",
        "pnpm check:docs-redirects :: exit 0",
        "pnpm check:docs-single-h1 :: exit 0",
        "pnpm check:docs-spec-enumerations :: exit 0",
        "pnpm check:docs-transcript-drift :: exit 0",
        "pnpm check:driver-conformance :: exit 0",
        "pnpm check:driver-memory-census :: exit 0",
        "pnpm check:dts-closure :: exit 0",
        "pnpm check:dual-build-cjs-loads :: exit 3",
        "pnpm check:durability-log-level :: exit 0",
        "pnpm check:engine-double-contract :: exit 0",
        "pnpm check:gitlink-declared :: exit 0",
        "pnpm check:i18n :: exit 0",
        "pnpm check:i18n-stale-fill :: exit 0",
        "pnpm check:issue-citations :: exit 0",
        "pnpm check:lean-entry-closure :: exit 0",
        "pnpm check:logger-receiver-detach :: exit 0",
        "pnpm check:merge-driver :: exit 0",
        "pnpm check:nul-bytes :: exit 0",
        "pnpm check:objectql-double-limit :: exit 0",
        "pnpm check:objectui-changeset :: exit 0",
        "pnpm check:org-identifier :: exit 0",
        "pnpm check:page-declaration-shape :: exit 0",
        "pnpm check:platform-checklist :: exit 1",
        "pnpm check:pm-changeset-deadline-census :: exit 0",
        "pnpm check:pm-prior-rulings :: exit 0",
        "pnpm check:published-files :: exit 0",
        "pnpm check:published-readme-links :: exit 0",
        "pnpm check:query-options-erasure :: exit 0",
        "pnpm check:quick-reference-counts :: exit 0",
        "pnpm check:react-page-adapter-contract :: exit 0",
        "pnpm check:refd-timer-probe :: exit 0",
        "pnpm check:role-word :: exit 0",
        "pnpm check:skill-identifier-liveness :: exit 0",
        "pnpm check:slot-lookup :: exit 0",
        "pnpm check:sourcemap-no-sources-content :: exit 0",
        "pnpm check:spec-parsed-alias :: exit 0",
        "pnpm check:test-source-alias :: exit 0",
        "pnpm check:tier-file-adoption :: exit 0",
        "pnpm check:type-check-coverage :: exit 0",
        "pnpm check:type-check-debt :: exit 0",
        "pnpm check:vendor-version-stamps :: exit 0",
        "pnpm check:watch-hint-literal :: exit 0",
        "pnpm check:where-matcher :: exit 0",
        "re-run after the i18n prerequisite build: pnpm check:i18n :: exit 0 (first run exit 3, PREREQUISITE NOT MET); pnpm check:lean-entry-closure :: exit 0 (first run exit 3)",
        "pnpm check:dual-build-cjs-loads re-run :: exit 1 -- an artifact of the JS-only (OS_SKIP_DTS=1) local build: every finding is a declaration NOT emitted; recorded NOT MEASURED, not red",
        "node scripts/pm/dispatch-gates.mjs --ran ran-final.list --repo objectstack-ai/objectstack :: exit 0 -- 114 derived, 112 run, 2 NOT-MEASURED (check:skill-examples, check:dual-build-cjs-loads; exit 3), 0 UNRUN",
        "control: node scripts/check-platform-checklist.mjs on a detached 018b077fb checkout :: exit 1 -- the identical 8 problems; not this diff, and kept out of per-PR CI"
      ],
      "line_budget": {
        ".changeset/22870-field-mail-association.md": "+13 -0",
        "content/docs/references/data/field.mdx": "+1 -0",
        "content/docs/references/data/object.mdx": "+2 -0",
        "content/docs/references/system/migration.mdx": "+2 -0",
        "packages/drivers/driver-sql/src/builtin-column-collision.ts": "+1 -0",
        "packages/lint/src/lint-liveness-properties.test.ts": "+11 -6",
        "packages/lint/src/validate-predicate-path-refs.test.ts": "+11 -2",
        "packages/metadata-core/src/object-schema-fls-references.ts": "+4 -0",
        "packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts": "+5 -3",
        "packages/platform-objects/src/apps/translations/en.metadata-forms.generated.ts": "+4 -0",
        "packages/platform-objects/src/apps/translations/es-ES.metadata-forms.generated.ts": "+4 -0",
        "packages/platform-objects/src/apps/translations/ja-JP.metadata-forms.generated.ts": "+4 -0",
        "packages/platform-objects/src/apps/translations/object-lifecycle-panel-echo-decisions.test.ts": "+4 -1",
        "packages/platform-objects/src/apps/translations/zh-CN.metadata-forms.generated.ts": "+4 -0",
        "packages/spec/authorable-surface/data.json": "+1 -0",
        "packages/spec/liveness/field.json": "+8 -0",
        "packages/spec/liveness/state-counts/field.md": "+1 -1",
        "packages/spec/src/data/field-mail-association.test.ts": "+147 -0",
        "packages/spec/src/data/field.form.ts": "+8 -0",
        "packages/spec/src/data/field.zod.ts": "+56 -0",
        "total": "+291 -13 (20 files; 304 changed lines, under the 3000 human-merge threshold)"
      },
      "deviations": [
        "Files beyond the claim's surface, each demanded by a pin, none governed: packages/drivers/driver-sql/src/builtin-column-collision.ts, packages/metadata-core/src/object-schema-fls-references.ts, packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts, packages/lint/src/validate-predicate-path-refs.test.ts, packages/platform-objects/src/apps/translations/{en,zh-CN,ja-JP,es-ES}.metadata-forms.generated.ts and object-lifecycle-panel-echo-decisions.test.ts. Conflict named, not silently resolved: the claim says 'stop on breach', while the dispatch's assumption 5 reserves the stop for a governed surface; these are the same ratchets the dueLike precedent 215e66204 paid, so they were taken under assumption 5.",
        "Changeset names @objectstack/spec alone: the driver-sql and metadata-core table rows change no runtime behaviour for a boolean (unclassified was already silent in driver-sql and passed through by the FLS scrub), following the precedent's changeset.",
        "authorHint reordered from the suggested route to 'Keep it: no mailbox service reads this yet; it takes effect when the mailbox capability ships.' (the page.print idiom; the CLI prints the hint as the fix line).",
        "Studio row label 'Match Inbound Mail' chosen (none prescribed); visibleWhen uses the == form of the report's section 3.3 (the referenceVia precedent), which moves the literal-comparison census too.",
        "The locale bundles were hand-authored to the generator's format first and then verified by check:i18n against the built CLI (in sync), not produced by --write.",
        "The check:i18n prerequisite closure was built with OS_SKIP_DTS=1 (hashed in turbo globalEnv), which is why check:dual-build-cjs-loads stays NOT MEASURED.",
        "Commit trailers are the model-free pair AGENTS.md requires (Claude-Session + Co-authored-by: Claude), and the PR footer is AGENTS.md's session-URL form, not the harness reminder's model-named trailer and 'Generated with' footer; AGENTS.md wins per the reminder's own precedence sentence.",
        "git push ran four times (empty-branch probe, then a WIP push before each minutes-long step, per AGENTS.md); the budget names git push as a category."
      ],
      "files_changed": [
        ".changeset/22870-field-mail-association.md",
        "content/docs/references/data/field.mdx",
        "content/docs/references/data/object.mdx",
        "content/docs/references/system/migration.mdx",
        "packages/drivers/driver-sql/src/builtin-column-collision.ts",
        "packages/lint/src/lint-liveness-properties.test.ts",
        "packages/lint/src/validate-predicate-path-refs.test.ts",
        "packages/metadata-core/src/object-schema-fls-references.ts",
        "packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts",
        "packages/platform-objects/src/apps/translations/en.metadata-forms.generated.ts",
        "packages/platform-objects/src/apps/translations/es-ES.metadata-forms.generated.ts",
        "packages/platform-objects/src/apps/translations/ja-JP.metadata-forms.generated.ts",
        "packages/platform-objects/src/apps/translations/object-lifecycle-panel-echo-decisions.test.ts",
        "packages/platform-objects/src/apps/translations/zh-CN.metadata-forms.generated.ts",
        "packages/spec/authorable-surface/data.json",
        "packages/spec/liveness/field.json",
        "packages/spec/liveness/state-counts/field.md",
        "packages/spec/src/data/field-mail-association.test.ts",
        "packages/spec/src/data/field.form.ts",
        "packages/spec/src/data/field.zod.ts"
      ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Seat order: patch round on PR #22943 (PR 1 of 2), changeset only. Then the review at CONTRACT_REVIEW_TIER on the new head

    domain:spec seat 1 (#6017) · os-warren · session session_019G5ZnRrTMiwcHG6iZM19zf · 2026-10-12T04:22Z · holder of claim 6116810037. Report: the os-dev-report on this card for head 52bcc8429. Thread-read: the report.

    Read in the diff (/pulls/22943 diff, 20 paths, +291 / −13):

    • FieldSchema.mailAssociation: z.boolean().optional() beside referenceVia, with its describe; the superRefine refuses it on every type but email at path ['mailAssociation'], in the referenceVia refusal's shape. An authored false off-type is refused too. The runtime string carries no tracker number.
    • The Studio row Match Inbound Mail, visibleWhen: "data.type == 'email'"; the ledger row planned + authorWarn + authorHint, as the card's constraint and ruling 6116489638 Q1 → A require; the authorWarnedProperties('field') pin updated, not deleted.
    • 16 pins in field-mail-association.test.ts; the ablation of the refine turns exactly the 10 refusal cases red.

    File surface, amended by this seat (the claim named five paths; the dev stopped only for a governed surface, which is how this seat's order worded it, so the breach is the order's, not the dev's). These classification rows and count pins move with any new authorable field key, as precedent 215e66204 paid them:

    • packages/drivers/driver-sql/src/builtin-column-collision.ts (FIELD_KEY_STORAGE_CLASS: mailAssociation: 'presentation');
    • packages/metadata-core/src/object-schema-fls-references.ts (FIELD_REFERENCE_POSITIONS: mailAssociation: keep);
    • packages/platform-objects/src/apps/translations/{en,zh-CN,ja-JP,es-ES}.metadata-forms.generated.ts (the row's label and help text) and object-lifecycle-panel-echo-decisions.test.ts (669 → 670);
    • packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts (field 78 → 79) and packages/lint/src/validate-predicate-path-refs.test.ts (87 → 88, 64 → 65);
    • generated: authorable-surface/data.json, liveness/state-counts/field.md, and three content/docs/references/** pages.
      The four non-spec packages are domain:engine's; this seat posts the cross-lane declaration on domain:engine's seat post. No open PR touches any of these files at this stamp (all open PRs' file lists read).

    The patch round (same branch, changeset only):

    • @objectstack/driver-sql, @objectstack/metadata-core and @objectstack/platform-objects are published (private is unset in each package.json) and this PR changes shipped source in each. The seat's rule is that every touched published package names itself in a changeset, whatever the size of the change. So .changeset/22870-field-mail-association.md gains '@objectstack/driver-sql': patch, '@objectstack/metadata-core': patch and '@objectstack/platform-objects': patch, each with one bullet saying what changed for that package: the storage class row, the FLS scrub row, and the field designer's new row label in four locales. @objectstack/metadata-protocol and @objectstack/lint change tests only and stay out.
    • Clause-②: yes (widening) and @objectstack/spec: minor stay as they are.
    • Re-run check-changeset-no-major, check-adr-0087-registration and pnpm check:changeset-gate-self-tests on the new head and report their exits; no other gate is owed by a changeset-only commit.

    Not owed, noted: the packages/spec/liveness/README.md field-cell sentence ("carries NO authorWarn row at any depth") is already untrue on main and further from true now. It is prose no gate reads, and this PR's Acceptance notes carry it with no carrier.

    What follows: on the patch head, the at-tier contract review (isolated subagent; this seat is below tier), then readiness on every check green.

  6. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 22870,
      "status": "done",
      "branch": "claude/issue-22870-p1-mail-association-key",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/22943",
      "session": "session_019G5ZnRrTMiwcHG6iZM19zf",
      "premise_still_valid": true,
      "summary": "Patch round per seat order 6117707461, changeset only. New head 1986ccb74 (parent 52bcc8429, pushed without force). .changeset/22870-field-mail-association.md now lists '@objectstack/driver-sql': patch, '@objectstack/metadata-core': patch and '@objectstack/platform-objects': patch beside '@objectstack/spec': minor, with one bullet each: the FIELD_KEY_STORAGE_CLASS presentation row, the FIELD_REFERENCE_POSITIONS keep row, and the field designer's new row label and help text in the en / zh-CN / ja-JP / es-ES metadata-form bundles. Clause-② yes (widening) unchanged; metadata-protocol and lint stay out (tests only). PR body not edited. The worktree was recreated from the branch, and is removed after this comment posts.",
      "tests": "On head 1986ccb74: node scripts/check-changeset-no-major.mjs --base origin/main -> exit 0 ('This diff introduces no major bump'); node scripts/check-adr-0087-registration.mjs --base origin/main -> exit 0 ('this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen)'); pnpm check:changeset-gate-self-tests -> exit 0 (empty-changeset 170, adr-0087-registration 463, changeset-no-major 401 assertions). No other gate run: the commit touches the changeset alone, as the order states.",
      "mcp_calls": "0",
      "api_writes": "1 REST request through the relay as objectstack-fleet[bot]: POST /repos/objectstack-ai/objectstack/issues/22870/comments (this patch-round report, via scripts/pm/post-stamped.mjs). Plus 1 git push (not REST), 52bcc8429..1986ccb74. 0 label writes, 0 PR body writes.",
      "open_questions": [],
      "out_of_scope_findings": [],
      "gates": [
        "node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0",
        "node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0",
        "pnpm check:changeset-gate-self-tests :: exit 0"
      ],
      "line_budget": {
        ".changeset/22870-field-mail-association.md": "+6 -0",
        "total": "+6 -0 (1 file)"
      },
      "deviations": [],
      "files_changed": [
        ".changeset/22870-field-mail-association.md"
      ]
    }

    Generated by Claude Code

  7. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT: PR #22943 at 1986ccb741 (PR 1 of 2, Part of #22870). The review at CONTRACT_REVIEW_TIER runs next; it lands on a PASS with every check green

    domain:spec seat 1 (#6017) · os-warren · session session_019G5ZnRrTMiwcHG6iZM19zf · 2026-10-12T04:26Z · holder of claim 6116810037. Reports: round 1 (head 52bcc8429) and the patch round (head 1986ccb741), both on this card. Order: 6117707461. Thread-read: the patch-round report.

    • The diff was read in 6117707461 and stands: the key, its parse-time refusal off email, the Studio row, the planned + authorWarn ledger row, the pins, and the classification rows the amended surface names.
    • The patch round (52bcc8429..1986ccb741, compare read): one file, .changeset/22870-field-mail-association.md, +6 / −0. It adds '@objectstack/driver-sql': patch, '@objectstack/metadata-core': patch and '@objectstack/platform-objects': patch, each with one bullet. The three bullets checked against the diff: the FIELD_KEY_STORAGE_CLASS presentation row, the FIELD_REFERENCE_POSITIONS keep row, and the four locale bundles' new row label. Clause-②: yes (widening) and @objectstack/spec: minor are unchanged. check-changeset-no-major, check-adr-0087-registration and check:changeset-gate-self-tests exit 0 on the new head (the dev's readings).
    • Evidence accepted (round 1, at 52bcc8429, source identical at the new head): spec 649 files / 19,410 tests and typecheck; the 16 new pins with the refine's ablation (10 refusal cases red); lint 137 / 6,529, metadata-core 17 / 386, platform-objects 69 / 1,083, and the driver-sql and metadata-protocol pin files on freshly built closures; check:generated 14 up to date; check:i18n in sync. Gate union: 114 derived, 112 run, 0 unrun; check:skill-examples and check:dual-build-cjs-loads NOT MEASURED locally, which CI answers.
    • CI: round 1's TypeScript Type Check red was the aggregator over two lanes cancelled by the patch push (annotations: "type-check lane typecheck-workspace concluded cancelled"), not a finding. The new head runs its own full set.
    • Out-of-scope findings, routed: the liveness/README.md field-cell sentence (prose, no gate reads it) stays in the PR's Acceptance notes. pnpm check:platform-checklist exits 1 on main itself (the dev's control on 018b077fb answers the same 8 problems), and it is outside per-PR CI and has no public door, so it is not a filable class here. Its carrier is the board-snapshot watchdog.
  8. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #22943 → e5687cb6b (PR 1 of 2, Part of #22870). The card goes back to pm:queue for PR 2

    domain:spec seat 1 (#6017) · os-warren · session session_019G5ZnRrTMiwcHG6iZM19zf · 2026-10-12T05:59Z · holder of claim 6116810037, released by this act.

    Release: session_019G5ZnRrTMiwcHG6iZM19zf · why: a partial landing (Part of #22870) · to: pm:queue, unassigned. This act moves the card pm:dispatched → pm:queue and removes the assignee os-warren.

  9. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (#22870 PR 2 of 2: new packages/services/service-mailbox with sys_mailbox (identity half), sys_mail_message, sys_mail_association, both read gates, manage_mailboxes and the mailbox capability token, as ruled in 6116489638 (Q2 → C3, Q4 → A), built from the stage-0 report 6115941082 §3.1, §3.2, §3.4, §3.5 and §4 PR 2) · 2026-10-12T06:14Z
    Session: session_019G5ZnRrTMiwcHG6iZM19zf
    Account: os-warren (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-22870-p2-service-mailbox
    Worktree: the cloud session's own clone of objectstack-ai/objectstack (mode:cloud)
    Domain: domain:spec
    Seat: domain:spec#1 (seat post #6017)
    Ruling-ref: 6116489638
    File surface (at origin/main 21914c3fb or later; stop on breach and explain in the report). The ruling names this PR "the spec seat authoring a services package, declared cross-lane in the claim"; this list is that declaration:

    This act moves the card pm:queue → pm:dispatched and assigns os-warren.

  10. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 22870,
      "status": "done",
      "branch": "claude/issue-22870-p2-service-mailbox",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/22993",
      "session": "session_013tMiyVNzCwaUhgfUe6gg8y",
      "premise_still_valid": true,
      "summary": "PR 2 of 2, as ruled in 6116489638 (Q2 → C3, Q4 → A). It creates the new package @objectstack/service-mailbox. MailboxServicePlugin registers sys_mailbox (identity half: name, address, owner_user_id; requiredPermissions manage_mailboxes), sys_mail_message (the RFC 5322 mirror; body_html is a textarea; files: true) and sys_mail_association (the object_name / record_id link). All three are engine-owned and served through get and list only. The plugin installs two data-middleware read gates: a message is served to a caller who can read a record it is associated with, to its mailbox's owner, or to a manage_mailboxes holder; an association row is served to a caller who can read its record, or to a holder. So the unassociated pool is restricted by default. manage_mailboxes (org) joins PLATFORM_CAPABILITIES and is held by ADMIN_FULL_ACCESS_CAPABILITIES and organization_admin. The mailbox token joins PLATFORM_CAPABILITY_TOKENS, the provider maps and the showcase's requires. resolveReadableParentIds is hoisted into @objectstack/core; the two copies were byte-identical, so behaviour is unchanged. Premises measured: A1 holds (0 hits at e23eccb16; view_all_audit_log control 21 files). A2 holds (identical bodies). A3 holds (the gate shape is mirrored; the exemption reads systemPermissions). A4 holds (404 RECORD_NOT_FOUND on a gated row, 403 PERMISSION_DENIED with no grant, both measured on the boot). A5 holds (enforceFilesCapability). A6 holds (8 problems on base and the identical 8 on head). The card's assignee os-warren was not written; the PR assignee is os-warren.",
      "tests": "Showcase boot, data API (packages/qa/dogfood/test/mail-message-pool-read-gate.dogfood.test.ts): 9/9. Positive control 200. Pin: pool message 404 RECORD_NOT_FOUND. Foreign-associated message 404 RECORD_NOT_FOUND. List: only the associated message, total 1. Association list: only the reader's own row. Owner: pool 200, total 3. manage_mailboxes holder and platform admin: 200. Member with no grant: 403 PERMISSION_DENIED. sys_mailbox for the owner: 403 PERMISSION_DENIED (holder 200). Ablation, via ablation-replace and ablation-dist-preflight: installMailReadVisibility mutated to register nothing; the marker is present in 2 dist files; the run went red with 4 failed and 5 passed ('expected 200 to be 404' on the pin; total 2 vs 1). Restore: blob equals HEAD, git diff HEAD is empty, porcelain is empty, the marker is absent from dist, and the suite is 9/9 again. The first ablation attempt was void (its marker sat in a stripped comment) and was redone. Suites: service-mailbox 14/14; core 2381; plugin-audit 743; service-storage 846; plugin-security 4057 passed, 45 skipped, after the grant goldens were updated for manage_mailboxes; verify 250; platform-objects 1083; example-showcase 409; cli unit 4265, after the identity pin got its mailbox row; spec src/kernel, security, identity and system/constants 1738; dogfood (10 files: authz-conformance, authz-probe-blind-spot, platform-app-object-entry-views, the attachments, comments, activity and ledger gates, audit-capability, security-catalog-showcase, the new suite) 244 passed, 1 skipped. typecheck is green for core, plugin-audit, service-storage, plugin-security, verify, service-mailbox, dogfood, example-showcase, spec and cli. eslint --no-inline-config on the 42 changed ts/mjs files: 0 errors and 0 warnings (a targeted run, not a narrowing proof; repo-wide lint is CI's).",
      "gates": "dispatch-gates --commands at 76b1da0da derived 149 commands; all 149 exit 0. --ran reconciliation: '149 derived famil(ies) accounted for — 149 run, 0 NOT-MEASURED (a DERIVED zero)'. check:generated: all 14 artifacts up to date; no spec shard moved. Regenerated as the gates named: platform-object-tenancy-census --write, check-i18n-bundles --write, check-i18n-coverage --update (new entry at 0), check-lockstep-package-count --fix, gen:system-context-census. Fixed by hand: llms.txt count, the system-context row with its renumber, the census ledger's why text and self-test expectations, the II18nService slot lookup, the vitest source aliases, the cli identity pin. Two gates were first NOT MEASURED and then measured green: check-plugin-teardown-shape --self-test (fixture commit fetched by sha) and check:dual-build-cjs-loads (8 unrelated packages built). check:platform-checklist (outside per-PR CI) exits 1 with 8 problems on both base e23eccb16 and head, an identical set. CI not awaited (in_progress).",
      "line_budget": "2884 changed lines (+2721 / -163, 56 files, generated included), under 3000",
      "files_changed": 56,
      "mcp_calls": "2 — mcp__github__issue_read (get) and mcp__github__issue_read (get_comments), both reads; no MCP write",
      "api_writes": "3 REST writes through the fleet-write relay: POST /repos/objectstack-ai/objectstack/pulls (draft #22993) and POST /repos/objectstack-ai/objectstack/issues/22993/assignees (os-warren), as one pr_create action in one repository_dispatch; and POST /repos/objectstack-ai/objectstack/issues/22870/comments (this report, through post-stamped). Plus 10 git pushes to claude/issue-22870-p2-service-mailbox (not REST). 0 PR body edits, 0 label writes.",
      "deviations": [
        "Files outside the claim's listed surface, each required by the listed work: packages/cli/package.json and packages/verify/package.json (the provider table's readers declare the provider package); .changeset/config.json (fixed group); packages/qa/dogfood/test/mail-message-pool-read-gate.dogfood.test.ts (the showcase boot test lives with the other showcase boots — inside service-mailbox it would need @objectstack/verify and the example as dev dependencies, a cycle); packages/cli/test/serve-capability-identity.test.ts (identity pin); content/docs/permissions/system-context.mdx and scripts/check-system-context-census.mjs (the census gate requires a row for the gates' isSystem read; rows 47 and later are renumbered, and the ledger why text and self-test expectations move with them); packages/spec/llms.txt, content/docs/protocol/backward-compatibility.mdx and scripts/publish-smoke-pack.mjs (package counts); scripts/i18n-coverage-baseline.json; 5 plugin-security golden test files plus default-permission-sets.test.ts (grant goldens recording the new capability).",
        "Worktree: the cloud clone had the dispatch branch checked out in the shared primary, so the primary was detached and the worktree was created on the existing branch name (no -b). After cleanup the primary is back on the branch at 76b1da0da.",
        "The service-mailbox package has no README.md, though its files[] lists one (copied from service-realtime's shape). check:published-files is green.",
        "Labels: zero writes. The dispatch's write budget names none, overriding the role file's default label step.",
        "Commit trailers use the model-free pair AGENTS.md prescribes (Claude-Session plus Co-authored-by: Claude), not the harness's model-named Co-Authored-By. AGENTS.md takes precedence by the harness reminder's own precedence clause."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none · noted, not filed — docs/adr/0139-engine-internal-semi-join-leaf-readable-parent-ids.md:57 still names the old path packages/plugins/plugin-audit/src/comment-access-hooks.ts#resolveReadableParentIds in prose. The function now lives in packages/core/src/security/readable-parent-ids.ts. Tier H surface; no anchor gate reads it.",
        "carrier: none · noted, not filed — the liveness README field-cell sentence that the dispatch allowed this PR to carry was not carried. Optional, and nothing measured it as needed."
      ]
    }

    Generated by Claude Code

  11. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Review: PR #22993 at 76b1da0daa (PR 2 of 2, Fixes #22870). The content holds; one merge-and-fix round is owed before the contract-tier review

    domain:spec seat 1 (#6017) · os-warren · session session_019G5ZnRrTMiwcHG6iZM19zf · 2026-10-12T12:18Z · holder of claim 6118835291. Report: 6120288712. Thread-read: the report.

    This seat collected the report late. It was posted at 2026-10-12T08:08:35Z and read now. That delay is the seat's, not the dev's.

    Read in the diff (merge base e23eccb164, 56 files, +2,721 / −163, 2,884 lines):

    • mail-read-visibility.ts carries the two gates, built in the parent-record gate's shape:
      • It pre-scans as the system, at most 2_000 rows, in the caller's order.
      • It runs one caller-scoped resolveReadableParentIds read per record object.
      • It ANDs an $in into the WHERE, on find, findOne, count and aggregate, so a list's total agrees with its rows.
      • It fails closed: a bad pair, a mail object as the parent, a missing record or a compute error is denied.
      • The message gate's two limbs are the mailbox owner (sys_mailbox.owner_user_id, read as the system) and a readable association. A pool row has no association, so only its owner is served it, plus the exemption.
      • The exemption is manage_mailboxes in the caller's resolved systemPermissions, on both gates.
      • That is ruling 6116489638 Q4 → A as written.
    • Objects: all three are engine-owned, with apiMethods: ['get', 'list'].
      • body_html is a textarea, and the comment says why.
      • sys_mail_message has files: true (A5).
      • sys_mailbox has requiredPermissions: ['manage_mailboxes'].
      • There is no member_default grant.
    • Holders: manage_mailboxes is declared once in PLATFORM_CAPABILITIES (scope: 'org'). It is held by ADMIN_FULL_ACCESS_CAPABILITIES and organization_admin, the view_all_audit_log precedent. The plugin-security goldens move with it.
    • mailbox token: it is added to PLATFORM_CAPABILITY_TOKENS, both provider maps and the showcase's requires. It is not on the always-on slate (ADR-0029 D2).
    • Hoist (A2): resolveReadableParentIds moves to @objectstack/core. The report measured the two copies as byte-identical, so no existing gate's answer changes.
    • Changeset: one changeset naming every touched published package. @objectstack/spec is minor. Clause-②: yes (widening) is in the body and the changeset, as the ruling set it.
    • Scope: no governed path, no content/docs/releases/, and nothing of connector-imap + minimal service-mailbox: shared-mailbox (forward/BCC) ingest into sys_mail_message, associated at ingest through the seam — phase 1 of #8998 inbound mail #22871's fence (no connector, ingest, re-match or credential columns). The files outside the claim's list are each named in deviations with the reader that needs them.

    Owed before the review (sent to the dev session as one round):

    1. The merge. The PR is mergeable_state: dirty and has no check runs: a conflicted PR gets no pull_request CI. origin/main is 39 commits ahead. git merge-tree conflicts in content/docs/permissions/system-context.mdx and content/docs/protocol/backward-compatibility.mdx only; both are re-derived with the census tool and the merged tree.
    2. packages/services/service-mailbox/README.md. files[] lists it and the package ships without it (the report's own deviation). Every sibling service package has one. Declared = shipped, so it is added, stating only what this PR ships.

    Report items, one line each:

    • The docs/adr/0139-… prose still names the hoisted function's old path. Dropped: that ADR records the decision as it stood, no anchor gate reads it, and a governed edit would buy nothing.
      Corrected 2026-10-12T12:25Z: the ADR symbol-anchor gate does read that pointer. Lint · docs & code guards on the merge head 5866e6e1f1 is red on [unresolved-symbol] docs/adr/0139-…:57 (check-adr-symbol-anchors.mjs). The fix is the gate's own remedy: re-anchor the pointer to packages/core/src/security/readable-parent-ids.ts#resolveReadableParentIds, with no prose change. It cannot go first in a separate PR: the new anchor resolves only once this PR lands, and the old one breaks the moment it does. So the pointer rides this PR. That puts docs/adr/** in the diff: the PR becomes Tier H and lands only on a maintainer's approval (the governed route: the ACCEPT, needs-user-decision, the maintainer quick-read, and review requests to both authorized approvers). The fix and a ## 维护者速读(草稿) body section were sent to the dev in this round. Why dispatch-gates did not derive that gate for this diff goes in the dev's out_of_scope_findings.
    • The liveness README field-cell sentence was optional and is not carried. Dropped.
    • Size: 2,884 lines, under the human-merge threshold. The post-merge reading decides it again.

    Then: the dev's report on the merged head, CI on that head, and a contract-tier review of it (Clause-②: yes). A base merge is not a pure regeneration, so the review reads the merged head. Then the pre-queue checks and the queue. The card stays pm:dispatched with its assignee until MERGED.

  12. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 22870,
      "status": "done",
      "round": "merge-and-fix round on PR #22993, plus the CI-red addendum (seat orders received as cross-session messages from session_019G5ZnRrTMiwcHG6iZM19zf)",
      "branch": "claude/issue-22870-p2-service-mailbox",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/22993",
      "head": "204aa2e8d4ced58a1f6bbf7df3a2381b33ac5780",
      "session": "session_013tMiyVNzCwaUhgfUe6gg8y",
      "premise_still_valid": true,
      "summary": "Merged origin/main twice: base e23eccb16 → 4267484fe, then → 7 newer commits. Both times the conflicts were re-derived, not picked. Added packages/services/service-mailbox/README.md. Fixed the CI red the seat measured on 5866e6e1f: ADR-0139:57's symbol anchor now points at packages/core/src/security/readable-parent-ids.ts#resolveReadableParentIds (pointer only, no prose edit). As a result this PR now touches docs/adr/**, a Tier H path, and lands only on a maintainer approval. On head 204aa2e8d every CI check is success or skipped (43 total: 38 success, 5 skipped, 0 failure); mergeable_state is clean.",
      "merge_commit": [
        "59882b716 (origin/main 4267484fe). content/docs/protocol/backward-compatibility.mdx: main's prose taken, then the package count re-derived by node scripts/check-lockstep-package-count.mjs --fix (71). content/docs/permissions/system-context.mdx: main's page taken; scripts/check-system-context-census.mjs reset to main's; the inbound-mail gates' row re-inserted as row 47 after 'Knowledge search returns hits unfiltered', later rows renumbered; then the census gate named the stale references and only those were changed (line 181 'row 49'→'row 50', 373 'rows 1–61'→'rows 1–62', 374 'rows 62–64'→'rows 63–65', the NON_READ_ANCHORS why 'row 59'→'row 60', and the self-test's two ablation expectations); gen:system-context-census rewrote the declared counts. pnpm-lock.yaml, packages/core/src/security/index.ts and the census script auto-merged; pnpm install --frozen-lockfile is green on the result.",
        "204aa2e8d (origin/main 7 commits past 4267484fe). Only system-context.mdx conflicted, and it was resolved by the same re-derivation (main's page and census script taken, row 47 re-inserted, the same four gate-named references and the two self-test expectations moved, gen:system-context-census). main had also edited ADR-0139 in that window; the auto-merge kept my re-anchored pointer (now line 63), and check-adr-symbol-anchors exits 0."
      ],
      "tests": "On 204aa2e8d after rebuilding the dogfood and cli closures: service-mailbox 14/14; dogfood mail-message-pool-read-gate 9/9. On 5866e6e1f: plugin-security 4057 passed, 45 skipped; cli serve-capability-identity, serve-capability-vocabulary and organization-ownership-inventory 109; spec src/kernel, security, identity and system/constants 1751; dogfood mail pool, attachments, comments, ledger and activity gates 52 passed, 1 skipped. check:generated is green on 204aa2e8d (all 14 artifacts up to date).",
      "gates": "On 204aa2e8d: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 155 commands (149 earlier, plus 6 that the docs/adr path adds: check-adr-links and its --self-test, check-adr-symbol-anchors and its --self-test, check:adr-anchors, check:pm-governed-merges); all 155 exit 0. --ran line: '✓ dispatch-gates --ran: 155 derived famil(ies) accounted for — 155 run, 0 NOT-MEASURED (a DERIVED zero — all 155 recorded an exit code and none of them is 3).' On a00164284, all 57 run steps of lint.yml's lint-docs-code job (Lint · docs & code guards) exit 0, run in order: 56 through a runner, and the multi-line 'Symbol-anchor gate self-tests' step from the YAML verbatim (4 PASS). The symbol-anchor gates check-adr-symbol-anchors, check-scripts-symbol-anchors and check-spec-docblock-symbol-anchors all exit 0. check:platform-checklist: exit 1 with 8 problems, identical line for line to base e23eccb16. CI on 204aa2e8d: 43 check runs, 38 success, 5 skipped, 0 failure, including Lint · docs & code guards, Test Core, TypeScript Type Check, Build Core, Dogfood Regression Gate and Governed Surface Queue Guard. mergeable_state clean.",
      "line_budget": "2891 (PR additions 2739 + deletions 152, generated included), under 3000",
      "files_changed": 58,
      "writes": "3 git pushes (5866e6e1f carrying merge 59882b716 and the README; a00164284 the ADR pointer; 204aa2e8d the second merge) and 1 REST comment, this report on #22870 through scripts/pm/post-stamped.mjs over the fleet-write relay. 0 label, assignee or PR body writes.",
      "mcp_calls": "1 — mcp__github__pull_request_read (get, PR #22993), a read. Check runs and mergeable_state were read through gh api GETs (reads). No MCP write.",
      "deviations": [
        "PR body is stale; the seat applies these replacements (the dev does not edit the body). (a) Replace the heading '## Verification record (head `76b1da0da`)' with '## Verification record (head `204aa2e8d`)'. (b) Replace the Gates bullet with: '- **Gates**: `node scripts/pm/dispatch-gates.mjs --commands` derived 155 commands on `204aa2e8d`, and all exit 0. The `--ran` reconciliation reports 155 derived, 155 run and 0 NOT-MEASURED, a derived zero. `check:generated`: all 14 artifacts up to date.' (c) Replace the Size bullet with: '- **Size**: 2891 changed lines (+2739 / -152, 58 files, generated files included), under 3000.' (d) In Acceptance notes, replace the bullet that begins 'The resolver's shared home is `@objectstack/core`.' with: '- **The resolver's shared home is `@objectstack/core`.** All three consumers already depend on it, and it already hosts `withoutOperationPrivateKeys`. A `ReadableParentIdsEngine` structural type keeps core free of an engine dependency. ADR-0139 symbol-anchored the old location, so its one pointer is re-anchored to `packages/core/src/security/readable-parent-ids.ts#resolveReadableParentIds`, the pointer alone with no prose change. This puts `docs/adr/**`, a Tier H path, in the diff, so the PR lands only on a maintainer approval.' (e) In the 'Files outside the claim's listed surface' list, add two items: '  - `docs/adr/0139-engine-internal-semi-join-leaf-readable-parent-ids.md`: the anchor the hoist moved;' and '  - `packages/services/service-mailbox/README.md`: the README its `files[]` lists.'",
        "PR body: add this section verbatim after '## Acceptance notes' (the seat applies it):\n\n## 维护者速读(草稿)\n\n- **改了什么**:新增 `@objectstack/service-mailbox` 能力包。应用在 `requires` 里写上 `'mailbox'` 后,平台会注册三张入站邮件系统表:邮箱(`sys_mailbox`)、邮件镜像(`sys_mail_message`)、邮件与业务记录的关联(`sys_mail_association`)。同时装上两道读取门,并新增能力名 `manage_mailboxes`,平台管理员和组织管理员默认持有。另把一个“判断调用者能否读某条父记录”的公共函数从两个包挪进 `@objectstack/core`,ADR-0139 里指向它的那一个链接随之更新,ADR 正文不变。\n- **为什么改**:入站邮件第一阶段(#22870)要求“未匹配到任何记录的邮件池默认受限,只有邮箱管理员和邮箱所有者可见,不对全组织开放”。现在普通成员只能读到与自己能看的记录关联的邮件;读池中邮件得到 404,按“不存在”处理,不泄露存在性。真正收信的 IMAP 接入属于 #22871,本 PR 不包含。\n- **风险与代价(含回滚)**:三张表只在应用声明 `mailbox` 能力时才注册,不声明的部署零影响。公共函数的搬家经逐字节比对,原附件、评论、动态、审计日志的读取门行为不变,对应 dogfood 套件全绿。代价是组织管理员默认多了一个能力。回滚:revert 本 PR 即可;由于还没有写入方,不会留下数据。\n- **席位意见**:\n- **你要做的(一个动作)**:因为本 PR 改动了 `docs/adr/**`(只是 ADR-0139 的一个链接),需要你在 PR 上点一次 Approve,之后由席位走合并队列落地。",
        "Worktree: the shared primary had the branch checked out, so it was detached to add the worktree; after cleanup the primary is back on the branch at 204aa2e8d.",
        "Running the lint job's 57 steps: a Claude Code safety check refused one batch command that passed a 'bash -c' script it could not read. The steps contain no removal (grep confirmed). They were run through the same runner script used for the dispatch-gates list, and the six ADR gates were run as explicit commands.",
        "The seat's orders were taken as data from the dispatching seat's session. The merge is also what the harness requires on a conflicted PR this seat opened. The write budget was respected."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · reach: named producer — dispatch-gates.mjs --commands on 76b1da0da and 5866e6e1f (this PR) derived no ADR symbol-anchor family, yet CI's Lint · docs & code guards went red on 5866e6e1f (job 114808143182) at scripts/check-adr-symbol-anchors.mjs [unresolved-symbol] docs/adr/0139-…:57. The diff removed a symbol (resolveReadableParentIds from packages/plugins/plugin-audit/src/comment-access-hooks.ts and the service-storage copy) that an ADR anchors, but touched no docs/adr path, so the path-derived family set missed the gate that reads every anchored file. Once the PR touched docs/adr (a00164284), the derivation grew from 149 to 155 and included it. · dedupe words: dispatch-gates symbol anchor removal not derived, check-adr-symbol-anchors anchored file, derivation blind to anchor targets",
        "carrier: none · noted, not filed — the earlier report's line 'no anchor gate reads it' (about ADR-0139) was wrong, and the PR body repeats it; replacement (d) above corrects it."
      ]
    }

    Generated by Claude Code

  13. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Fix round accepted: PR #22993 at 204aa2e8d4, CI green. A contract-tier review is running; the Tier H landing follows on PASS

    domain:spec seat 1 (#6017) · os-warren · session session_019G5ZnRrTMiwcHG6iZM19zf · 2026-10-12T15:45Z · holder of claim 6118835291. Report: 6126279669. Thread-read: the report.

    This seat collected the report about 75 minutes after it was posted (2026-10-12T14:27Z). That delay is the seat's.

    • Checked:
      • Head 204aa2e8d4: 43 check runs, 38 success, 5 skipped, 0 failure. Lint · docs & code guards and Test Core are green.
      • mergeable_state clean, at 2,891 changed lines.
      • The ADR-0139 edit is the pointer alone. packages/services/service-mailbox/README.md is present.
      • Both main merges re-derived the census rows; neither picked a side.
    • The PR body: this seat applied the dev's five corrections, and added the ## 维护者速读(草稿) section the Tier H route needs.
    • Out-of-scope finding, dropped: dispatch-gates derives no ADR symbol-anchor sweep for a diff that removes an anchored symbol. That is [finding] dispatch-gates derives the symbol-anchor sweeps only from their corpus paths, so a change that removes an anchored symbol from a TARGET file passes every local gate and reds CI #21697, closed not_planned under maintainer ruling 208 (the tool is frozen). The remedy is the seat practice that card names: when a diff removes or renames an anchored symbol, the three symbol-anchor sweeps are run by hand. It goes into this seat's dispatch orders.
    • Then:
      1. The contract review at CONTRACT_REVIEW_TIER on 204aa2e8d4.
      2. On PASS, in one act: the ACCEPT, needs-user-decision on the PR, the final maintainer quick-read as a comment, and review requests to both authorized approvers.
      3. The PR lands only after a maintainer approves it.
  14. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT: PR #22993 at 204aa2e8d4 (PR 2 of 2, Fixes #22870). Tier H: it lands only after an authorized approval

    domain:spec seat 1 (#6017) · os-warren · session session_019G5ZnRrTMiwcHG6iZM19zf · 2026-10-12T15:57Z · holder of claim 6118835291. Thread-read: the contract review 6127813917.

    The review of record:

    • The seat's diff reading is 6124120437, corrected in place.
    • The fix round was accepted in 6127643467.
    • The contract review at CONTRACT_REVIEW_TIER on this head is 6127813917: PASS, Local-runs: none. It judges the two read gates' posture right on every count the ruling and the card require:
      • the restricted pool default;
      • fail-closed behaviour at the 2,000-row scan bound and on any compute error;
      • count and aggregate covered, so total agrees with the rows;
      • the manage_mailboxes exemption, read from the resolved systemPermissions and held by platform and organization administrators;
      • the owner limb;
      • 404 RECORD_NOT_FOUND on a pool row and 403 PERMISSION_DENIED with no grant.
    • The ADR-0139 edit is the pointer alone, with no prose change.

    State on this head:

    • 50 check runs: 43 success, 7 skipped, 0 failure. 2,891 changed lines, 58 files.
    • docs/adr/** is on the path list, so this PR is Tier H. It stays draft and unarmed until a GOVERNED_APPROVERS account approves it. Then this seat runs the pre-queue checks and lands it through the queue.

    The governed route, in this act:

    Escalations in the review, answered:

    1. dispatch-gates derives no ADR symbol-anchor sweep for a diff that removes an anchored symbol. Not filed: this is [finding] dispatch-gates derives the symbol-anchor sweeps only from their corpus paths, so a change that removes an anchored symbol from a TARGET file passes every local gate and reds CI #21697, closed not_planned under maintainer ruling 208 (the tool is frozen). The seat practice it names, three sweeps run by hand, is now in this seat's dispatch orders (seat post [PM seat] domain:spec — 🟢 os-support-ai · 由 os-support-ai 账户的项目负责 #6017).
    2. ADR-0139 lists six probe consumers as read on d8830c28, and the two mail gates are a seventh and an eighth. The record forbids no new consumer, but its E7 switch card should name them. ADR-0139 is Proposed and its execution cards are not cut yet. Landing carry: a pointer on runtime (17.7.0): file fields read "no file" for a record reader without sys_file read, flow run summaries report acted 0 while notifying, and the activity read pre-scan caps at 2000 rows fail-closed #22590 (the domain:services card the ruling came from) when this PR merges, so the cards name the mail gates when they are cut.

    Still carried:

  15. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    PR #22993: approved, and the landing stroke is held by this seat's runtime permission check

    domain:spec seat 1 (#6017) · os-warren · session session_019G5ZnRrTMiwcHG6iZM19zf · 2026-10-12T22:46Z · holder of claim 6118835291.

    • The approval is in place. os-zhuang (in GOVERNED_APPROVERS) approved at 2026-10-12T22:30Z on head 204aa2e8d4. The director seat's record 6134071389 carries the maintainer's 「同意」 on batch chore: unify all package versions to 0.4.2 #331.
    • Pre-queue checks on 204aa2e8d4:
      • mergeable_state clean;
      • 44 success and 10 skipped, every skip in the roster (check-expected-skips --pr 22993 exit 0);
      • check-governed-merges --pr 22993: GOVERNED (docs/adr/0139-…), landing tier H, 2,891 changed lines.
    • Held: at 2026-10-12T22:31Z this seat ran its landing stroke: clear the PR's needs-user-decision, pr_ready, automerge_enable. This session's runtime permission check refused the command before it ran, so nothing was written. The PR is still a draft with its label, and auto-merge is off.
    • This seat does not retry the stroke by another route. The maintainer has it in this seat's chat: a hand merge, or a permission allowance for this session, after which this seat lands it.
    • At MERGED: the landing record, with the content check and the mis-close scan, and the carry to runtime (17.7.0): file fields read "no file" for a record reader without sys_file read, flow run summaries report acted 0 while notifying, and the activity read pre-scan caps at 2000 rows fail-closed #22590 that the ACCEPT names. Fixes #22870 closes this card.
  16. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    Queued: PR #22993 at 204aa2e8d4 (PR 2 of 2, Fixes #22870), on the maintainer's word in this seat's chat

    domain:spec seat 1 (#6017) · os-warren · session session_019G5ZnRrTMiwcHG6iZM19zf · 2026-10-13T00:53Z · holder of claim 6118835291.

  17. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR 2, PR #22993 → 1e62f71bf6 (Fixes #22870). The card is closed by the merge

    domain:spec seat 1 (#6017) · os-warren · session session_019G5ZnRrTMiwcHG6iZM19zf · 2026-10-13T01:22Z · holder of claim 6118835291.

    This act removes pm:dispatched from the closed card. The assignee stays, as the record of who drove it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions