You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
chore(deps): upgrade @objectstack/* past 17.7.0 once the next release publishes — it carries the platform fixes #2016, #2000, #1583 and #1585 wait on #2031
Restart-when: npm view @objectstack/cli version prints a version above 17.7.0
Filing gate: ③ a task the maintainer set. The maintainer answered 「同意」 on 2026-10-09 to the decision batch hotcrm-R74b, whose item 1 ruled D for #2016: wait until the platform fix is released and hotcrm is upgraded, then build it. Its post-ruling step, as presented: 「立一张『升级平台版本』的杂事卡,#2016 转为阻塞,挂在这张卡上」. Filed by the repo:hotcrm seat, session_012zh91QzFgePbkmuHnugLN3.
Who acts on it: the repo:hotcrm seat dispatches it when the Restart-when: line above holds. ⛔ Not a claim.
Hold: 2026-10-09, seat grading under that ruling. hotcrm pins @objectstack/* 17.7.0 (every package, exact versions), and 17.7.0 is the newest release on npm today, so there is nothing to upgrade to yet.
What the upgrade carries
Each row is an objectstack card closed after 17.7.0 published (2026-10-06), and the hotcrm card that waits on it. The dev checks each fix is really in the release it installs (the package CHANGELOG or the source), and reports per row.
#2016 unblocks only when BOTH are in the installed release: #22310 shuts the trigger door, #22407 the action door and the declared flow endpoint. If #22407 is missing, #2016 re-blocks on the next upgrade
filed by this lane; nothing to do unless the release notes say so
Scope
Bump every @objectstack/* pin together to the newest release. Use the repo's own upgrade path, and regenerate the lockfile with its tooling. ⛔ Never hand-edit a lockfile.
Read the release's upgrade notes, and fix what breaks. Record every breakage in the PR body (what broke, the exact error, the upstream change, the fix), as the 17.7.0 upgrade did (chore(deps): upgrade @objectstack/* to 17.7.0 #2008).
pnpm verify is green. Browser-verify that the app boots and serves a record page, in place.
⛔ The waiting cards' own fixes are NOT in this PR. The seat dispatches each one after this lands.
Duplicate check
gh search is refused in this container (GraphQL and REST search answer 403), so every hotcrm issue was listed into a local index (/issues?state=all, through today) and matched case-insensitively: upgrade 17.8 0; 17.7.0 → 17.8 1 (the seat post #1353 only); pin bump 37, none an open upgrade card. None is a duplicate.
Claim: PM loop round R82
Session: session_01BCDqr1DqnQjEh5jA9gGkUG
Account: os-justin (the seat's linked user as GET /user answers it; always the card's assignee)
Branch: claude/issue-2031-objectstack-18-next-0
Worktree: hotcrm-issue-2031
Domain: repo:hotcrm (single-lane repo, no domain:* taxonomy)
Seat: repo:hotcrm#1
File surface: package.json (every @objectstack/* pin, together), pnpm-lock.yaml (regenerated by pnpm install, never hand-edited), objectstack.manifest.json (specVersion, engines.protocol); whatever the 18.0.0-next.0 breaking changes force in src/**, test/**, scripts/** (each breakage named in the PR body); the old-pin claims 17.7.0 re-scoped per AGENTS.md "Platform Upgrades" step 5 across src/**, test/**, docs/**, content/docs/**, README*; test/helpers/identity-objects.ts retired only if objectstack#22074 ships in the installed release; one .changeset/2031-*.md. ⛔ Not AGENTS.md, CLAUDE.md or .claude/** (governed; a stale claim there is reported, not edited). (stop on breach; explain in the report)
Container & model: M (a major-line prerelease: judgment on each breaking change), mode:subagent, model: default (dispatch-gates --tier --repo objectstack-ai/hotcrm over package.json pnpm-lock.yaml objectstack.manifest.json: "no path-derived mandate: the surface hits none of the 3 declared glob(s)"; the tier stays the PM's per-card call)
Clause-②: no
Responsibility: n/a — not a defect card
Thread-read: none
Serial constraints cleared: in flight in this lane: none (0 pm:dispatched before this card, 0 seat PRs). Open PRs touching the same files: dependabot #2063 (fumadocs-core) and #2064 (@playwright/test), both on package.json + pnpm-lock.yaml; whichever lands second regenerates the lockfile. Token ratchet headroom at the brief: src/revenue ~14, src/service ~89; a pin-claim re-wording inside a ceilinged package must not grow it. Queued cards on the seat post's hot-file queues (§5) start after this lands, on the new pin.
Dispatch authority (maintainer direct order, ahead of the queue): the card's own Restart-when: line (npm view @objectstack/cli version prints a version above 17.7.0) does not hold: latest is still 17.7.0. 18.0.0-next.0 was published to the next dist-tag at 2026-10-13T08:28Z, and every one of the 24 @objectstack/* packages hotcrm pins resolves at that exact version. The maintainer ordered this upgrade in this session's chat after summoning the seat with /pm-dispatch hotcrm, verbatim: 「首先处理 objectstack 升级到 18.0.0-next.0」. The target therefore is 18.0.0-next.0 exactly, not "the newest release".
{
"issue": 2031,
"status": "blocked",
"branch": "claude/issue-2031-objectstack-18-next-0",
"pr": null,
"session": "session_01BCDqr1DqnQjEh5jA9gGkUG — the PM seat's session (this dev ran as its subagent)",
"premise_still_valid": true,
"summary": "BLOCKED on the src/revenue token ceiling (one decision only the maintainer can make). 18.0.0-next.0 refuses the single-brace template dialect in every flow VALUE slot and TEXT slot (a87d8be..2b61f2d family: 0e9e7b7, 615c696, 111ffff, 12b9daf, f66fdc7, 9fdb127, 968acc8, d85615d, 6a06e3b). `os validate`, `os lint` and `pnpm dev` all refuse the stack on 340 sites in 32 flows (sales 213, revenue 71, service 33, marketing 23). The dev server exits with `Compile failed — fix errors above before starting dev server`. Upstream ships no D2 conversion for these sites (`no authored spelling maps losslessly`), so each site needs the CEL envelope, plus a decision about what happens when the value is absent. Measured on a scratch copy, with the most compact spelling the spec offers (the `cel` tagged template) and NO has() guards, src/revenue's authored total is ~19,040 against a ~19,000 ceiling (over by ~40, headroom today ~14). With guards on every optional dotted read, revenue is ~19,234 authored (over ~234) and ~16,094 business semantics (over ~94), and src/service is ~22,010 (over ~10). The dispatch rules out raising a ceiling, so I stopped there. Landed on the branch (be03a57, pushed, no PR): all 24 pins moved to 18.0.0-next.0 with the lockfile regenerated by pnpm install; engines.protocol and specVersion moved to ^18.0.0-next.0; the retired email_template `category` removed (ec7f7f8); test/hooks-runtime.test.ts follows hooks.run's operation-generic type (a6f3a13). Every upstream fix row on the card IS in 18.0.0-next.0 (table below), so the waiting cards' blocker becomes this ceiling decision, not availability. Further breakages measured for the resume: 96 i18n/missing-flow keys (screen select options and descriptions are now translatable, and translations are outside the ratchet), and 75 failing test files. Of those, 47+ fail because package-declared permission sets no longer get sys_permission_set rows (c8a9783, test/helpers/verify-stack.ts signUpPerson). Others fail on the ADR-0131 refusal of org-less system writes (SystemWriteOrganizationRequiredError), on flow refusal, on `Object ... not found` in reduced kernels, and on docs-declared-versions (pin claims not yet re-scoped).",
"gates": [
{"cmd": "pnpm validate (lock)", "head": "36a4669", "verdict": "os-verify-lock: VERDICT command-exit 1", "exit": 1, "reading": "defineStack validation failed (8 issues): emailTemplates.N.category retired"},
{"cmd": "pnpm validate (lock)", "head": "be03a57 src tree (committed after the run; the later commit touched only test/hooks-runtime.test.ts)", "verdict": "os-verify-lock: VERDICT command-exit 1", "exit": 1, "reading": "340 expression-invalid (317 value-slot, 23 text-slot) in 32 flows"},
{"cmd": "pnpm typecheck (lock)", "head": "be03a57 minus the hooks-runtime edit", "verdict": "os-verify-lock: VERDICT command-exit 1", "exit": 1, "reading": "test/hooks-runtime.test.ts(49,3): error TS2322"},
{"cmd": "npx tsc --noEmit (NOT under the lock, deviation 1)", "head": "be03a57 tree", "verdict": "exit 0, no output", "exit": 0},
{"cmd": "pnpm lint (lock)", "head": "be03a57", "verdict": "os-verify-lock: VERDICT command-exit 1", "exit": 1, "reading": "Errors (340) expression-invalid; ✗ 96 warning(s) fail this run under --strict"},
{"cmd": "node scripts/check-lint-i18n-gate.mjs", "head": "be03a57", "verdict": "✗ i18n lint gate: 96 `i18n/missing-*` issue(s) found — must be zero.", "exit": 1},
{"cmd": "node scripts/check-source-hygiene.mjs", "head": "be03a57", "verdict": "✓ source hygiene clean", "exit": 0},
{"cmd": "node scripts/check-source-token-ratchet.mjs", "head": "be03a57", "verdict": "✓ source token ratchet clean (revenue authored ~18,986 / ~19,000, headroom ~14; service authored ~21,913 / ~22,000, headroom ~87)", "exit": 0},
{"cmd": "pnpm build", "head": "be03a57", "verdict": "NOT MEASURED: build, reason: the same author-time rules refuse the stack (validate red); pnpm dev reports `Compile failed`", "exit": null},
{"cmd": "pnpm test (lock)", "head": "be03a57", "verdict": "os-verify-lock: VERDICT command-exit 1 · held the lock 523s", "exit": 1, "reading": "Test Files 75 failed | 104 passed (179); Tests 111 failed | 2231 passed | 1349 skipped (3691); Errors 18"},
{"cmd": "pnpm dev (fresh DB, PID 5999)", "head": "be03a57", "verdict": "✗ Compile failed — fix errors above before starting dev server; ELIFECYCLE exit 1 (process exited by itself, nothing left running)", "exit": 1},
{"cmd": "browser verification (record page, dashboard)", "head": "be03a57", "verdict": "NOT MEASURED: browser, reason: the app does not boot on 18.0.0-next.0 until the flow migration lands", "exit": null},
{"cmd": "pnpm test:e2e / docs build / changeset-check", "head": "be03a57", "verdict": "NOT MEASURED: e2e, docs, changeset, reason: no boot and no PR; declared to CI on resume", "exit": null}
],
"tests": "See `gates` for every verdict line. Measurements behind the block: (1) Ratchet lower bound: a scratch copy of src/ + scripts/ (never the worktree) had every one of the 340 refused sites rewritten to the CLI's own suggested CEL spelling, wrapped in the spec's `cel` tagged template (`'{x.y}'` becomes cel`x.y`, +2 chars), with no has() guards. `node scripts/check-source-token-ratchet.mjs` on that copy: exit 1, `✗ src/revenue authored total is ~19,040 tokens; the ratchet ceiling is ~19,000 (over by ~40)`; service authored ~21,957 (headroom ~43); sales BS ~57,596; marketing BS ~8,027. (2) Guarded estimate: the same rewrite with `has(a.b) ? a.b : null` on every dotted read that is not `.id` gives exit 1: revenue authored ~19,234 (over ~234), revenue BS ~16,094 (over ~94), service authored ~22,010 (over ~10); sales BS ~58,248 (headroom ~752). (3) Completeness: that guarded rewrite was copied over the worktree's src under `trap 'git checkout HEAD -- REPO_ROOT/src' EXIT` and validated under the lock: `os-verify-lock: VERDICT command-exit 1`. 6 errors remained, all residue of the measurement codemod (4 from `cel` JSON-quoting an interpolated `${key}` and 2 filter operands in the shared _billing-endpoint.ts that it did not reach). No further rule family hides behind the 340. Restore proven: `git diff HEAD` empty, `git status --porcelain` 0 lines, blob hash 2c26093d equal to HEAD's for src/revenue/flows/contract-renewal.flow.ts. The first attempt was a no-op (`rsync: command not found`, still 340 errors), so that reading was discarded and the copy redone with cp. (4) Handshake: a probe of the installed @objectstack/metadata-core checkProtocolCompat gives `^17.7.0 incompatible 18`, `^18.0.0-next.0 ok 18`, `^18 ok 18`, `^18.0.0 ok 18`. rangeAdmitsMajor compares only the leading major (`^` means runtimeMajor === maj), and PROTOCOL_VERSION = \"18.0.0\" in the installed spec. hotcrm's test/docs-declared-versions.test.ts requires bare(specVersion) == the installed @objectstack/spec version and engines.protocol == specVersion, so the spelling is ^18.0.0-next.0, not the ^18 that `os migrate meta --write` would write. (5) Lockfile, by pnpm install: packages-section keys 790 to 791 (pnpm reported `resolved 705`); 54 @objectstack keys moved 1:1, plus 1 new transitive @objectstack/service-mailbox@18.0.0-next.0 (pulled in by cli and verify); no third-party key added, removed or moved. The install's peer warning `better-auth 1.7.3 unmet peer better-sqlite3@^12.0.0: found 13.0.3` predates this change (same pair in the 0f84c9c lockfile).",
"upstream_rows": [
"upstream | in 18.0.0-next.0? (evidence) | hotcrm card",
"objectstack#22310 (PR #22404) | yes: ca135dc in runtime CHANGELOG 18.0.0-next.0; REST compare ca135dcc...d889448b (release commit of every @objectstack/*@18.0.0-next.0 tag) = ahead 413 behind 0 | #2016",
"objectstack#22407 (PR #22424) | yes: c572fe2 in runtime CHANGELOG; compare ahead 405 behind 0 | #2016 (both doors present, so #2016's two-fix condition holds)",
"objectstack#22178 (PR #22213) | yes: 0e9371f in service-analytics CHANGELOG; ahead 520/0 | #2000",
"objectstack#22212 (PR #22281 + #23146) | yes: d1dbe70 in lint, 02bec6c in spec CHANGELOG; ahead 487/0 and 20/0 | #1583",
"objectstack#22238 (PR #22285) | yes: 6d2da32 in cli CHANGELOG; ahead 482/0 | #1585",
"objectstack#22074 (c6fe02d, createIdentityObjectsPlugin preset) | yes: c6fe02d in plugin-auth + plugin-security CHANGELOG, and createIdentityObjectsPlugin is exported by the installed plugin-auth dist; ahead 535/0 | test/helpers/identity-objects.ts retirement NOT done here (blocked before it); safe to do on resume",
"objectstack#22149 (PR #22294) | yes: 9ad3d24 in spec CHANGELOG; ahead 473/0 | —",
"objectstack#22189 (PR #22285) | yes: 6d2da32 in cli CHANGELOG; ahead 482/0 | —",
"objectstack#22067 (e67ba80, c8d06a9) | yes: plugin-security / objectql CHANGELOG; ahead 578/0, 555/0 | —",
"objectstack#22071 (bafb58b, 4935c66) | yes: cli / plugin-auth CHANGELOG; ahead 574/0, 567/0 | —",
"objectstack#22072 (PR #22108 + #22115) | yes: a959493, cdeabec in cli/spec CHANGELOG; ahead 563/0, 558/0 | —",
"objectstack#22073 (4935c66) | yes: cli CHANGELOG; ahead 567/0 | —",
"objectstack#22079 (dd39171, d7c5c33) | yes: cli / spec CHANGELOG; ahead 566/0, 541/0 | —",
"objectstack#22824 (PR #22850) | yes: 69d4218 in plugin-approvals/lint/spec CHANGELOG; ahead 168/0 | per seat post",
"objectstack#22455 (PR #22513 + #22492/#22523/#22535) | yes: ce3d0ad in service-storage/plugin-security/plugin-audit CHANGELOG; ahead 360/0 | per seat post",
"objectstack#22437 (PR #22462) | yes: 7806a14 in rest CHANGELOG; ahead 383/0 | per seat post",
"objectstack#22607 (PR #22625) | yes: e8c6666 in plugin-approvals CHANGELOG; ahead 309/0 | per seat post",
"Note: the CHANGELOGs cite commit hashes, not these issue numbers (`#22310` greps 0 hits in every installed CHANGELOG); every row was matched by its fix commit's short hash."
],
"breakages": [
"1 FIXED · os validate: `✗ emailTemplates.0.category: email_template.category was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove)` x8 (68 more in the second package). Upstream ec7f7f8 (#22876, conversion email-template-category-is-system-removed). Fix: src/sales/email-templates/_bundle.ts writes no `category`.",
"2 FIXED · protocol handshake: `manifest.engines.protocol '^17.7.0' does not admit protocol 18, so the load refuses this stack` (os migrate meta). Upstream 4e9fe9f (PROTOCOL_VERSION 18.0.0). Fix: ^18.0.0-next.0 in src/sales/index.ts:93, src/service/index.ts:138 and objectstack.manifest.json (specVersion + engines.protocol).",
"3 FIXED · typecheck: `test/hooks-runtime.test.ts(49,3): error TS2322: Type 'Promise number|boolean|EngineRow' is not assignable to type 'Promise Rec'`. Upstream a6f3a13 (verify hooks.run generic over the operation). Fix: repWrites is generic over Op.",
"4 OPEN (the blocker) · os validate / os lint / pnpm dev: 340 `expression-invalid`, e.g. `A value slot no longer reads the {…} template dialect … Write {record.id} as { dialect: 'cel', source: 'record.id' }` and `A flow text slot reads double-brace template holes (ADR-0032 §3), not the single-brace {…} dialect`. Upstream 0e9e7b7 + 615c696 + 111ffff + 12b9daf + f66fdc7 + 9fdb127 + 968acc8 + d85615d + 6a06e3b + 2b61f2d. No D2 conversion exists. Upstream measured hotcrm itself at c529de2 (91 create/update/assignment sites). Per-package site counts: sales 213, revenue 71, service 33, marketing 23. The shared src/sales/flows/_billing-endpoint.ts carries the billing payload sites both handoff flows use. Filter operands that may be absent need a `decision` route rather than a has() guard (the refusal says so).",
"5 OPEN · lint:i18n-gate: 96 i18n/missing-flow (32 keys x zh-CN/es-ES/ja-JP: screen select options, screen and refusal descriptions in campaign_enrollment, lead_conversion, schedule_followup, quote_generation, claim_case). #2008 recorded these as untranslatable on 17.7.0 (FLOW_SCREEN_FIELD_NO_OPTIONS); 18.0.0-next.0 makes them keys. Translations are outside the ratchet.",
"6 OPEN · tests, 47 files plus 6 mixed: `Error: no permission set named \"sales_rep\" is registered on this stack` at test/helpers/verify-stack.ts:145. Upstream c8a9783 (`no seeder or materializer writes a sys_permission_set row`); the grant is now written by name (`permission_set`). Fix site: signUpPerson.",
"7 OPEN · tests, 3 files plus others: `SystemWriteOrganizationRequiredError: Insert on 'crm_account' was REFUSED: a system-context write on a tenant-scoped object must carry an organization … this install declares the 'single' tenancy posture but holds NO organization` (ADR-0131). The message names the cause `a composition that never booted the auth plugin`. Not yet attributed: harness or platform. To be measured on resume before any classification.",
"8 OPEN · tests: `Object 'crm_quote' / 'crm_opportunity' not found` (4-6 files with reduced kernels), `already has a live bootStack kernel` (cascade from failed teardowns), MemoryMultiTenantUnsupportedError (unassigned-case-triage-reach), docs-declared-versions (pin claims not re-scoped yet), lint-i18n-gate test (= item 5). Not attributed yet."
],
"files_changed": [
"package.json (24 pins 17.7.0 to 18.0.0-next.0)",
"pnpm-lock.yaml (pnpm install)",
"objectstack.manifest.json (specVersion, engines.protocol)",
"src/sales/index.ts (engines.protocol)",
"src/service/index.ts (engines.protocol)",
"src/sales/email-templates/_bundle.ts (retired category removed; two comment lines)",
"test/hooks-runtime.test.ts (repWrites generic)"
],
"deviations": [
"1 · Three runs went outside the shared verify lock: `npx tsc --noEmit` (about 12s), `npx objectstack migrate meta --from 17`, and the first `npx objectstack validate --json`. Every other validate, typecheck, lint and test run went through os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=hotcrm-2031.",
"2 · No PR was opened. The dispatch says to report blocked with the numbers when a required change cannot fit a ceiling, and to report blocked rather than land a red PR when the boot is blocked. The work is pushed: Resume-from: claude/issue-2031-objectstack-18-next-0@be03a57.",
"3 · Commit trailers use this repo's model-free pair (`Claude-Session:` + `Co-authored-by: Claude`), not the harness reminder's model-named Co-Authored-By line, as the dev definition requires.",
"4 · The handshake spelling is ^18.0.0-next.0, not the ^18 that `os migrate meta --write` prescribes. Both are admitted (probe above). This repo's own test/docs-declared-versions.test.ts pins specVersion to the installed spec version, and engines.protocol to specVersion.",
"5 · Conflict noted, not resolved silently: AGENTS.md Platform Upgrades step 1 says to read the curated release notes and not to work from changelogs. The v18 page says curated notes are not written yet and the changesets are the record. The installed CHANGELOGs were used, as the dispatch directed. Governed text, not edited.",
"6 · `os migrate meta --from 17` still lists 15 decision nodes for mode 'inclusive' (flow-decision-mode-inclusive-explicit). That is a 17.5.0 change (0283cb9) the earlier upgrades reviewed and left unwritten, not an 18.0.0-next.0 breakage, so none were written here.",
"7 · Writes: git push x3 (empty branch, 36a4669, be03a57); 0 pr_create; 1 os-dev-report comment. 0 label writes."
],
"mcp_calls": "0",
"api_writes": "1 — POST /repos/objectstack-ai/hotcrm/issues/2031/comments (this os-dev-report, via scripts/pm/post-stamped.mjs and the fleet-write relay). Reads only otherwise (gh api GET on issues, pulls, timeline, compare, commits, contents).",
"open_questions": [
{
"question": "The 18.0.0-next.0 flow migration (340 sites; no lossless conversion upstream) cannot fit src/revenue's token ceiling. Even the most compact unguarded spelling is over by ~40 tokens on authored total (19,000). A realistic guarded one is ~+250 tokens (authored ~19,234, business semantics ~16,094 against 16,000), and src/service goes ~10 over 22,000. How should it be paid?",
"options": [
"A · A maintainer ruling re-anchors THIS ceiling for src/revenue (authored and business semantics), and for src/service authored if the final count needs it, at the number the finished migration measures, quoted in the upgrade PR as the ratchet's own message prescribes. Real need: business need is real (the waiting cards #2016/#2000/#1583/#1585 need 18.0.0-next.0, and every row is in it); the growth is forced by a platform contract, not by new capability. Long term: the CEL envelope is the single dialect going forward, so this is not a workaround. AI-safety: the envelope plus explicit absence guards is the stricter, loudly-refused form. Startup scope: the README headline is about src/sales, which stays inside its own ceilings (guarded estimate BS ~58,248 of 59,000), so the headline claim does not move. Effort: one large dev card (migration + translations + harness + docs).",
"B · No ceiling moves. Before the upgrade, a separate card compresses src/revenue (and src/service) by at least the measured growth (dedupe shared flow payloads, hoist repeated subject text), then the upgrade lands. Real need: same. Long term: shrink-only is honoured, but the cut (~1.5% of revenue's business semantics) is unscoped and may be hard to find without deleting behaviour. AI-safety: neutral. Startup scope: adds a refactor card in front of the upgrade. Effort: unknown; feasibility not measured.",
"C · Stay on 17.7.0 until objectstack offers a terser null-tolerant read for value slots, or until v18 GA, and ask upstream for it (AGENTS.md §4: a per-site tax is fixed at the default). Real need: the waiting cards stay blocked although their fixes are released. Long term: depends on an upstream decision that is not filed. AI-safety: neutral. Startup scope: lowest work now. Effort: a filing, plus waiting."
],
"recommendation": "A, because the growth is a platform-forced contract migration, not new capability. The ratchet itself names a ruling-quoted re-anchor as the route. The headline package (src/sales) stays inside its ceilings. B has unmeasured feasibility, and C keeps four released fixes out of reach. Second question for the seat: the remaining work is large (340 flow sites, each needing an absence decision, plus 96 translations, a 75-file test-harness migration and the pin-claim sweep). The seat may want to dispatch it under one claim but with an M-or-larger tier and the ADR-0131 harness item measured first."
}
],
"out_of_scope_findings": [
"carrier: whoever lands the upgrade · noted, not filed: at be03a57 the ratchet prints `src/service interaction layer … headroom is 1,384 tokens, over twice the 5% buffer — re-anchor this ceiling to ~6,000`. It is an advisory to lower a ceiling, and nothing here touched it.",
"carrier: maintainer (governed file) · noted, not filed: AGENTS.md Platform Upgrades step 1 (read curated release notes, not changelogs) has no reading for a prerelease line whose curated notes do not exist yet. The v18 page says the changesets are the record."
]
}
Release: session session_01BCDqr1DqnQjEh5jA9gGkUG · reason: the dev stopped on a token ceiling that only a maintainer ruling can move · destination: needs-user-decision (this card is the anchor)
Resume-from: claude/issue-2031-objectstack-18-next-0@be03a574f862dbc0c2421c485cf1813891eb6ffa
Verdict on the os-dev report 6142372109 — ESCALATE (decision), pr: null. repo:hotcrm seat, 2026-10-13T09:23Z.
Checked against GitHub and the trees, not the report:
The branch head is be03a57, two commits on 0f84c9c (36a4669 pins + lockfile, be03a57 the changes that need no flow rewrite); 7 files.
The blocker is a deliberate upstream break, not a platform defect. objectstack packages/spec/src/migrations/entries/semantic/18.flow-value-slot-template-dialect-refused.ts, verbatim: "Semantic-only — every token spelling authored in flows was measured lossy under conversion, so no D2 conversion rewrites any of them." ADR-0087's launch-window addendum allows that before GA ("one D3 semantic entry per retirement family in every case"). The breaking commits are the #19939 series (2b61f2d, 0e9e7b7, 6a06e3b, d85615d, f66fdc7, 12b9daf, 9fdb127, 111ffff, 968acc8, 615c696), 2026-10-08 → 2026-10-13. So nothing is filed upstream, and hotcrm rewrites its own flows.
Re-measured: CEL optional selection (x.?y.orValue(null)) is supported (packages/formula/src/validate.test.ts) but is no shorter than the has() guard, so it does not change the ceiling arithmetic.
Not re-measured by the seat: the 340 sites, and the ~+40 lower bound / ~+250 guarded estimate for src/revenue. Those are the dev's readings, on a scratch copy and under the verify lock.
The rest of the dev's breakage list (96 i18n/missing-flow, 75 failing test files) needs no ruling. It rides the resumed dispatch. One condition: the ADR-0131 refusal of org-less system writes (its item 7) must be attributed to harness or platform before anything is classified; a platform defect means waiting, per AGENTS.md §2.
Ruling: raise every HotCRM token ceiling by 50% · the maintainer's words, verbatim, in the repo:hotcrm seat's session chat (session_01BCDqr1DqnQjEh5jA9gGkUG), after the seat's R82 report on this card's decision: 「hotcrm token 上限提升 50%」, then 「暂时只优先处理升级相关的任务」
Recorded by the repo:hotcrm seat, 2026-10-13T10:02Z. Freshness gate: the newest comment before this record is the analysis 6142448037, and the body was last edited before it; there is nothing newer to reconcile. needs-user-decision → pm:dispatched in the claim that follows this record.
What the ruling decides: how the 18.0.0-next.0 flow migration's token growth is paid. Every ceiling moves up by half, so none of the analysis's letters is picked as written. It is closest to A (the ceilings move, quoted in the raising PR's body, as scripts/check-source-token-ratchet.mjs requires), but it is not anchored to a post-migration reading.
The seat's reading of the numbers (stated to the maintainer in the same chat; a veto there replaces it): all 12 rows of CEILINGS × 1.5, exact, no rounding to 1k.
package
business semantics
interaction layer
authored total
src/sales
59,000 → 88,500
31,000 → 46,500
107,000 → 160,500
src/service
14,000 → 21,000
7,000 → 10,500
22,000 → 33,000
src/revenue
16,000 → 24,000
3,000 → 4,500
19,000 → 28,500
src/marketing
9,000 → 13,500
1,000 → 1,500
10,000 → 15,000
Execution: the raise is this card's own first commit, in the upgrade PR, with the ruling quoted verbatim in the PR body (the ratchet's rule; precedent PR #1953). The upgrade resumes from claude/issue-2031-objectstack-18-next-0@be03a57. Per the second sentence, the seat dispatches only upgrade work for now: #2069 and #2070 (already in flight) finish, and no other card is dispatched.
Side effect, stated: once this lands, #2077 and #2083 no longer wait on a src/revenue shrink.
Claim: PM loop round R82
Session: session_01BCDqr1DqnQjEh5jA9gGkUG
Account: os-justin (the seat's linked user as GET /user answers it; always the card's assignee)
Branch: claude/issue-2031-objectstack-18-next-0
Worktree: hotcrm-issue-2031
Domain: repo:hotcrm (single-lane repo, no domain:* taxonomy)
Seat: repo:hotcrm#1
File surface: scripts/check-source-token-ratchet.mjs (the 12 CEILINGS rows × 1.5 and its header ledger, first commit, per the ruling); the pins (package.json, pnpm-lock.yaml regenerated by pnpm install, objectstack.manifest.json, the two engines.protocol lines; already on the branch); all 32 src/*/flows/*.flow.ts (the {…} → CEL migration); the four locale packs under src/sales/translations/** (the 96 i18n/missing-flow keys); test/** (the 75 failing files: harness changes for 18.0.0-next.0, test/helpers/identity-objects.ts retired); src/** beyond flows only where an 18.0.0-next.0 breaking change forces it (each one named in the PR); the old-pin claims across src/**, test/**, docs/**, content/docs/**, README*; one .changeset/2031-*.md. ⛔ Not AGENTS.md, CLAUDE.md or .claude/**. (stop on breach; explain in the report)
Container & model: L (340 flow sites, 96 keys, 75 test files: judgment at each site), mode:subagent by the seat's judgment, a deviation from the L→mode:cloud default. The relay write tools, the verify lock and the first leg's measurements live in this container, and the first leg ran here in ~28 min. model: default (dispatch-gates --tier --repo objectstack-ai/hotcrm hits no declared tier glob; the tier stays the PM's per-card call)
Clause-②: no
Responsibility: n/a — not a defect card
Ruling-ref: 6143114246 (the maintainer's 「hotcrm token 上限提升 50%」 and 「暂时只优先处理升级相关的任务」; fetched in this act)
Thread-read: 6143114246
Resume-from: claude/issue-2031-objectstack-18-next-0@be03a574f862dbc0c2421c485cf1813891eb6ffa
Serial constraints cleared:
repo:hotcrm seat, 2026-10-13T11:46Z. The card stays pm:dispatched: the dev keeps working on everything else, and only this item waits. Source: the dev's status reply in this session (head dac942b4).
os-decision-facets
① 项目长远合理性:A 跟随上游迁移指引并用回迁卡钉住过渡;B 静默跳过;C 把升级挂在最后一个阶段上。
② 实际业务拉动:你要求升级优先;生产走 SQL,缺键形状主要在内存驱动上,A 的暂时缺口线上影响最小。
③ 防 AI 犯错:静态 has() 守卫在 A 下完整保留;B 制造「测试还在、实际不跑」的假象。
④ 创业阶段不扩散:三者都不加能力;A 多一张回迁卡。
Prior rulings read: skip / quarantine (the fleet's never-skip rule); ADR-0131 §8 (C1 34dba5a → C8 order); thread: this card's dev status reply.
Ruling (#2031 sub-item, the driver-memory legs) · the maintainer's word, verbatim, answering the seat's question in this session's chat (session_01BCDqr1DqnQjEh5jA9gGkUG): 「就应该在 sql 上测试,作废内存驱动的测试」
Recorded by the repo:hotcrm seat, 2026-10-13T11:48Z. It answers the analysis 6144835860. The card stays pm:dispatched; the in-flight dev applies it.
What the ruling binds (the seat's reading, stated to the maintainer in the same chat):
The suites that ran legs on driver-memory run on SQL. That is close to letter A, but without A's restore card: the memory-driver legs are retired, not parked until ADR-0131 C8. No card is filed to move them back.
A leg whose only purpose was the absent-key shape that only driver-memory produces is deleted by this ruling. Each one is named in the PR body (suite, leg, what it asserted). That is a retirement on a quoted ruling, ⛔ not a skip, and no sentinel or disabled leg is left behind.
A sentence elsewhere that names the memory-driver measurement as current is re-scoped, unless it is in a governed file (AGENTS.md / CLAUDE.md / .claude/**), which is reported instead.
os-dev-report
{
"issue": 2031,
"status": "done",
"branch": "claude/issue-2031-objectstack-18-next-0",
"pr": "#2091",
"session": "session_01BCDqr1DqnQjEh5jA9gGkUG",
"premise_still_valid": true,
"summary": "Draft PR #2091 moves HotCRM to @objectstack/* 18.0.0-next.0. All local verify links are green at head 1407ca7 except one test flake, which passed when re-run alone; CI was still in progress at report time. What changed: (1) 24 pins move 17.7.0 -> 18.0.0-next.0 exact, with protocol ^18.0.0-next.0. (2) All 32 flows and the billing payload are rewritten to CEL value envelopes and double-brace text holes; os validate went from 340 errors to passing. (3) 32 new flow i18n keys are translated in 3 locales. (4) manage_org_presentation is removed from tenant_admin. (5) The test harness follows 18: Default Organization at boot, permission sets granted by name, a variant boot builds its configuration again, plugin-auth's identity preset replaces identity-objects.ts (retired), and runScheduledFlow (seat-approved) starts sweeps the way the schedule tick does. (6) The 12 token ceilings are raised x1.5 as RULED, per the seat's answer. (7) The memory-driver legs are moved to SQL or retired, per the maintainer's ruling (comment 6144864473). (8) Pin claims are re-scoped and a changeset is added. Browser pass: the app boots, an account record page renders, Convert Lead converts a lead, and the executive dashboard draws.",
"tests": "Every link ran under os-verify-lock at HEAD 1407ca7 on a clean tree. validate exit 0 'Validation passed' (18 Objects 357 Fields). typecheck exit 0. lint exit 0, '18 suggestion(s)'. lint:i18n-gate exit 0, '0 i18n/missing-* issues'. hygiene exit 0, 'source hygiene clean'. hygiene:tokens exit 0, 'source token ratchet clean'. build exit 0, 'Build complete' with 8 author-time warnings (7 approval admin_rescue routing notes, 1 hierarchy-security enterprise note). pnpm test (detached, tail --pid): VERDICT command-exit 1, held 749s, 'Test Files 1 failed | 178 passed (179)', 'Tests 1 failed | 3658 passed | 1 skipped'. The failure was script-bodies 'send_email writes the email and its timeline pointer' (handed-over status 'failed' instead of 'queued', under shared-box load). Re-run alone at 1407ca7: exit 0, 'Tests 57 passed (57)'; reported as a flake and not changed. Seat note: test/case-sla-breach-on-close.test.ts re-run alone at 1407ca7 gives exit 0, 'Tests 21 passed (21)'. The touched-file batch (15 files) after the RULED and (b) edits gave exit 0, 'Tests 382 passed (382)'. The ratchet x1.5-as-RULED probe that previously failed 6 self-test cases now passes, because those 6 cases were adapted as the seat directed. CI on PR #2091 was in progress at report time (Typecheck and Build, Quality Checks, Check Changeset, Build and Test 22.x, Playwright, link-check); Label Pull Request and Vercel had succeeded. Browser (Chromium /opt/pw-browsers/chromium, demo:reset + dev on 4001): 'STEP 3 executive_dashboard chart marks {svgs:4, paths:19}', 'STEP 4 account record Acme Corporation name visible: true', 'STEP 5 lead after convert {is_converted:true, status:converted, converted_account set, converted_contact set}', page errors none. The dev server's PIDs were stopped and port 4001 is closed. No ablation was run: no new gate was introduced.",
"mcp_calls": "0",
"api_writes": "2 relay or REST writes plus 3 branch pushes in this leg. (1) fleet-write dispatch fw-20261013T122201Z-332b55: POST /repos/objectstack-ai/objectstack/dispatches, executed as POST /repos/objectstack-ai/hotcrm/pulls (draft) and POST /repos//issues/2091/assignees. Read-back: 20126 bytes sent, 20126 stored, identical; assignee os-justin. (2) This os-dev-report comment, POST /repos//issues/2031/comments via post-stamped.mjs. git push of the branch: 248b865..dac942b, dac942b..29dd01c, 29dd01c..1407ca7. No label writes.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: named producer: this app's scheduled-flow suites (test/flow-scheduled.test.ts and 9 others) drive @objectstack/verify flows.run. verify.flows.run('case_sla_monitor', {}, { as: admin }) answers 403 PERMISSION_DENIED 'This caller may not start this flow. A flow that runs on its own trigger starts there, or as a sub-flow from a parent flow's subflow node.' on 18.0.0-next.0 · evidence: runtime dist flow-start-admission refusesElevatedSelfTriggeredStart admits only executionContext.isSystem === true (ca135dc / objectstack#22310, c572fe2 / objectstack#22407). The verify dist flows.run(name, params, opts: AsUser) dispatches POST /automation/:name/trigger with the bearer only and has no AsSystem arm, while hooks.run takes { system: true } (b4ce5e2). An app therefore has no kit door to start its scheduled flows; hotcrm carries the thin runScheduledFlow helper (test/helpers/verify-stack.ts) until the kit ships one · dedupe words: verify flows.run system principal; flows.run AsSystem; scheduled flow test kit trigger door 403",
"class: a · reach: named producer: every 17.x install upgraded to 18 (its sys_user_position rows carry no organization_id), and any system or seed write of sys_user_position, which stores no organization (measured: a system seed stores null, while an admin's POST /api/v1/data/sys_user_position stores the session organization) · evidence: two implementations disagree on organization-less position holders. @objectstack/plugin-security@18.0.0-next.0 dist holdingTakesEffectIn(row, org) is !org || org === organizationId: an org-less holding takes effect in every organization. It is used by activeHoldings and assignmentAnchorsOfPosition, whose doc calls organization_id = :org OR organization_id IS NULL 'the governed spelling of exactly this rule'. @objectstack/plugin-approvals@18.0.0-next.0 dist ApprovalService.expandPositionUsers(positionName, organizationId) sets filter.organization_id = organizationId (strict equality); it is called from the position-approver expansion with directoryOrg. Minimal repro: bootStack the app (SQL, single); sign up user U; system-seed sys_user_position { user_id: U, position: 'sales_manager' } with no organization_id; a rep creates an account so account_approval opens in the Default Organization. The slate omits U (opens on administrators), and U's approve answers 403 'not a pending approver', while plugin-security treats U as holding sales_manager in that organization. The 403 was measured earlier in this run, on the pre-merge leg · dedupe words: expandPositionUsers organization-less holder; org-less position approval slate; holdingTakesEffectIn approvals",
"carrier: seat · noted, not filed: test/undeclared-key-probe.test.ts (its memory leg) and test/decorative-field-sweep.test.ts still boot bare kernels on driver-memory. They pass (no organization exists in those kernels), so they fall outside the seat's mechanical scope of 'now refused'. The maintainer's words 「作废内存驱动的测试」 could reach them; the seat decides"
],
"rulings_applied": {
"token_ceilings": "12 rows x1.5 exact, kind RULED, header record quotes 「hotcrm token 上限提升 50%」 / 「暂时只优先处理升级相关的任务」 (comment 6143114246). Anchored rows and declined rows kept as history marked 'superseded (#2031)'. No gate logic changed. Test lines changed: listed in the PR body under 'Ratchet self-test lines changed, and why'.",
"memory_driver": "「就应该在 sql 上测试,作废内存驱动的测试」 (comment 6144864473). Refusal on every affected leg: 'MemoryMultiTenantUnsupportedError: [driver-memory] Refusing to answer: this driver has NO row-level tenant isolation. Detected: create() on crm_account (crm_case, crm_forecast) was handed a tenant scope by the engine: tenantId = org_…'. RETIRED: unassigned-case-triage-reach 'driver-memory (sparse rows — the key is ABSENT)' matrix plus the memory half of the shape case; sla-at-risk-live-work 'a resolved case is not at risk — over memory'; flow-escalation-ownerless-case 'owner column never written (sparse datasource)' x2 flows x4 cases; flow-sla-ownerless-case 'sparse datasource (absent owner)'; freeze-guard-reference-cleanup 'does not yield when the link was already empty' x3 objects. MOVED to SQL, assertions kept: do-not-call-enforcement (whole suite), forecast-period-boundary and forecast-period-end-boundary refusal matrices, hooks-runtime-service campaign_lead_conversion_refresh desk, hooks-runtime 'is a no-op when there is no rep pool'. hotcrmMemoryStack removed. object-validation-predicates (#630) untouched."
},
"seat_note_2090": "case_sla_monitor query_breached reads sla_due_date: { $lt: '{now}' } (admitted by 18.0.0-next.0: os validate passes). escalated_date reads celisoDatetime(now()). The §4 pin asserts { $lt: '{now}' } and compares the tile's second branch to the sweep's whole selection (stronger). After the merge of #2090/#2089/#2087, case-sla-breach-on-close passes: 'Tests 21 passed (21)' at 1407ca7. The dev-server tick in the observed window: NOT MEASURED, reason: hourly cadence, and no tick fell within it. The sweep on a fresh DB is measured by the verify fresh boot.",
"per_breakage": "Listed in the PR body: handshake; email-template category; flow value-slot dialect (340 errors); 32 new i18n keys; manage_org_presentation retired; verify harness (34dba5a, c8a9783/aa71c4d, #22301, #22074, #22519, a6f3a13, 97610a5); trigger door system-only (ca135dc, c572fe2); driver-memory tenant refusal; behaviour moves (settled contracts cascade with the account, seed data in the Default Organization, data-door position stamp); retired spellings refused at the door.",
"per_site_exceptions": "close_case · close · resolved_by_article: an omitted answer kept the stored link on 17.7.0 and writes NULL on 18 (no CEL spelling keeps it; CEL refuses an unbound variable). Billing account block: on a sparse row a missing optional column was dropped on 17.7.0 and is sent as null on 18; on SQL the two send the same bytes.",
"tokens_before_after": "src/sales BS 57,449->57,878 IL 28,377->28,377 total 102,183->102,599; src/service BS 12,989->13,037 IL 5,602->5,602 total 21,897->21,948; src/revenue BS 15,799->15,745 IL 2,165->2,165 total 18,939->18,885; src/marketing BS 7,994->8,027 IL 886->886 total 9,100->9,132 (origin/main -> head; ceilings x1.5 RULED).",
"files_changed": "90 files vs origin/main: package.json, pnpm-lock.yaml, objectstack.manifest.json, src/sales/index.ts, src/service/index.ts, src/sales/email-templates/_bundle.ts, src/sales/profiles/tenant-admin.profile.ts, 28 flow files under src/*/flows (incl. _billing-endpoint.ts), src/sales/translations/{zh-CN,es-ES,ja-JP}/app.ts, scripts/check-source-token-ratchet.mjs, content/docs/whats-new{,.zh-Hans,.zh-Hant}.mdx, docs/STATUS.md, .changeset/2031-objectstack-18-next-0.md, test/helpers/verify-stack.ts, test/helpers/identity-objects.ts (deleted), and 44 test files.",
"lockfile_delta": "790 -> 791 packages: keys; 54 @objectstack/create-objectstack keys moved 1:1; +@objectstack/service-mailbox@18.0.0-next.0; no third-party change.",
"deviations": [
"The harness system-reminder asked for a model-named Co-Authored-By trailer and a different PR footer. I followed the dispatch and the agent file instead: model-free trailers, and the session-URL footer.",
"src/service/objects/_case-assignment.ts: a pool widening to organization-less holders was written, then measured as unnecessary (test/case-assignment.test.ts passed 35/35 without it), and reverted. App behaviour is unchanged.",
"test/helpers/verify-stack.ts signUpPerson stamps the session organization on position rows, the shape the data door produces on 18 (measured). flow-sla-ownerless-assignment seeds its pool with the install organization for the same reason.",
"hooks-runtime 'is a no-op when there is no rep pool' used driver-memory only as a second, empty datasource (not the absent-key shape). It moved to a fresh SQL boot under ruling 2's 'move' arm.",
"Pin-claim sweep: test/fixtures/composed-artifact.json protocol ^17.7.0 was left, because nothing reads it. Version-less pin claims are found by reading and were not re-measured; the PR says so.",
"The PR was opened after the full verify rather than right after steps 1 and 2, so the write-once body could carry the measured results. The branch was pushed after every step."
],
"cleanup": "dev server PIDs 25111 25123 25134 25135 25197 25211 stopped; worktree node_modules removed and worktree removed after this report."
}
Verdict on the os-dev report 6145479122 for PR #2091 — ACCEPT, with human merge. The PR is +2,191 / −1,568 = 3,759 changed lines (lockfile included), over the 3,000-line threshold, so it lands as Tier H. It stays a draft, carries needs-user-decision, and gets the final 维护者速读 on the PR; review is requested from os-zhuang and hotlong. A direct merge by the maintainer is the review record. On an approval, this seat lands it through the queue. repo:hotcrm seat, session_01BCDqr1DqnQjEh5jA9gGkUG, 2026-10-13T12:27Z.
6143114246, ceilings ×1.5: the 12 rows are CEILING_KIND.RULED, recorded with the ruling, the path the gate's header prescribes for a grant. The 6 self-test cases that hard-coded "every ceiling is ANCHORED" are adapted, and the PR names each changed test line.
fix(service): the SLA Violations tile counts open breaches at read time #2090 coordination: the sweep's query_breached reads $lt: '{now}', the same spelling as the tile (admitted on 18.0.0-next.0, since os validate passes), and escalated_date reads cel\isoDatetime(now())`. test/case-sla-breach-on-close.test.tspasses (21 / 21 at1407ca7`).
Per-site exception, judged by the seat:close_case's resolved_by_article, where an omitted variable writes NULL on 18. On main (17.7.0) a close-screen field left empty already resumes as '' and case_resolution_article_normalize writes NULL (the flow's own comment records the measurement). So the UI path is unchanged; only an API caller that omits the variable differs. Accepted.
Gates: the dev's run on 1407ca7a covered validate, typecheck, lint, i18n-gate, hygiene, tokens and build, all exit 0. pnpm test reported 178 / 179 files: 1 test (script-bodies send_email status) failed under shared-box load and passed alone, 57 / 57. It is a flake, recorded and not changed; CI decides it. The browser check covered boot, an account record page, Convert Lead and the executive dashboard drawing.
CI on 1407ca7a at verdict: Check Changeset, Quality Checks, Typecheck and Build, Playwright, CodeQL, link-check, Label all success; Build and Test (22.x) is in_progress.
Filed or carried by the seat after this verdict:
The two class: a platform findings in the report: the @objectstack/verifyflows.run has no system-principal door; plugin-approvalsexpandPositionUsers disagrees with plugin-securityholdingTakesEffectIn on organization-less position holders. Both go to objectstack.
A follow-up card for the two suites that still boot bare kernels on driver-memory (undeclared-key-probe's memory leg and decorative-field-sweep), under ruling 6144864473's wording.
Landed: PR #2091 MERGED as 2f3d898d on main (approved by os-zhuang on head 1407ca7a, who enqueued it; merged 12:44:50Z). This card was closed completed by its Fixes line. HotCRM's main now pins @objectstack/*18.0.0-next.0. repo:hotcrm seat, 2026-10-13T12:45Z.
Two readings: the PR object reads merged; git diff 2f3d898d^ 2f3d898d has the same stable patch-id (a87f6ab11f12) as base → reviewed head. The merged tree equals 1407ca7a's tree (an empty git diff --stat).
Activity
objectstack-fleet commentedon Oct 13, 2026
Claim: PM loop round R82
Session:
session_01BCDqr1DqnQjEh5jA9gGkUGAccount:
os-justin(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-2031-objectstack-18-next-0Worktree:
hotcrm-issue-2031Domain:
repo:hotcrm(single-lane repo, nodomain:*taxonomy)Seat:
repo:hotcrm#1File surface:
package.json(every@objectstack/*pin, together),pnpm-lock.yaml(regenerated bypnpm install, never hand-edited),objectstack.manifest.json(specVersion,engines.protocol); whatever the 18.0.0-next.0 breaking changes force insrc/**,test/**,scripts/**(each breakage named in the PR body); the old-pin claims17.7.0re-scoped per AGENTS.md "Platform Upgrades" step 5 acrosssrc/**,test/**,docs/**,content/docs/**,README*;test/helpers/identity-objects.tsretired only if objectstack#22074 ships in the installed release; one.changeset/2031-*.md. ⛔ NotAGENTS.md,CLAUDE.mdor.claude/**(governed; a stale claim there is reported, not edited). (stop on breach; explain in the report)Container & model:
M(a major-line prerelease: judgment on each breaking change),mode:subagent,model: default(dispatch-gates --tier --repo objectstack-ai/hotcrmoverpackage.json pnpm-lock.yaml objectstack.manifest.json: "no path-derived mandate: the surface hits none of the 3 declared glob(s)"; the tier stays the PM's per-card call)Clause-②: no
Responsibility:
n/a — not a defect cardThread-read: none
Serial constraints cleared: in flight in this lane: none (0
pm:dispatchedbefore this card, 0 seat PRs). Open PRs touching the same files: dependabot #2063 (fumadocs-core) and #2064 (@playwright/test), both onpackage.json+pnpm-lock.yaml; whichever lands second regenerates the lockfile. Token ratchet headroom at the brief:src/revenue~14,src/service~89; a pin-claim re-wording inside a ceilinged package must not grow it. Queued cards on the seat post's hot-file queues (§5) start after this lands, on the new pin.Dispatch authority (maintainer direct order, ahead of the queue): the card's own
Restart-when:line (npm view @objectstack/cli versionprints a version above 17.7.0) does not hold:latestis still17.7.0.18.0.0-next.0was published to thenextdist-tag at 2026-10-13T08:28Z, and every one of the 24@objectstack/*packages hotcrm pins resolves at that exact version. The maintainer ordered this upgrade in this session's chat after summoning the seat with/pm-dispatch hotcrm, verbatim: 「首先处理 objectstack 升级到 18.0.0-next.0」. The target therefore is18.0.0-next.0exactly, not "the newest release".Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
os-dev-report
{ "issue": 2031, "status": "blocked", "branch": "claude/issue-2031-objectstack-18-next-0", "pr": null, "session": "session_01BCDqr1DqnQjEh5jA9gGkUG — the PM seat's session (this dev ran as its subagent)", "premise_still_valid": true, "summary": "BLOCKED on the src/revenue token ceiling (one decision only the maintainer can make). 18.0.0-next.0 refuses the single-brace template dialect in every flow VALUE slot and TEXT slot (a87d8be..2b61f2d family: 0e9e7b7, 615c696, 111ffff, 12b9daf, f66fdc7, 9fdb127, 968acc8, d85615d, 6a06e3b). `os validate`, `os lint` and `pnpm dev` all refuse the stack on 340 sites in 32 flows (sales 213, revenue 71, service 33, marketing 23). The dev server exits with `Compile failed — fix errors above before starting dev server`. Upstream ships no D2 conversion for these sites (`no authored spelling maps losslessly`), so each site needs the CEL envelope, plus a decision about what happens when the value is absent. Measured on a scratch copy, with the most compact spelling the spec offers (the `cel` tagged template) and NO has() guards, src/revenue's authored total is ~19,040 against a ~19,000 ceiling (over by ~40, headroom today ~14). With guards on every optional dotted read, revenue is ~19,234 authored (over ~234) and ~16,094 business semantics (over ~94), and src/service is ~22,010 (over ~10). The dispatch rules out raising a ceiling, so I stopped there. Landed on the branch (be03a57, pushed, no PR): all 24 pins moved to 18.0.0-next.0 with the lockfile regenerated by pnpm install; engines.protocol and specVersion moved to ^18.0.0-next.0; the retired email_template `category` removed (ec7f7f8); test/hooks-runtime.test.ts follows hooks.run's operation-generic type (a6f3a13). Every upstream fix row on the card IS in 18.0.0-next.0 (table below), so the waiting cards' blocker becomes this ceiling decision, not availability. Further breakages measured for the resume: 96 i18n/missing-flow keys (screen select options and descriptions are now translatable, and translations are outside the ratchet), and 75 failing test files. Of those, 47+ fail because package-declared permission sets no longer get sys_permission_set rows (c8a9783, test/helpers/verify-stack.ts signUpPerson). Others fail on the ADR-0131 refusal of org-less system writes (SystemWriteOrganizationRequiredError), on flow refusal, on `Object ... not found` in reduced kernels, and on docs-declared-versions (pin claims not yet re-scoped).", "gates": [ {"cmd": "pnpm validate (lock)", "head": "36a4669", "verdict": "os-verify-lock: VERDICT command-exit 1", "exit": 1, "reading": "defineStack validation failed (8 issues): emailTemplates.N.category retired"}, {"cmd": "pnpm validate (lock)", "head": "be03a57 src tree (committed after the run; the later commit touched only test/hooks-runtime.test.ts)", "verdict": "os-verify-lock: VERDICT command-exit 1", "exit": 1, "reading": "340 expression-invalid (317 value-slot, 23 text-slot) in 32 flows"}, {"cmd": "pnpm typecheck (lock)", "head": "be03a57 minus the hooks-runtime edit", "verdict": "os-verify-lock: VERDICT command-exit 1", "exit": 1, "reading": "test/hooks-runtime.test.ts(49,3): error TS2322"}, {"cmd": "npx tsc --noEmit (NOT under the lock, deviation 1)", "head": "be03a57 tree", "verdict": "exit 0, no output", "exit": 0}, {"cmd": "pnpm lint (lock)", "head": "be03a57", "verdict": "os-verify-lock: VERDICT command-exit 1", "exit": 1, "reading": "Errors (340) expression-invalid; ✗ 96 warning(s) fail this run under --strict"}, {"cmd": "node scripts/check-lint-i18n-gate.mjs", "head": "be03a57", "verdict": "✗ i18n lint gate: 96 `i18n/missing-*` issue(s) found — must be zero.", "exit": 1}, {"cmd": "node scripts/check-source-hygiene.mjs", "head": "be03a57", "verdict": "✓ source hygiene clean", "exit": 0}, {"cmd": "node scripts/check-source-token-ratchet.mjs", "head": "be03a57", "verdict": "✓ source token ratchet clean (revenue authored ~18,986 / ~19,000, headroom ~14; service authored ~21,913 / ~22,000, headroom ~87)", "exit": 0}, {"cmd": "pnpm build", "head": "be03a57", "verdict": "NOT MEASURED: build, reason: the same author-time rules refuse the stack (validate red); pnpm dev reports `Compile failed`", "exit": null}, {"cmd": "pnpm test (lock)", "head": "be03a57", "verdict": "os-verify-lock: VERDICT command-exit 1 · held the lock 523s", "exit": 1, "reading": "Test Files 75 failed | 104 passed (179); Tests 111 failed | 2231 passed | 1349 skipped (3691); Errors 18"}, {"cmd": "pnpm dev (fresh DB, PID 5999)", "head": "be03a57", "verdict": "✗ Compile failed — fix errors above before starting dev server; ELIFECYCLE exit 1 (process exited by itself, nothing left running)", "exit": 1}, {"cmd": "browser verification (record page, dashboard)", "head": "be03a57", "verdict": "NOT MEASURED: browser, reason: the app does not boot on 18.0.0-next.0 until the flow migration lands", "exit": null}, {"cmd": "pnpm test:e2e / docs build / changeset-check", "head": "be03a57", "verdict": "NOT MEASURED: e2e, docs, changeset, reason: no boot and no PR; declared to CI on resume", "exit": null} ], "tests": "See `gates` for every verdict line. Measurements behind the block: (1) Ratchet lower bound: a scratch copy of src/ + scripts/ (never the worktree) had every one of the 340 refused sites rewritten to the CLI's own suggested CEL spelling, wrapped in the spec's `cel` tagged template (`'{x.y}'` becomes cel`x.y`, +2 chars), with no has() guards. `node scripts/check-source-token-ratchet.mjs` on that copy: exit 1, `✗ src/revenue authored total is ~19,040 tokens; the ratchet ceiling is ~19,000 (over by ~40)`; service authored ~21,957 (headroom ~43); sales BS ~57,596; marketing BS ~8,027. (2) Guarded estimate: the same rewrite with `has(a.b) ? a.b : null` on every dotted read that is not `.id` gives exit 1: revenue authored ~19,234 (over ~234), revenue BS ~16,094 (over ~94), service authored ~22,010 (over ~10); sales BS ~58,248 (headroom ~752). (3) Completeness: that guarded rewrite was copied over the worktree's src under `trap 'git checkout HEAD -- REPO_ROOT/src' EXIT` and validated under the lock: `os-verify-lock: VERDICT command-exit 1`. 6 errors remained, all residue of the measurement codemod (4 from `cel` JSON-quoting an interpolated `${key}` and 2 filter operands in the shared _billing-endpoint.ts that it did not reach). No further rule family hides behind the 340. Restore proven: `git diff HEAD` empty, `git status --porcelain` 0 lines, blob hash 2c26093d equal to HEAD's for src/revenue/flows/contract-renewal.flow.ts. The first attempt was a no-op (`rsync: command not found`, still 340 errors), so that reading was discarded and the copy redone with cp. (4) Handshake: a probe of the installed @objectstack/metadata-core checkProtocolCompat gives `^17.7.0 incompatible 18`, `^18.0.0-next.0 ok 18`, `^18 ok 18`, `^18.0.0 ok 18`. rangeAdmitsMajor compares only the leading major (`^` means runtimeMajor === maj), and PROTOCOL_VERSION = \"18.0.0\" in the installed spec. hotcrm's test/docs-declared-versions.test.ts requires bare(specVersion) == the installed @objectstack/spec version and engines.protocol == specVersion, so the spelling is ^18.0.0-next.0, not the ^18 that `os migrate meta --write` would write. (5) Lockfile, by pnpm install: packages-section keys 790 to 791 (pnpm reported `resolved 705`); 54 @objectstack keys moved 1:1, plus 1 new transitive @objectstack/service-mailbox@18.0.0-next.0 (pulled in by cli and verify); no third-party key added, removed or moved. The install's peer warning `better-auth 1.7.3 unmet peer better-sqlite3@^12.0.0: found 13.0.3` predates this change (same pair in the 0f84c9c lockfile).", "upstream_rows": [ "upstream | in 18.0.0-next.0? (evidence) | hotcrm card", "objectstack#22310 (PR #22404) | yes: ca135dc in runtime CHANGELOG 18.0.0-next.0; REST compare ca135dcc...d889448b (release commit of every @objectstack/*@18.0.0-next.0 tag) = ahead 413 behind 0 | #2016", "objectstack#22407 (PR #22424) | yes: c572fe2 in runtime CHANGELOG; compare ahead 405 behind 0 | #2016 (both doors present, so #2016's two-fix condition holds)", "objectstack#22178 (PR #22213) | yes: 0e9371f in service-analytics CHANGELOG; ahead 520/0 | #2000", "objectstack#22212 (PR #22281 + #23146) | yes: d1dbe70 in lint, 02bec6c in spec CHANGELOG; ahead 487/0 and 20/0 | #1583", "objectstack#22238 (PR #22285) | yes: 6d2da32 in cli CHANGELOG; ahead 482/0 | #1585", "objectstack#22074 (c6fe02d, createIdentityObjectsPlugin preset) | yes: c6fe02d in plugin-auth + plugin-security CHANGELOG, and createIdentityObjectsPlugin is exported by the installed plugin-auth dist; ahead 535/0 | test/helpers/identity-objects.ts retirement NOT done here (blocked before it); safe to do on resume", "objectstack#22149 (PR #22294) | yes: 9ad3d24 in spec CHANGELOG; ahead 473/0 | —", "objectstack#22189 (PR #22285) | yes: 6d2da32 in cli CHANGELOG; ahead 482/0 | —", "objectstack#22067 (e67ba80, c8d06a9) | yes: plugin-security / objectql CHANGELOG; ahead 578/0, 555/0 | —", "objectstack#22071 (bafb58b, 4935c66) | yes: cli / plugin-auth CHANGELOG; ahead 574/0, 567/0 | —", "objectstack#22072 (PR #22108 + #22115) | yes: a959493, cdeabec in cli/spec CHANGELOG; ahead 563/0, 558/0 | —", "objectstack#22073 (4935c66) | yes: cli CHANGELOG; ahead 567/0 | —", "objectstack#22079 (dd39171, d7c5c33) | yes: cli / spec CHANGELOG; ahead 566/0, 541/0 | —", "objectstack#22824 (PR #22850) | yes: 69d4218 in plugin-approvals/lint/spec CHANGELOG; ahead 168/0 | per seat post", "objectstack#22455 (PR #22513 + #22492/#22523/#22535) | yes: ce3d0ad in service-storage/plugin-security/plugin-audit CHANGELOG; ahead 360/0 | per seat post", "objectstack#22437 (PR #22462) | yes: 7806a14 in rest CHANGELOG; ahead 383/0 | per seat post", "objectstack#22607 (PR #22625) | yes: e8c6666 in plugin-approvals CHANGELOG; ahead 309/0 | per seat post", "Note: the CHANGELOGs cite commit hashes, not these issue numbers (`#22310` greps 0 hits in every installed CHANGELOG); every row was matched by its fix commit's short hash." ], "breakages": [ "1 FIXED · os validate: `✗ emailTemplates.0.category: email_template.category was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove)` x8 (68 more in the second package). Upstream ec7f7f8 (#22876, conversion email-template-category-is-system-removed). Fix: src/sales/email-templates/_bundle.ts writes no `category`.", "2 FIXED · protocol handshake: `manifest.engines.protocol '^17.7.0' does not admit protocol 18, so the load refuses this stack` (os migrate meta). Upstream 4e9fe9f (PROTOCOL_VERSION 18.0.0). Fix: ^18.0.0-next.0 in src/sales/index.ts:93, src/service/index.ts:138 and objectstack.manifest.json (specVersion + engines.protocol).", "3 FIXED · typecheck: `test/hooks-runtime.test.ts(49,3): error TS2322: Type 'Promise number|boolean|EngineRow' is not assignable to type 'Promise Rec'`. Upstream a6f3a13 (verify hooks.run generic over the operation). Fix: repWrites is generic over Op.", "4 OPEN (the blocker) · os validate / os lint / pnpm dev: 340 `expression-invalid`, e.g. `A value slot no longer reads the {…} template dialect … Write {record.id} as { dialect: 'cel', source: 'record.id' }` and `A flow text slot reads double-brace template holes (ADR-0032 §3), not the single-brace {…} dialect`. Upstream 0e9e7b7 + 615c696 + 111ffff + 12b9daf + f66fdc7 + 9fdb127 + 968acc8 + d85615d + 6a06e3b + 2b61f2d. No D2 conversion exists. Upstream measured hotcrm itself at c529de2 (91 create/update/assignment sites). Per-package site counts: sales 213, revenue 71, service 33, marketing 23. The shared src/sales/flows/_billing-endpoint.ts carries the billing payload sites both handoff flows use. Filter operands that may be absent need a `decision` route rather than a has() guard (the refusal says so).", "5 OPEN · lint:i18n-gate: 96 i18n/missing-flow (32 keys x zh-CN/es-ES/ja-JP: screen select options, screen and refusal descriptions in campaign_enrollment, lead_conversion, schedule_followup, quote_generation, claim_case). #2008 recorded these as untranslatable on 17.7.0 (FLOW_SCREEN_FIELD_NO_OPTIONS); 18.0.0-next.0 makes them keys. Translations are outside the ratchet.", "6 OPEN · tests, 47 files plus 6 mixed: `Error: no permission set named \"sales_rep\" is registered on this stack` at test/helpers/verify-stack.ts:145. Upstream c8a9783 (`no seeder or materializer writes a sys_permission_set row`); the grant is now written by name (`permission_set`). Fix site: signUpPerson.", "7 OPEN · tests, 3 files plus others: `SystemWriteOrganizationRequiredError: Insert on 'crm_account' was REFUSED: a system-context write on a tenant-scoped object must carry an organization … this install declares the 'single' tenancy posture but holds NO organization` (ADR-0131). The message names the cause `a composition that never booted the auth plugin`. Not yet attributed: harness or platform. To be measured on resume before any classification.", "8 OPEN · tests: `Object 'crm_quote' / 'crm_opportunity' not found` (4-6 files with reduced kernels), `already has a live bootStack kernel` (cascade from failed teardowns), MemoryMultiTenantUnsupportedError (unassigned-case-triage-reach), docs-declared-versions (pin claims not re-scoped yet), lint-i18n-gate test (= item 5). Not attributed yet." ], "files_changed": [ "package.json (24 pins 17.7.0 to 18.0.0-next.0)", "pnpm-lock.yaml (pnpm install)", "objectstack.manifest.json (specVersion, engines.protocol)", "src/sales/index.ts (engines.protocol)", "src/service/index.ts (engines.protocol)", "src/sales/email-templates/_bundle.ts (retired category removed; two comment lines)", "test/hooks-runtime.test.ts (repWrites generic)" ], "deviations": [ "1 · Three runs went outside the shared verify lock: `npx tsc --noEmit` (about 12s), `npx objectstack migrate meta --from 17`, and the first `npx objectstack validate --json`. Every other validate, typecheck, lint and test run went through os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=hotcrm-2031.", "2 · No PR was opened. The dispatch says to report blocked with the numbers when a required change cannot fit a ceiling, and to report blocked rather than land a red PR when the boot is blocked. The work is pushed: Resume-from: claude/issue-2031-objectstack-18-next-0@be03a57.", "3 · Commit trailers use this repo's model-free pair (`Claude-Session:` + `Co-authored-by: Claude`), not the harness reminder's model-named Co-Authored-By line, as the dev definition requires.", "4 · The handshake spelling is ^18.0.0-next.0, not the ^18 that `os migrate meta --write` prescribes. Both are admitted (probe above). This repo's own test/docs-declared-versions.test.ts pins specVersion to the installed spec version, and engines.protocol to specVersion.", "5 · Conflict noted, not resolved silently: AGENTS.md Platform Upgrades step 1 says to read the curated release notes and not to work from changelogs. The v18 page says curated notes are not written yet and the changesets are the record. The installed CHANGELOGs were used, as the dispatch directed. Governed text, not edited.", "6 · `os migrate meta --from 17` still lists 15 decision nodes for mode 'inclusive' (flow-decision-mode-inclusive-explicit). That is a 17.5.0 change (0283cb9) the earlier upgrades reviewed and left unwritten, not an 18.0.0-next.0 breakage, so none were written here.", "7 · Writes: git push x3 (empty branch, 36a4669, be03a57); 0 pr_create; 1 os-dev-report comment. 0 label writes." ], "mcp_calls": "0", "api_writes": "1 — POST /repos/objectstack-ai/hotcrm/issues/2031/comments (this os-dev-report, via scripts/pm/post-stamped.mjs and the fleet-write relay). Reads only otherwise (gh api GET on issues, pulls, timeline, compare, commits, contents).", "open_questions": [ { "question": "The 18.0.0-next.0 flow migration (340 sites; no lossless conversion upstream) cannot fit src/revenue's token ceiling. Even the most compact unguarded spelling is over by ~40 tokens on authored total (19,000). A realistic guarded one is ~+250 tokens (authored ~19,234, business semantics ~16,094 against 16,000), and src/service goes ~10 over 22,000. How should it be paid?", "options": [ "A · A maintainer ruling re-anchors THIS ceiling for src/revenue (authored and business semantics), and for src/service authored if the final count needs it, at the number the finished migration measures, quoted in the upgrade PR as the ratchet's own message prescribes. Real need: business need is real (the waiting cards #2016/#2000/#1583/#1585 need 18.0.0-next.0, and every row is in it); the growth is forced by a platform contract, not by new capability. Long term: the CEL envelope is the single dialect going forward, so this is not a workaround. AI-safety: the envelope plus explicit absence guards is the stricter, loudly-refused form. Startup scope: the README headline is about src/sales, which stays inside its own ceilings (guarded estimate BS ~58,248 of 59,000), so the headline claim does not move. Effort: one large dev card (migration + translations + harness + docs).", "B · No ceiling moves. Before the upgrade, a separate card compresses src/revenue (and src/service) by at least the measured growth (dedupe shared flow payloads, hoist repeated subject text), then the upgrade lands. Real need: same. Long term: shrink-only is honoured, but the cut (~1.5% of revenue's business semantics) is unscoped and may be hard to find without deleting behaviour. AI-safety: neutral. Startup scope: adds a refactor card in front of the upgrade. Effort: unknown; feasibility not measured.", "C · Stay on 17.7.0 until objectstack offers a terser null-tolerant read for value slots, or until v18 GA, and ask upstream for it (AGENTS.md §4: a per-site tax is fixed at the default). Real need: the waiting cards stay blocked although their fixes are released. Long term: depends on an upstream decision that is not filed. AI-safety: neutral. Startup scope: lowest work now. Effort: a filing, plus waiting." ], "recommendation": "A, because the growth is a platform-forced contract migration, not new capability. The ratchet itself names a ruling-quoted re-anchor as the route. The headline package (src/sales) stays inside its ceilings. B has unmeasured feasibility, and C keeps four released fixes out of reach. Second question for the seat: the remaining work is large (340 flow sites, each needing an absence decision, plus 96 translations, a 75-file test-harness migration and the pin-claim sweep). The seat may want to dispatch it under one claim but with an M-or-larger tier and the ADR-0131 harness item measured first." } ], "out_of_scope_findings": [ "carrier: whoever lands the upgrade · noted, not filed: at be03a57 the ratchet prints `src/service interaction layer … headroom is 1,384 tokens, over twice the 5% buffer — re-anchor this ceiling to ~6,000`. It is an advisory to lower a ceiling, and nothing here touched it.", "carrier: maintainer (governed file) · noted, not filed: AGENTS.md Platform Upgrades step 1 (read curated release notes, not changelogs) has no reading for a prerelease line whose curated notes do not exist yet. The v18 page says the changesets are the record." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
Release: session
session_01BCDqr1DqnQjEh5jA9gGkUG· reason: the dev stopped on a token ceiling that only a maintainer ruling can move · destination:needs-user-decision(this card is the anchor)Resume-from:
claude/issue-2031-objectstack-18-next-0@be03a574f862dbc0c2421c485cf1813891eb6ffaVerdict on the os-dev report
6142372109— ESCALATE (decision),pr: null.repo:hotcrmseat, 2026-10-13T09:23Z.Checked against GitHub and the trees, not the report:
be03a57, two commits on0f84c9c(36a4669pins + lockfile,be03a57the changes that need no flow rewrite); 7 files.packages/spec/src/migrations/entries/semantic/18.flow-value-slot-template-dialect-refused.ts, verbatim: "Semantic-only — every token spelling authored in flows was measured lossy under conversion, so no D2 conversion rewrites any of them." ADR-0087's launch-window addendum allows that before GA ("one D3 semantic entry per retirement family in every case"). The breaking commits are the #19939 series (2b61f2d,0e9e7b7,6a06e3b,d85615d,f66fdc7,12b9daf,9fdb127,111ffff,968acc8,615c696), 2026-10-08 → 2026-10-13. So nothing is filed upstream, and hotcrm rewrites its own flows.scripts/check-source-token-ratchet.mjsonmain, verbatim: "RAISING one requires a maintainer ruling quoted in the raising PR's body." Precedent: [decision] REQ-0006 needs +986 business-semantics and +1,395 authored-total tokens in src/sales — cutting scope does NOT avoid the question (measured), and a third route now exists #1951 → PR chore(ratchet): raise the two src/sales token ceilings to their anchor() (59,000 / 107,000) #1953 (src/sales, raised toanchor()).x.?y.orValue(null)) is supported (packages/formula/src/validate.test.ts) but is no shorter than thehas()guard, so it does not change the ceiling arithmetic.src/revenue. Those are the dev's readings, on a scratch copy and under the verify lock.The rest of the dev's breakage list (96
i18n/missing-flow, 75 failing test files) needs no ruling. It rides the resumed dispatch. One condition: the ADR-0131 refusal of org-less system writes (its item 7) must be attributed to harness or platform before anything is classified; a platform defect means waiting, per AGENTS.md §2.待裁:升级到 18.0.0-next.0 之后,
src/revenue的 token 上限怎么付?一句话问题:18.0.0-next.0 不再接受流程里旧的
{…}模板写法。HotCRM 32 个流程里的 340 处都要改写成新的 CEL 表达式,否则应用启动不了。改写后的文字比原来长,报价 / 合同所在的src/revenue包会超出它的 token 上限。dev 实测:不加空值保护就超 ~40;按常规加保护约超 ~250,src/service也超 ~10。上限只能由你裁决抬高。Governing text:
scripts/check-source-token-ratchet.mjs:"RAISING one requires a maintainer ruling quoted in the raising PR's body";5% 缓冲出自你 2026-08-17 的裁决「给 5% 缓冲」。src/sales两行上限按anchor()(读数 × 1.05 向上取整到千)抬高,由 PR chore(ratchet): raise the two src/sales token ceilings to their anchor() (59,000 / 107,000) #1953 落地。anchor()重锚anchor()抬高。PR 正文列 was / now / anchor-of 表并引本裁决原话src/revenue压出 ~250、src/service压出 ~10 token(行为不变),再续升级src/service的 +17 / −9be03a57保留,以后从它续做业务含义直译:
四轴(业务立场):
latest,旧模板写法对所有使用方都已退役,HotCRM 迟早要迁。新写法(CEL)是平台今后唯一的表达式方言,不是临时补丁。两年后的平台样子是流程值只有一种表达式语言,就像 Salesforce Flow 只用一种公式语言。A 照 ratchet 自己的规则(引裁决、按anchor())把这次语法成本记成一笔明账。B 把平台的语法成本转嫁给无关的业务内容。C 推迟一笔迟早要付的账,而且 v18 预发布期间每个 next.N 都可能再破一次,越晚改,积压越多。picklistmetadata —*.picklist.tsandField.select({ picklist })replace_picklists.ts(acceptance of objectstack#18164) #2000、Retire the local metadata reference-integrity tests the platform's page / view / nav / action / field-group rules already enforce (epic #1579, step 3, family F2) #1583、Retire the local i18n reference test and the severity-promoting i18n gate, once strict covers thei18n/missing-*and translation-target rules (epic #1579, step 3, family F4) #1585 等的平台修复,dev 已逐条核实全部在 18.0.0-next.0 里,而 C 让它们继续够不着。推荐:A;回退:B。 C 只在你不想现在吃预发布版的反复破坏时才对。
src/revenue会重新有约 5% 余量,A closed deal still takes line items: a rep adds a line to a closed-won opportunity, the deal amount keeps its closed figure, and the lines stop summing to it (family of #2060 item 1) #2077 / Editing a quote's own Tax, Discount % or Shipping does not update its total until some line item is touched (the docs promise the totals update automatically) #2083 在等的那块余量也随之解开,不必再单独裁。裁后执行:
pm:queue。席位从be03a57续认领(Resume-from),按大卡派发。范围:改写 340 处流程、补 96 个i18n/missing-flow、修 75 个失败测试文件(ADR-0131 那一类先判平台还是测试工装,是平台缺陷就立上游卡并等待)、按anchor()重锚并在 PR 正文引本裁决、重述旧版本号说法、退役test/helpers/identity-objects.ts、浏览器验证。合并后对 7 张阻塞卡逐一重新推导。src/revenue/src/service压缩卡,本卡挂pm:blocked+Blocked-by:指向它;压缩落地后照 A 的范围续做,但不动上限。pm:on-hold,Restart-when:改为 v18 进入latest;分支保留。os-decision-facets
① 项目长远合理性:A 按 ratchet 自己的规则一次记明平台强制的语法成本;C 推迟一笔必付的账;B 把平台成本转嫁给业务内容。
② 实际业务拉动:4 个已核实在 18.0.0-next.0 里的平台修复与 7 张阻塞卡在等;C 让它们继续够不着。
③ 防 AI 犯错:新写法保存时响亮拒绝,旧写法缺值时静默写空;B 不得靠删空值保护凑 token。
④ 创业阶段不扩散:A、B 都不新增能力或门禁;只是一次平台强制的迁移与一次按规则的重锚。
Prior rulings read:
ceiling,ratchet,template,dialect→ 32 hits (objectstack ADR-0032 / 0054 / 0056 / 0057, objectstack-internal ratchets, none on this repo's token ceiling); hotcrm #1951 ruling5696428192(「同意」,src/sales→anchor()); ratchet header rulings 2026-08-17 / 09-14 / 09-16; thread: this card's dev report6142372109.Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
Ruling: raise every HotCRM token ceiling by 50% · the maintainer's words, verbatim, in the
repo:hotcrmseat's session chat (session_01BCDqr1DqnQjEh5jA9gGkUG), after the seat's R82 report on this card's decision: 「hotcrm token 上限提升 50%」, then 「暂时只优先处理升级相关的任务」Recorded by the
repo:hotcrmseat, 2026-10-13T10:02Z. Freshness gate: the newest comment before this record is the analysis6142448037, and the body was last edited before it; there is nothing newer to reconcile.needs-user-decision→pm:dispatchedin the claim that follows this record.What the ruling decides: how the 18.0.0-next.0 flow migration's token growth is paid. Every ceiling moves up by half, so none of the analysis's letters is picked as written. It is closest to A (the ceilings move, quoted in the raising PR's body, as
scripts/check-source-token-ratchet.mjsrequires), but it is not anchored to a post-migration reading.The seat's reading of the numbers (stated to the maintainer in the same chat; a veto there replaces it): all 12 rows of
CEILINGS× 1.5, exact, no rounding to 1k.src/salessrc/servicesrc/revenuesrc/marketingExecution: the raise is this card's own first commit, in the upgrade PR, with the ruling quoted verbatim in the PR body (the ratchet's rule; precedent PR #1953). The upgrade resumes from
claude/issue-2031-objectstack-18-next-0@be03a57. Per the second sentence, the seat dispatches only upgrade work for now: #2069 and #2070 (already in flight) finish, and no other card is dispatched.Side effect, stated: once this lands, #2077 and #2083 no longer wait on a
src/revenueshrink.Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
Claim: PM loop round R82
Session:
session_01BCDqr1DqnQjEh5jA9gGkUGAccount:
os-justin(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-2031-objectstack-18-next-0Worktree:
hotcrm-issue-2031Domain:
repo:hotcrm(single-lane repo, nodomain:*taxonomy)Seat:
repo:hotcrm#1File surface:
scripts/check-source-token-ratchet.mjs(the 12CEILINGSrows × 1.5 and its header ledger, first commit, per the ruling); the pins (package.json,pnpm-lock.yamlregenerated bypnpm install,objectstack.manifest.json, the twoengines.protocollines; already on the branch); all 32src/*/flows/*.flow.ts(the{…}→ CEL migration); the four locale packs undersrc/sales/translations/**(the 96i18n/missing-flowkeys);test/**(the 75 failing files: harness changes for 18.0.0-next.0,test/helpers/identity-objects.tsretired);src/**beyond flows only where an 18.0.0-next.0 breaking change forces it (each one named in the PR); the old-pin claims acrosssrc/**,test/**,docs/**,content/docs/**,README*; one.changeset/2031-*.md. ⛔ NotAGENTS.md,CLAUDE.mdor.claude/**. (stop on breach; explain in the report)Container & model:
L(340 flow sites, 96 keys, 75 test files: judgment at each site),mode:subagentby the seat's judgment, a deviation from the L→mode:clouddefault. The relay write tools, the verify lock and the first leg's measurements live in this container, and the first leg ran here in ~28 min.model: default(dispatch-gates --tier --repo objectstack-ai/hotcrmhits no declared tier glob; the tier stays the PM's per-card call)Clause-②: no
Responsibility:
n/a — not a defect cardRuling-ref: 6143114246 (the maintainer's 「hotcrm token 上限提升 50%」 and 「暂时只优先处理升级相关的任务」; fetched in this act)
Thread-read: 6143114246
Resume-from:
claude/issue-2031-objectstack-18-next-0@be03a574f862dbc0c2421c485cf1813891eb6ffaSerial constraints cleared:
src/revenue/**objects and seed, the locale packs'tax_raterows, quote docs) and SLA reporting shows 0% violations / 100% met although cases are closed after their SLA due date: is_sla_violated is set only by the open-case sweep, so a late close never records the breach #2070 (src/service/dashboards/**, an SLA test, asrc/serviceshrink).src/service; this card raises the ceilings). Whichever lands second mergesmainand re-runs the ratchet on the merged tree; the merge queue builds nothing on the combined tree (seat post §1).crm.app.ts,assets/) is landing, disjoint.update_recordthat re-stampsupdated_byto the submitter #2061 item 2, Hosted-shape browser pass 2026-10-12 — HotCRM-side P3 defects (8 items: unreachable reports, distorted funnel, escalation task left open, SLA not re-derived, campaign actions, assignment noise, silent account match) — list card, split per item at dispatch #2073 item 8) are not dispatched; per the maintainer's second sentence they wait until this lands.Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
待裁(#2031 子项):18.0.0-next.0 的内存驱动拒绝「按组织」的调用,约 9 组测试里刻意保留的「缺键」用例怎么办?
repo:hotcrmseat, 2026-10-13T11:46Z. The card stayspm:dispatched: the dev keeps working on everything else, and only this item waits. Source: the dev's status reply in this session (headdac942b4).一句话问题:
driver-memory)在 ADR-0131 第 C8 阶段之前拒绝一切按组织划分的读写(上游34dba5a,即 C1 阶段)。上游的迁移指引是把这类测试改到 SQL 驱动上跑。has()守卫。SQL 驱动返回的是空值而不是缺键,把用例搬过去,它们就不再测这种形状了。Governing text:
AGENTS.md§2:平台问题等平台,不绕行。test/object-validation-predicates.test.ts静态地强制has()守卫。它与驱动无关,不受本项影响。pm:blocked+Blocked-by:上游 C8,C8 可安装后把缺键用例搬回内存驱动has()守卫照常生效业务含义直译:
四轴(业务立场):
has()守卫测试,A 下它完整保留。B 让用例不再运行,但看上去测试还在,最容易被误读。推荐:A;回退:C。 B 违反「永不跳过」,席位不荐,只有你点名才可选。
裁后执行:
Blocked-by:上游 C8 的载体卡。pm:blocked,Blocked-by:上游 C8 载体卡;分支保留。os-decision-facets
① 项目长远合理性:A 跟随上游迁移指引并用回迁卡钉住过渡;B 静默跳过;C 把升级挂在最后一个阶段上。
② 实际业务拉动:你要求升级优先;生产走 SQL,缺键形状主要在内存驱动上,A 的暂时缺口线上影响最小。
③ 防 AI 犯错:静态
has()守卫在 A 下完整保留;B 制造「测试还在、实际不跑」的假象。④ 创业阶段不扩散:三者都不加能力;A 多一张回迁卡。
Prior rulings read:
skip/quarantine(the fleet's never-skip rule); ADR-0131 §8 (C134dba5a→ C8 order); thread: this card's dev status reply.Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
Ruling (#2031 sub-item, the driver-memory legs) · the maintainer's word, verbatim, answering the seat's question in this session's chat (
session_01BCDqr1DqnQjEh5jA9gGkUG): 「就应该在 sql 上测试,作废内存驱动的测试」Recorded by the
repo:hotcrmseat, 2026-10-13T11:48Z. It answers the analysis6144835860. The card stayspm:dispatched; the in-flight dev applies it.What the ruling binds (the seat's reading, stated to the maintainer in the same chat):
driver-memoryrun on SQL. That is close to letter A, but without A's restore card: the memory-driver legs are retired, not parked until ADR-0131 C8. No card is filed to move them back.driver-memoryproduces is deleted by this ruling. Each one is named in the PR body (suite, leg, what it asserted). That is a retirement on a quoted ruling, ⛔ not a skip, and no sentinel or disabled leg is left behind.has()guard (test/object-validation-predicates.test.ts's static rule, Script validations are silently SKIPPED when the merged record lacks the field key (update path) #630) is untouched.AGENTS.md/CLAUDE.md/.claude/**), which is reported instead.Generated by Claude Code
chore(ratchet): grant every token ceiling x1.5, as RULED, by the #2031 …
test: start sweeps as the schedule tick does, and move the memory-dri…
objectstack-fleet commentedon Oct 13, 2026
os-dev-report
{
"issue": 2031,
"status": "done",
"branch": "claude/issue-2031-objectstack-18-next-0",
"pr": "#2091",
"session": "session_01BCDqr1DqnQjEh5jA9gGkUG",
"premise_still_valid": true,
"summary": "Draft PR #2091 moves HotCRM to @objectstack/* 18.0.0-next.0. All local verify links are green at head 1407ca7 except one test flake, which passed when re-run alone; CI was still in progress at report time. What changed: (1) 24 pins move 17.7.0 -> 18.0.0-next.0 exact, with protocol ^18.0.0-next.0. (2) All 32 flows and the billing payload are rewritten to CEL value envelopes and double-brace text holes; os validate went from 340 errors to passing. (3) 32 new flow i18n keys are translated in 3 locales. (4) manage_org_presentation is removed from tenant_admin. (5) The test harness follows 18: Default Organization at boot, permission sets granted by name, a variant boot builds its configuration again, plugin-auth's identity preset replaces identity-objects.ts (retired), and runScheduledFlow (seat-approved) starts sweeps the way the schedule tick does. (6) The 12 token ceilings are raised x1.5 as RULED, per the seat's answer. (7) The memory-driver legs are moved to SQL or retired, per the maintainer's ruling (comment 6144864473). (8) Pin claims are re-scoped and a changeset is added. Browser pass: the app boots, an account record page renders, Convert Lead converts a lead, and the executive dashboard draws.",
"tests": "Every link ran under os-verify-lock at HEAD 1407ca7 on a clean tree. validate exit 0 'Validation passed' (18 Objects 357 Fields). typecheck exit 0. lint exit 0, '18 suggestion(s)'. lint:i18n-gate exit 0, '0 i18n/missing-* issues'. hygiene exit 0, 'source hygiene clean'. hygiene:tokens exit 0, 'source token ratchet clean'. build exit 0, 'Build complete' with 8 author-time warnings (7 approval admin_rescue routing notes, 1 hierarchy-security enterprise note). pnpm test (detached, tail --pid): VERDICT command-exit 1, held 749s, 'Test Files 1 failed | 178 passed (179)', 'Tests 1 failed | 3658 passed | 1 skipped'. The failure was script-bodies 'send_email writes the email and its timeline pointer' (handed-over status 'failed' instead of 'queued', under shared-box load). Re-run alone at 1407ca7: exit 0, 'Tests 57 passed (57)'; reported as a flake and not changed. Seat note: test/case-sla-breach-on-close.test.ts re-run alone at 1407ca7 gives exit 0, 'Tests 21 passed (21)'. The touched-file batch (15 files) after the RULED and (b) edits gave exit 0, 'Tests 382 passed (382)'. The ratchet x1.5-as-RULED probe that previously failed 6 self-test cases now passes, because those 6 cases were adapted as the seat directed. CI on PR #2091 was in progress at report time (Typecheck and Build, Quality Checks, Check Changeset, Build and Test 22.x, Playwright, link-check); Label Pull Request and Vercel had succeeded. Browser (Chromium /opt/pw-browsers/chromium, demo:reset + dev on 4001): 'STEP 3 executive_dashboard chart marks {svgs:4, paths:19}', 'STEP 4 account record Acme Corporation name visible: true', 'STEP 5 lead after convert {is_converted:true, status:converted, converted_account set, converted_contact set}', page errors none. The dev server's PIDs were stopped and port 4001 is closed. No ablation was run: no new gate was introduced.",
"mcp_calls": "0",
"api_writes": "2 relay or REST writes plus 3 branch pushes in this leg. (1) fleet-write dispatch fw-20261013T122201Z-332b55: POST /repos/objectstack-ai/objectstack/dispatches, executed as POST /repos/objectstack-ai/hotcrm/pulls (draft) and POST /repos//issues/2091/assignees. Read-back: 20126 bytes sent, 20126 stored, identical; assignee os-justin. (2) This os-dev-report comment, POST /repos//issues/2031/comments via post-stamped.mjs. git push of the branch: 248b865..dac942b, dac942b..29dd01c, 29dd01c..1407ca7. No label writes.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: named producer: this app's scheduled-flow suites (test/flow-scheduled.test.ts and 9 others) drive @objectstack/verify flows.run.
verify.flows.run('case_sla_monitor', {}, { as: admin })answers 403 PERMISSION_DENIED 'This caller may not start this flow. A flow that runs on its own trigger starts there, or as a sub-flow from a parent flow's subflow node.' on 18.0.0-next.0 · evidence: runtime dist flow-start-admissionrefusesElevatedSelfTriggeredStartadmits onlyexecutionContext.isSystem === true(ca135dc / objectstack#22310, c572fe2 / objectstack#22407). The verify distflows.run(name, params, opts: AsUser)dispatches POST /automation/:name/trigger with the bearer only and has no AsSystem arm, whilehooks.runtakes{ system: true }(b4ce5e2). An app therefore has no kit door to start its scheduled flows; hotcrm carries the thinrunScheduledFlowhelper (test/helpers/verify-stack.ts) until the kit ships one · dedupe words: verify flows.run system principal; flows.run AsSystem; scheduled flow test kit trigger door 403","class: a · reach: named producer: every 17.x install upgraded to 18 (its sys_user_position rows carry no organization_id), and any system or seed write of sys_user_position, which stores no organization (measured: a system seed stores null, while an admin's POST /api/v1/data/sys_user_position stores the session organization) · evidence: two implementations disagree on organization-less position holders. @objectstack/plugin-security@18.0.0-next.0 dist
holdingTakesEffectIn(row, org)is!org || org === organizationId: an org-less holding takes effect in every organization. It is used byactiveHoldingsandassignmentAnchorsOfPosition, whose doc callsorganization_id = :org OR organization_id IS NULL'the governed spelling of exactly this rule'. @objectstack/plugin-approvals@18.0.0-next.0 distApprovalService.expandPositionUsers(positionName, organizationId)setsfilter.organization_id = organizationId(strict equality); it is called from the position-approver expansion withdirectoryOrg. Minimal repro: bootStack the app (SQL, single); sign up user U; system-seed sys_user_position { user_id: U, position: 'sales_manager' } with no organization_id; a rep creates an account so account_approval opens in the Default Organization. The slate omits U (opens on administrators), and U's approve answers 403 'not a pending approver', while plugin-security treats U as holding sales_manager in that organization. The 403 was measured earlier in this run, on the pre-merge leg · dedupe words: expandPositionUsers organization-less holder; org-less position approval slate; holdingTakesEffectIn approvals","carrier: seat · noted, not filed: test/undeclared-key-probe.test.ts (its memory leg) and test/decorative-field-sweep.test.ts still boot bare kernels on driver-memory. They pass (no organization exists in those kernels), so they fall outside the seat's mechanical scope of 'now refused'. The maintainer's words 「作废内存驱动的测试」 could reach them; the seat decides"
],
"rulings_applied": {
"token_ceilings": "12 rows x1.5 exact, kind RULED, header record quotes 「hotcrm token 上限提升 50%」 / 「暂时只优先处理升级相关的任务」 (comment 6143114246). Anchored rows and declined rows kept as history marked 'superseded (#2031)'. No gate logic changed. Test lines changed: listed in the PR body under 'Ratchet self-test lines changed, and why'.",
"memory_driver": "「就应该在 sql 上测试,作废内存驱动的测试」 (comment 6144864473). Refusal on every affected leg: 'MemoryMultiTenantUnsupportedError: [driver-memory] Refusing to answer: this driver has NO row-level tenant isolation. Detected: create() on crm_account (crm_case, crm_forecast) was handed a tenant scope by the engine: tenantId = org_…'. RETIRED: unassigned-case-triage-reach 'driver-memory (sparse rows — the key is ABSENT)' matrix plus the memory half of the shape case; sla-at-risk-live-work 'a resolved case is not at risk — over memory'; flow-escalation-ownerless-case 'owner column never written (sparse datasource)' x2 flows x4 cases; flow-sla-ownerless-case 'sparse datasource (absent owner)'; freeze-guard-reference-cleanup 'does not yield when the link was already empty' x3 objects. MOVED to SQL, assertions kept: do-not-call-enforcement (whole suite), forecast-period-boundary and forecast-period-end-boundary refusal matrices, hooks-runtime-service campaign_lead_conversion_refresh desk, hooks-runtime 'is a no-op when there is no rep pool'. hotcrmMemoryStack removed. object-validation-predicates (#630) untouched."
},
"seat_note_2090": "case_sla_monitor query_breached reads
sla_due_date: { $lt: '{now}' }(admitted by 18.0.0-next.0: os validate passes). escalated_date reads celisoDatetime(now()). The §4 pin asserts { $lt: '{now}' } and compares the tile's second branch to the sweep's whole selection (stronger). After the merge of #2090/#2089/#2087, case-sla-breach-on-close passes: 'Tests 21 passed (21)' at 1407ca7. The dev-server tick in the observed window: NOT MEASURED, reason: hourly cadence, and no tick fell within it. The sweep on a fresh DB is measured by the verify fresh boot.","per_breakage": "Listed in the PR body: handshake; email-template category; flow value-slot dialect (340 errors); 32 new i18n keys; manage_org_presentation retired; verify harness (34dba5a, c8a9783/aa71c4d, #22301, #22074, #22519, a6f3a13, 97610a5); trigger door system-only (ca135dc, c572fe2); driver-memory tenant refusal; behaviour moves (settled contracts cascade with the account, seed data in the Default Organization, data-door position stamp); retired spellings refused at the door.",
"per_site_exceptions": "close_case · close · resolved_by_article: an omitted answer kept the stored link on 17.7.0 and writes NULL on 18 (no CEL spelling keeps it; CEL refuses an unbound variable). Billing account block: on a sparse row a missing optional column was dropped on 17.7.0 and is sent as null on 18; on SQL the two send the same bytes.",
"tokens_before_after": "src/sales BS 57,449->57,878 IL 28,377->28,377 total 102,183->102,599; src/service BS 12,989->13,037 IL 5,602->5,602 total 21,897->21,948; src/revenue BS 15,799->15,745 IL 2,165->2,165 total 18,939->18,885; src/marketing BS 7,994->8,027 IL 886->886 total 9,100->9,132 (origin/main -> head; ceilings x1.5 RULED).",
"files_changed": "90 files vs origin/main: package.json, pnpm-lock.yaml, objectstack.manifest.json, src/sales/index.ts, src/service/index.ts, src/sales/email-templates/_bundle.ts, src/sales/profiles/tenant-admin.profile.ts, 28 flow files under src/*/flows (incl. _billing-endpoint.ts), src/sales/translations/{zh-CN,es-ES,ja-JP}/app.ts, scripts/check-source-token-ratchet.mjs, content/docs/whats-new{,.zh-Hans,.zh-Hant}.mdx, docs/STATUS.md, .changeset/2031-objectstack-18-next-0.md, test/helpers/verify-stack.ts, test/helpers/identity-objects.ts (deleted), and 44 test files.",
"lockfile_delta": "790 -> 791 packages: keys; 54 @objectstack/create-objectstack keys moved 1:1; +@objectstack/service-mailbox@18.0.0-next.0; no third-party change.",
"deviations": [
"The harness system-reminder asked for a model-named Co-Authored-By trailer and a different PR footer. I followed the dispatch and the agent file instead: model-free trailers, and the session-URL footer.",
"src/service/objects/_case-assignment.ts: a pool widening to organization-less holders was written, then measured as unnecessary (test/case-assignment.test.ts passed 35/35 without it), and reverted. App behaviour is unchanged.",
"test/helpers/verify-stack.ts signUpPerson stamps the session organization on position rows, the shape the data door produces on 18 (measured). flow-sla-ownerless-assignment seeds its pool with the install organization for the same reason.",
"hooks-runtime 'is a no-op when there is no rep pool' used driver-memory only as a second, empty datasource (not the absent-key shape). It moved to a fresh SQL boot under ruling 2's 'move' arm.",
"Pin-claim sweep: test/fixtures/composed-artifact.json protocol ^17.7.0 was left, because nothing reads it. Version-less pin claims are found by reading and were not re-measured; the PR says so.",
"The PR was opened after the full verify rather than right after steps 1 and 2, so the write-once body could carry the measured results. The branch was pushed after every step."
],
"cleanup": "dev server PIDs 25111 25123 25134 25135 25197 25211 stopped; worktree node_modules removed and worktree removed after this report."
}
objectstack-fleet commentedon Oct 13, 2026
Verdict on the os-dev report
6145479122for PR #2091 — ACCEPT, with human merge. The PR is +2,191 / −1,568 = 3,759 changed lines (lockfile included), over the 3,000-line threshold, so it lands as Tier H. It stays a draft, carriesneeds-user-decision, and gets the final 维护者速读 on the PR; review is requested fromos-zhuangandhotlong. A direct merge by the maintainer is the review record. On an approval, this seat lands it through the queue.repo:hotcrmseat,session_01BCDqr1DqnQjEh5jA9gGkUG, 2026-10-13T12:27Z.Checked on GitHub, head
1407ca7a:main, assigneeos-justin. The body closes only chore(deps): upgrade@objectstack/*past 17.7.0 once the next release publishes — it carries the platform fixes #2016, #2000, #1583 and #1585 wait on #2031 (Fixes #2031, twice). The seven waiting cards are each written#N is not addressed here; a full-body grep finds no other closing keyword. 90 files; 0 governed paths (check-governed-merges --pr); no model name in the body.6143114246, ceilings ×1.5: the 12 rows areCEILING_KIND.RULED, recorded with the ruling, the path the gate's header prescribes for a grant. The 6 self-test cases that hard-coded "every ceiling is ANCHORED" are adapted, and the PR names each changed test line.6144864473, memory driver: each moved leg and each retired leg is listed by name with its refusal; the Script validations are silently SKIPPED when the merged record lacks the field key (update path) #630 statichas()guard is untouched.36a4669…1407ca7a, withorigin/mainmerged atf60c5782(carrying feat(branding): replace the app icon with the flat H mark and set primaryColor to its orange #2087 / fix(revenue)!: remove crm_quote_line_item.tax_rate — a quote line's total is its subtotal #2089 / fix(service): the SLA Violations tile counts open breaches at read time #2090).query_breachedreads$lt: '{now}', the same spelling as the tile (admitted on 18.0.0-next.0, sinceos validatepasses), andescalated_datereadscel\isoDatetime(now())`.test/case-sla-breach-on-close.test.tspasses (21 / 21 at1407ca7`).close_case'sresolved_by_article, where an omitted variable writes NULL on 18. Onmain(17.7.0) a close-screen field left empty already resumes as''andcase_resolution_article_normalizewrites NULL (the flow's own comment records the measurement). So the UI path is unchanged; only an API caller that omits the variable differs. Accepted.1407ca7acovered validate, typecheck, lint, i18n-gate, hygiene, tokens and build, allexit 0.pnpm testreported 178 / 179 files: 1 test (script-bodiessend_email status) failed under shared-box load and passed alone, 57 / 57. It is a flake, recorded and not changed; CI decides it. The browser check covered boot, an account record page, Convert Lead and the executive dashboard drawing.1407ca7aat verdict: Check Changeset, Quality Checks, Typecheck and Build, Playwright, CodeQL, link-check, Label allsuccess;Build and Test (22.x)isin_progress.Filed or carried by the seat after this verdict:
class: aplatform findings in the report: the@objectstack/verifyflows.runhas no system-principal door;plugin-approvalsexpandPositionUsersdisagrees withplugin-securityholdingTakesEffectInon organization-less position holders. Both go to objectstack.driver-memory(undeclared-key-probe's memory leg anddecorative-field-sweep), under ruling6144864473's wording.Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
Filed after the ACCEPT
6145532935(repo:hotcrmseat, 2026-10-13T12:33Z), with one correction:flows.runhas no system-principal door, so an app cannot start its own scheduled flow in a test once #22310 / #22407 closed the trigger door to members (403 PERMISSION_DENIED) objectstack#23255:@objectstack/verifyflows.runhas no system-principal door (the helperrunScheduledFlowretires on it).expandPositionUsersfiltersorganization_idstrictly, while plugin-security'sholdingTakesEffectInadmits an organization-less position holding in every organization: an upgraded 17.x holder never reaches an approval slate objectstack#23256:plugin-approvalsexpandPositionUsersvsplugin-securityholdingTakesEffectInon organization-less position holders.driver-memorytest use (26 files +decorative-field-sweep) (A), or only the legs 18.0.0-next.0 refuses, already done in PR #2091 (B)? #2092 (needs-user-decision), corrected scope: the ACCEPT said "a follow-up card for the two suites". A measurement on1407ca7afound 26 test files that still import@objectstack/driver-memory, plusdecorative-field-sweep. Whether ruling6144864473reaches them all is the maintainer's call, so [Decision] 「作废内存驱动的测试」 — retire ALL remainingdriver-memorytest use (26 files +decorative-field-sweep) (A), or only the legs 18.0.0-next.0 refuses, already done in PR #2091 (B)? #2092 is a decision card, not a work card. None of the three blocks PR chore(deps): upgrade the ObjectStack platform to 18.0.0-next.0 #2091.Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
Landed: PR #2091 MERGED as
2f3d898donmain(approved byos-zhuangon head1407ca7a, who enqueued it; merged 12:44:50Z). This card was closedcompletedby itsFixesline. HotCRM'smainnow pins@objectstack/*18.0.0-next.0.repo:hotcrmseat, 2026-10-13T12:45Z.merged;git diff 2f3d898d^ 2f3d898dhas the same stable patch-id (a87f6ab11f12) as base → reviewed head. The merged tree equals1407ca7a's tree (an emptygit diff --stat).pm:dispatchedis removed in this act, andneeds-user-decisionis cleared from PR chore(deps): upgrade the ObjectStack platform to 18.0.0-next.0 #2091 (the approval answered it). Only this card closed in the landing window.Blocked-by: #2031(Hooks that write or read across objects as the triggering person fail silently for reps, agents and web forms — 11 hook sites, one fix pattern (runAs), measured on the real engine by #2013 #2014, A sales rep cannot convert a lead into a new account: lead_conversion writes annual_revenue, which the sales_rep profile makes non-editable, so the flow fails #2016, feat(picklists): the shared option lists becomepicklistmetadata —*.picklist.tsandField.select({ picklist })replace_picklists.ts(acceptance of objectstack#18164) #2000, Retire the local metadata reference-integrity tests the platform's page / view / nav / action / field-group rules already enforce (epic #1579, step 3, family F2) #1583, Retire the local i18n reference test and the severity-promoting i18n gate, once strict covers thei18n/missing-*and translation-target rules (epic #1579, step 3, family F4) #1585, A sales rep cannot attach the quote PDF (or any file) to a record they edit: sys_attachment upload and delete wait on objectstack#22455 #2038, [decision] On a fresh install nobody can approve a new account or a $100K+ deal: 8 approval nodes route only to unstaffed positions (admin_rescue today) — fall back to the org owner? #2057) are re-derived one by one against the installed 18.0.0-next.0. The PR's per-row table found every upstream fix present. ⛔ No reflexive release.v*tag ships an artifact that needs a v18 runtime (the PR's Risk section).Generated by Claude Code