Repository navigation
verify: the in-process handle boots a leaner stack than serve and has no door for eight things an app's tests need (requires[] capabilities, system/predicate update, the form door, user-less triggers, …), measured by hotcrm#2013 #22301
Description
Activity
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsTriage: first grade,
bug·priority:p2·domain:cli·area:devpath·pm:queue. Direction: the verify handle boots whatserveboots, and gains the missing doorsTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-08T13:53Z. ⛔ Not a claim, ⛔ not a dispatch.Triage: lands in
packages/verify(harness.ts), with item 1 reusing the provider mapping frompackages/cli⇒domain:cli; rationale: verify sits with the CLI lane (as #15953 does).- Why p2: under the maintainer's B′ ruling on verify: an in-process handle on the booted stack — run a hook, flow, action or validation rule against the REAL engine and assert, so an app never fakes
ctx.apiagain (epic hotcrm#1579, step 5a) #15951, an app's tests run on the platform through this handle. Each gap forces hotcrm to keep one local path, and item 1 means an app's required capabilities are not even booted. - Item 1 first: move the capability-to-provider mapping (
CAPABILITY_PROVIDERS,CapabilitySpec) to a home bothserveandverifyread, so the handle boots the app'srequires[]exactly asservedoes. Then the remaining doors, one PR each or grouped by the seat. Clause-②: yesfor each new door on the published handle. The contract-review tier is owed.- Done when: each item deletes the matching hotcrm local path (hotcrm#1595's rule).
- Why p2: under the maintainer's B′ ruling on verify: an in-process handle on the booted stack — run a hook, flow, action or validation rule against the REAL engine and assert, so an app never fakes
- addedarea:devpathThe road — create, dev, verify, publish/install, connect an agent, iterateThe road — create, dev, verify, publish/install, connect an agent, iteratebugSomething isn't workingSomething isn't workingpriority:p2Medium: important, M3Medium: important, M3
on Oct 8, 2026 objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsEvidence for item 9 (cel-dated seed replay under
bootStack), measured twice more by therepo:hotcrmseat's devs on hotcrm9451b6de/49fe305awith@objectstack/*17.7.0 (objectstack-ai/hotcrm#2016 report6062013748, objectstack-ai/hotcrm#2021 report6063336229).repo:hotcrmseat,session_012zh91QzFgePbkmuHnugLN3, 2026-10-08T15:35Z.- The rows: on every boot through
@objectstack/verify(3 of 3), the SeedLoader refusescrm_campaign#0 "Q3 Enterprise Email Nurture" and Implement ObjectStack protocol specification with Zod schemas and TypeScript interfaces #3 "Operations Platform Launch". Both arestatus: in_progresswith dates written as celdaysAgo(15)/daysFromNow(21)(hotcrmsrc/marketing/data/marketing.seed.ts:178-180,:223-227). The refusal is the app hook's own: "Campaign cannot move to in_progress without both start_date and end_date". Each of theircrm_campaign_memberrows then fails with "Campaign is required" (23 SeedLoader failures per boot). - The likely seam (NOT proven): the hook reads the dates only when
typeofis string (campaign.hook.ts:74-81). So the replay hands it either the unresolved cel envelope or a resolved non-string value (daysFromNowreturns a JSDate,formula/src/stdlib.ts). Which of the two this is decides whether the fix is the boot's (resolve and serialise asservedoes) or the app's. hotcrm WAITs on this card for that answer. - NOT MEASURED: an
objectstack devboot of the same commit, run for 150 s on fix(i18n): add view form end_user_controls translations #2016, logged neither refusal. So reach beyond the verify boot is unestablished.
Generated by Claude Code
- The rows: on every boot through
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsItem 9 narrowed: the seed divergence is the verify boot's. Measured by the dev of objectstack-ai/hotcrm#2018 on hotcrm
dc58e047, 17.7.0 (report6064159813).repo:hotcrmseat,session_012zh91QzFgePbkmuHnugLN3, 2026-10-08T16:16Z.objectstack dev -p 4925 --freshon a scratch SQLite file: all 7 seededcrm_campaignrows are stored, including "Q3 Enterprise Email Nurture" (dates 2026-09-23 → 2026-10-29) and "Operations Platform Launch" (2026-09-26 → 2026-10-22), plus 51crm_campaign_memberrows. No refusal.bootStack/bootStackOnceof the same artifact (memory and SQL): 46[SeedLoader] Failed to writelines per boot. The app'scampaign_validationrefuses those two campaigns, and every one of their members then fails "Campaign is required".
So the same cel-dated seed rows (
cel`daysAgo(..)`/cel`daysFromNow(..)`) reach the app's hook in a formserve/devnever hands it. The fix is on the verify boot's seed path; hotcrm builds nothing for it.
Generated by Claude Code
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsThis amends my grade (
6061416383):domain:cli→domain:spec. I graded this cardClause-②: yes, and perexecution-duties.md:101(「命中即 spec 车道的活」) anddispatch-gates: "a hit outside those lanes is spec-lane work and moves there" a widening of a published surface is spec-lane work wherever it lands. The landing (packages/verify, item 1 reusing the provider mapping frompackages/cli), the grade and the direction are unchanged. Thedomain:cliseat reviews the files in its own package.Triage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-08T16:58Z. ⛔ Not a claim, ⛔ not a dispatch.objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsClaim: PM loop round 1 (stage 1 of this card: items 1 and 9, "the handle boots what
serveboots", per triage6061416383's item-1-first direction) · 2026-10-08T18:08Z
Session:session_01DhTqaEHqPVSVnAkjG3jywn
Account:os-sales(the seat's linked user asGET /useranswers it; the card's assignee from this act)
Branch:claude/issue-22301-verify-boot-parity
Worktree:objectstack-issue-22301
Domain:domain:spec
Seat:domain:spec#2(seat post #18549)
File surface (atorigin/main28bff18d0or later; stop on breach and explain in the report):- Item 1:
packages/cli/src/commands/serve.tsServe.CAPABILITY_PROVIDERS(about:1870) andCapabilitySpec(about:962) move to one home that bothserveand@objectstack/verifyread (where it lives is measured: a packageverifyalready depends on, with no new cycle).packages/verify/src/harness.tsbootStackthen mounts the providers an app'srequires[]names, by the same ruleserveuses (top level when present, otherwise each package body, as PR fix(cli): os serve and seven sibling readers read a multi-package config's package-owned keys through its package bodies #22321 left it). Both readers are re-pointed in the same PR, ⛔ with no second copy of the mapping. - Item 9:
bootStack's seed replay hands a cel-dated seed value (cel`daysFromNow(..)`) to the engine in the formserve/devdo (seed-loader.tsabout:1138→formula/src/seed-eval.tsabout:74). The divergence is located first. The fix is on the verify boot's seed path, or on the shared seed loader if that is where verify diverges. - Tests in
packages/verify(arequires: ['…']app boots the provider; a cel-dated seed row is stored, the controls unchanged) and inpackages/clifor the moved mapping..changeset/22301-*.md. - ⛔ Not items 2–8 (new handle doors): each is a later stage on this card. ⛔ Not
packages/rest. - Declared cross-lane files:
domain:cli(packages/verify,packages/cli), declared on [PM seat] domain:cli — 🟢 marchtian · session_01B5CHJNXuuqzChM4w6hkTN4 #6024.domain:engine(packages/metadata-protocol/src/seed-loader.ts,packages/formula) only if item 9's fix lands there, declared on [PM seat] domain:engine — ⏳ vacant #6367 when it does.
Container & model:M,mode:subagent,model: opus(dispatch-gates --tier --repo objectstack-ai/objectstackon these paths: no path-derived mandate; the default tier). A contract review atCONTRACT_REVIEW_TIERis owed before enqueue (this claim'sClause-②: yes), from an isolated at-tier subagent.
Clause-②: yes (widening:bootStackmounts the providers an app requires, and the provider mapping gains a public home both readers import)
Responsibility:packages/verify'sbootStackboots a fixed plugin set and replays seeds unlikeserve| none: the handle is the only platform path for an app's tests under the maintainer's B′ ruling on verify: an in-process handle on the booted stack — run a hook, flow, action or validation rule against the REAL engine and assert, so an app never fakesctx.apiagain (epic hotcrm#1579, step 5a) #15951 | every app testing through@objectstack/verify; hotcrm measures both (test: run the hook, flow and action suites on @objectstack/verify and retire the five hand-built harnesses hotcrm#2013, hotcrm reports6062013748,6063336229,6064159813)
Thread-read: 6064912705
Serial constraints cleared: - PR fix(cli): os serve and seven sibling readers read a multi-package config's package-owned keys through its package bodies #22321 (
serve.ts, the multi-packagerequiresreader) has landed as28bff18d0, the base of this claim, and [finding] cli(serve):os serveresolves capability providers from a multi-package artifact's top-levelrequiresonly — a package'srequires: ['automation']is not loaded at boot #22288 is closed with it. The dev works on the merged code. - No open PR touches
packages/verify/src/**,serve.ts,seed-loader.tsorformula/src/seed-eval.ts(14 open PRs read at this stamp; the Version Packages PR chore: version packages #21988 touches onlypackages/verify/CHANGELOG.mdandpackage.json). area:devpathis also on this seat's [maintainer] validate: thefield-no-consumerswarning is one 856-character line, printed by validate, build and dev alike — one-line verdict +rule:id + a pointer to the full reasoning (os explain, which today takes only schema names) #22161 (packages/lint,packages/cli/src/commands/explain.ts): the file surfaces are disjoint.
- Item 1:
109 remaining items
objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsClaim: PM loop round 2 (item 7 of this card: no lowered-body door. The handle boots the source config, so the production body-only path, and its refusal envelope, are not what a handle test runs) · 2026-10-11T07:08Z
Session:session_016njDy8ozy9B9Ns5Y8kAWEK
Account:marchtian(the seat's linked user asGET /useranswers it; the card's assignee from this act)
Branch:claude/issue-22301-item7-lowered-body-door
Worktree:objectstack-issue-22301-i7
Domain:domain:spec
Seat:domain:spec#2(seat post #18549)
File surface (atorigin/maina8f24b092c; stop on breach and explain in the report):packages/verify/src/handle.ts/harness.ts: a way for a handle test to boot, or drive, the LOWERED artifact. That is the hook and actionbodythatlowerCallablesproduces and the QuickJS sandbox runs, asobjectstack dev/ production boot it, so a test runs the body-only path and meets its refusal envelope. Its JSDoc and the docblock door roster are in scope.packages/verify/README.md, pins inpackages/verify(each ablation-verified), and any consumer that hand-builds the handle's type (packages/qa/dogfood/test/rls-runner.test.ts's fake stack).domain:cli, declared cross-lane ONLY if the measurement shows the lowering must be reached from verify:packages/cli/src/utils/lower-callables.ts/extract-hook-body.ts.- ⛔
@objectstack/verifydoes not depend on@objectstack/clitoday. A new dependency edge, or moving the lowering to a shared package, is a package-graph decision. The report states it with the four axes before building it, or returnsneeds_decision.
- ⛔
.changeset/22301-*.md:@objectstack/verifyminor(plus any package whosesrcmoves).Clause-②: yes (widening).- ⛔ Zero re-implemented semantics (verify: an in-process handle on the booted stack — run a hook, flow, action or validation rule against the REAL engine and assert, so an app never fakes
ctx.apiagain (epic hotcrm#1579, step 5a) #15951 B′): the door boots or reaches the REAL lowering and the REAL sandbox. It never re-derives what they would do. - ⛔ Item 1's remaining divergence (the MCP / pinyin host defaults) is not in this claim.
Container & model:S,mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate; floor sonnet · default opus · ceiling fable). A widening of a published handle: the contract review atCONTRACT_REVIEW_TIERis owed before enqueue.
Clause-②: yes (widening)
Responsibility:@objectstack/verify's handle boots the source config, so an in-processhandlerruns where production runs the lowered sandboxedbody. A hook that passes a handle test can TypeError or be refused once lowered (item 9's measured divergence is the precedent) | no platform path boots the lowered artifact under a test | who reaches it: hotcrm'srunShippedHooklocal path (test/helpers/verify-stack.ts), per the card body
Thread-read: 6106511170
Prior rulings read:6070767186(A, item 1; "Items 2–8 … are their own stages and do not wait"; the handle is not a second boot path); verify: an in-process handle on the booted stack — run a hook, flow, action or validation rule against the REAL engine and assert, so an app never fakesctx.apiagain (epic hotcrm#1579, step 5a) #15951 B′ (tests reach the real engine, zero re-implemented semantics).
Serial constraints cleared: - None of the 13 open PRs touches
packages/verify/**,packages/cli/src/utils/lower-callables.ts,extract-hook-body.tsorhook-body.ts; their file lists were read in this act. - Item 6 (PR feat(verify): the handle observes the writes the engine receives, a hook's refused write included #22781) has landed as
a8f24b092c. - No other claim on this card is in flight.
This act moves the card
pm:queue→pm:dispatchedand assignsmarchtian.
Generated by Claude Code
objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 22301,
"status": "done",
"scope": "item 7 only (claim 6106527562)",
"branch": "claude/issue-22301-item7-lowered-body-door",
"pr": "#22808",
"head": "c14e269ed0",
"session": "session_016njDy8ozy9B9Ns5Y8kAWEK (the PM session that dispatched this subagent; the container CLAUDE_CODE_REMOTE_SESSION_ID resolves to the same id)",
"premise_still_valid": true,
"reported_at": "2026-10-11T08:46Z",
"summary": "Item 7's door is a boot option: bootStack(config, { artifact }) (BootOptions.artifact, packages/verify/src/harness.ts) reads the compiled objectstack.json with the runtime's own loader (loadArtifactBundle, unwrapEnvelope, as createStandaloneStack reads it) and mounts it as the app (new AppPlugin(bundle)) where a source boot mounts config. AppPlugin binds each hook's body ahead of its handler through the QuickJS body runner, so every handle door then runs what the build shipped; the composition (requires, own plugins, default profile, datasources) is still read from config, as objectstack serve CONFIG composes it. Mechanism assumptions measured: (1) cli depends on verify, verify does not depend on cli, so route (a) was taken: the caller builds the artifact with the real CLI, and no package edge is added; (b) is not needed, because in-memory lowering would be the second artifact route ruling 6070767186 A rules out; (c) os verify building the artifact is a later convenience with no new edge. (2) bootStack already ran a body for any bundle it mounted, so the door is mount-the-artifact plus docs and pins, and no runtime, objectql or cli file is touched. (3) The envelope, pinned. A handler writing ctx.dispatch.scope lowers cleanly and its body throws TypeError: the handle rejects with SandboxError (no code), REST answers 500 INTERNAL_ERROR, and no row is stored; the source boot stores it. A declared VALIDATION_FAILED/400 refusal reaches the handle as the handler's Error in-process and as a SandboxError carrying the same code and status lowered; REST serves both paths a byte-equal 400 body. Four door refusals, each with an ADR-0112 code and status: unloadable artifact RESOURCE_NOT_FOUND/404 (never falls back to source), artifact of another app RESOURCE_CONFLICT/409, config carrying onEnable INVALID_REQUEST/400 (no artifact carries one; serve's graft is a cli rule verify cannot import), empty option INVALID_REQUEST/400. hotcrm's runShippedHook can move onto bootStack(config, { artifact: 'dist/objectstack.json' }) after objectstack build, once a release carries this.",
"tests": "At c14e269 (final head, merges origin/main 5fc57b3):pnpm --filter @objectstack/verify exec vitest run --maxWorkers=2 src/harness.artifact-door.test.ts→ Tests 6 passed (6);pnpm --filter @objectstack/dogfood exec vitest run --maxWorkers=2 test/lowered-body-door.dogfood.test.ts→ Tests 8 passed (8);pnpm --filter @objectstack/spec build && … check:generated→ all 14 generated artifacts up to date. At 197daa3 (the later merge brought spec contracts, docs and governance text only): the whole verify suite → Test Files 30 passed, Tests 240 passed;pnpm --filter @objectstack/verify typecheck→ exit 0, 'check:test-typecheck: OK … 0 error(s)'; dogfood lowered-body-door + rls-runner + showcase-declarative-endpoints → 3 files / 42 tests passed;pnpm --filter @objectstack/dogfood typecheck→ 0 errors (closure built); cli--project uniton serve-verify-security-parity + serve-audit-registration (both read harness.ts) → 2 files / 20 tests passed. The cli integration tier is declared to CI (no cli file moved). Ablations, predictions written at 4f8ab6b before any run (scratch file sha1 0798d3a9c2), every mutation through scripts/ablation-replace.mjs (anchor hit, blob changed, restore = blob == HEAD and empty git diff HEAD). A1, mount config instead of the artifact: verify 1 failed / 5 passed as predicted. A1 in dogfood (verify is dist-resolved there): the mutate-leg rebuild exited 1, output not captured; ablation-dist-preflight found ABLATION_A1_MOUNTS_CONFIG in packages/verify/dist; 3 failed / 5 passed as predicted (lowered TypeError, REST 500, lowered SandboxError envelope). Restore leg: rebuild exit 0, --absent preflight exit 0, tree clean. A2, an unloadable artifact falls back to the source: 1 failed / 5 passed as predicted. Its first attempt was a no-op: the replacement contained the anchor, ablation-replace refused it ('anchor count moved 1 -> 1') and restored, and it was re-run on another anchor. A3, the other-app check deleted: 1/5 as predicted. A4, the onEnable check deleted: 1/5 as predicted. A5, relative path against process.cwd(): 2 failed / 4 passed as predicted. A6, the runtime marshals dispatch.scope: NOT MEASURED. Its prediction assumed dogfood resolves @objectstack/runtime from source; it resolves dist (the stack traces were source-mapped), so it would need two runtime rebuilds, and A1 already shows the pins discriminate. Lint, narrowed: eslint --no-inline-config --format json over the 4 changed TS files → 4 files, 0 errors, 0 warnings; each file is in the config's population (--print-config resolves its rules); eslint.config.mjs enables no type-aware linting (no parserOptions.project), so untouched files' verdicts cannot move. Repo-wide pnpm lint is CI's. Additional probe: require('@objectstack/verify') from packages/qa/dogfood resolves dist/index.cjs with bootStack a function; @objectstack/runtime CJS exports loadArtifactBundle and isHttpUrl.",
"gates": "At c14e269:node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 68 commands, and all 68 exited 0.--ranwith recorded exit codes: '68 derived famil(ies) accounted for — 68 run, 0 NOT-MEASURED (a DERIVED zero — all 68 recorded an exit code and none of them is 3)'. Named readings: check:dual-build-cjs-loads '107 published require entry point(s) across 66 package(s) load'; check:dts-closure '171/171 declared declaration file(s) present'; check:cross-package-test-inputs OK (30 packages, all declared); check:test-source-alias OK; check:nul-bytes OK (10839 files); check:published-files OK; check:type-check-coverage OK; check:type-check-debt OK; check:issue-citations '6 across 2 file(s) resolve'; check-adr-0087-registration and check-empty-changeset green (1 non-breaking declaring changeset). PR CI at report time: 32 check runs, 10 completed, 20 in_progress (honest value; not awaited).",
"line_budget": "n/a: no skills/** or ledgered governed file touched; changed lines 574 (+565 / -9) over 6 files, under the 3000 human-merge threshold.",
"files_changed": [
".changeset/22301-verify-lowered-body-door.md",
"packages/qa/dogfood/test/lowered-body-door.dogfood.test.ts",
"packages/verify/README.md",
"packages/verify/src/handle.ts",
"packages/verify/src/harness.artifact-door.test.ts",
"packages/verify/src/harness.ts"
],
"deviations": [
"File surface: the divergence, envelope and control pins live in packages/qa/dogfood/test/lowered-body-door.dogfood.test.ts, not packages/verify. The real lowering is reached there through build-shaped-artifact.ts (cli source by relative path, an existing route, no manifest edge). From verify it would be a reverse test-time edge onto the package that depends on verify. packages/verify/src/harness.artifact-door.test.ts pins the door and its four refusals, with an artifact whose body is in the authored form os build ships unchanged.",
"Labels: zero label writes. The dispatch budget names git push, one pr_create and one comment, and no label write.",
"origin/main moved one commit after the final merge (0984817, plugin-webhooks redeliver door). It was not merged, so the measured head is kept; CI and the merge queue rebuild onto current main.",
"A2 first attempt was a no-op refused by the tool (see tests); A6 NOT MEASURED (see tests); the A1 dogfood mutate-leg build exited 1 with output not captured, while the dist preflight proved the marker reached dist."
],
"mcp_calls": "0 (no MCP GitHub tool called).",
"api_writes": "2 relay actions, each sent as one repository_dispatch to the board through scripts/pm/fleet-write/dispatch.mjs. (1) pr_create, dispatch fw-20261011T084446Z-93159e, run 38125890381: POST /repos/objectstack-ai/objectstack/pulls (draft) then POST /repos//issues/22808/assignees (marchtian); read-back 12298 bytes sent = stored. (2) comment: POST /repos//issues/22301/comments, this report. Plus 7 git pushes of the branch (not REST). Reads: gh api on issue 22301, its comments, PR 22808 and the head's check-runs.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: public door measured. On a boot of the lowered artifact, REST POST /api/v1/data/lbd_stash_note answers 500 INTERNAL_ERROR and stores nothing, while the source boot of the same config answers 2xx. Pinned in packages/qa/dogfood/test/lowered-body-door.dogfood.test.ts at c14e269. Named producer: lowerCallables / extractHookBody (packages/cli/src/utils/extract-hook-body.ts), the os build lowering, which turns a handler writing ctx.dispatch.scope into a body at exit 0. The sandbox hands a body ctx.dispatch as { mode, index } without scope (buildSandboxContext, packages/runtime/src/sandbox/body-runner.ts; documented on HookContextSchema.dispatch), so every insert TypeErrors: 'cannot set property 'stashed' of undefined'. Same family as the forbidden-pattern refusals for .sudo( (#14010), .create( (#16249) and the Intl free identifier (#14301): a member real in-process and absent from the body. Unmeasured sibling: ctx.submitted, also not marshalled, reads undefined. · dedupe words: dispatch.scope lowered body TypeError; extractHookBody forbidden pattern dispatch; hook body scope not marshalled; lowered handler passes in-process fails sandbox; submitted not marshalled body",
"carrier: none · noted, not filed. The artifact door refuses a configuration carrying onEnable rather than grafting it. objectstack serve CONFIG grafts it with graftAuthoredRuntimeMembers (packages/cli/src/utils/graft-runtime-hooks.ts), which verify cannot import. If an app's artifact-door tests need onEnable, moving that rule into a package both import is the route. Recorded in PR 22808's Acceptance notes."
]
}objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actions✅ ACCEPT — PR #22808 at
c14e269ed0(item 7,Part of #22301). Contract review PASS; ready and enqueueddomain:specseat 2 (#18549) ·marchtian· sessionsession_016njDy8ozy9B9Ns5Y8kAWEK· 2026-10-11T09:06Z · holder of claim6106527562. Report:6107272381.Checked against the diff and the record, not the report:
-
The door:
bootStack(config, { artifact }).- It reads the compiled
objectstack.jsonthrough the runtime's ownloadArtifactBundle(withunwrapEnvelope, the same function and optionscreateStandaloneStackuses) and mountsnew AppPlugin(bundle)as the app. AppPluginbinds each hook'sbodyahead of itshandler, through the QuickJS body runner, so every handle door runs what the build shipped.- Composition (
requires, the app's own plugins, profile, datasources) is still read fromconfig.
- It reads the compiled
-
Ruling
6070767186A holds. The artifact path is the deployment's, so this is not a second boot path.loadCompiledArtifactis a type check, anonEnablecheck, the loader, a null check and an id compare, with zero re-derived lowering or sandbox semantics (verify: an in-process handle on the booted stack — run a hook, flow, action or validation rule against the REAL engine and assert, so an app never fakesctx.apiagain (epic hotcrm#1579, step 5a) #15951 B′). -
The package graph:
@objectstack/clidepends on@objectstack/verify, and the diff adds no edge back, insrcor in tests.- The caller builds the artifact with the real CLI.
- The dogfood pins reach the real lowering through
build-shaped-artifact.ts. That is a pre-existing route ([finding]@objectstack/cliand@objectstack/plugin-hono-serverare the only two published packages with noexportsmap — everydist/**module is deep-importable, and one already is #12879), carried by dogfood's devDependency on cli.
-
Four refusals, each with an ADR-0112 code, thrown before any kernel exists:
- an unloadable artifact →
RESOURCE_NOT_FOUND/ 404, ⛔ never a fallback to source; - another app's artifact →
RESOURCE_CONFLICT/ 409; - a config carrying
onEnable→INVALID_REQUEST/ 400 (the graft is cli's); - an empty option →
INVALID_REQUEST/ 400.
Relative paths resolve against the host root, not
process.cwd(), and this is pinned. - an unloadable artifact →
-
The envelope pins are real divergences against the real lowering and the real sandbox.
- A handler writing
ctx.dispatch.scopelowers at exit 0 and TypeErrors in the body: the handle gets aSandboxErrorwith no code, REST answers 500, and no row is written. The source boot stores the row. - A declared
VALIDATION_FAILED/ 400 passes through both paths. REST's parsed bodies are equal. - A lowerable control behaves identically both ways.
- A handler writing
-
Ablations: A1–A5 were predicted and observed. A6 was not measured, and A1's dogfood mutate-leg build output was lost, but the dist preflight proved the marker reached dist. The review judged the evidence sufficient.
-
Semver:
@objectstack/verifyminor,Clause-②: yes (widening). No other published package moved. -
Contract review: at-tier PASS
6107414318onc14e269ed055e8e8ef350e701040cafd14f9cee3. -
CI on
c14e269ed0: 35 runs, 32 success, 3 skipped (Build Docs,Console Pin Gate,Packed-tarball smoke (opt-in)), 0 failure. All required contexts green. -
Paths: 6 files, +565 / −9. Not governed. First line
Part of #22301, no closing keyword. The card stays open for item 1's remaining divergence.
Routed from this stage: #22810 (filed by this seat,
bug,pm:queue, awaiting triage).objectstack buildlowers a handler that writesctx.dispatch.scope(or readsctx.submitted) at exit 0, although the sandbox marshals neither, so every lowered write TypeErrors and REST answers 500. Note for #22810's dev: a build-side refusal un-lowers this PR's dogfood divergence pin (lbd_stash) and trips its anti-vacuity case. Move that pin to another real divergence in the same PR.Acceptance notes (carrier: this card's close-out; none blocking):
- The
bootStackJSDoc and the changeset sayservecomposes the artifact boot "the same way". That holds for a NON-host configuration only: for a host config,shouldBootWithLibraryis false andserveboots the source module. The PR body carries the qualifier; the JSDoc does not. This is for a later text round. - Serve-side, read from source and unmeasured (item 1's territory):
mergeBootConfigserves the boot result'spluginswhole. So a non-host config's non-instancepluginsentries do not reachserve's artifact boot, while the handle mounts them. - The
onEnablegraft (graftAuthoredRuntimeMembers) stays cli's. An artifact-door test that needsonEnablewould need that rule moved into a package both import. There is no measured pull. - "byte-equal" in the PR body is
toEqualon parsed JSON.
hotcrm:
runShippedHookcan move ontobootStack(config, { artifact: 'dist/objectstack.json' })afterobjectstack build, once a release carries this door.Landing: the PR goes ready and auto-merge is enabled now. The landing record follows the merge.
Generated by Claude Code
-
objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsLanded: PR #22808 →
27f0d83fb8(item 7,Part of #22301). The card goes back topm:queuefor item 1's remaining divergencedomain:specseat 2 (#18549) ·marchtian· sessionsession_016njDy8ozy9B9Ns5Y8kAWEK· 2026-10-11T09:40Z · holder of claim6106527562, released by this act.- Landed: through the merge queue as
27f0d83fb8(2026-10-11T09:39Z). It has one parent,60bcfe726c, and is an ancestor oforigin/main. - Content check: all 6 PR paths are blob-equal to the reviewed head
c14e269ed0. Review chain: ACCEPT6107421846; at-tier contract review PASS6107414318. - What now holds (
@objectstack/verifyminor):bootStack(config, { artifact })reads the compiledobjectstack.jsonwith the runtime's ownloadArtifactBundleand mounts it as the app (AppPlugin). Every handle door then runs each hook's and action's loweredbodyin the QuickJS sandbox, as a deployment does. Composition is read fromconfig.- Four refusals carry ADR-0112 codes:
- an unloadable artifact gives 404, and the boot never falls back to source;
- another app's artifact gives 409;
onEnablein the config gives 400;- an empty option gives 400.
- Real lowered-only divergences are pinned in dogfood. No package-graph edge was added.
- Routed: finding:
objectstack buildlowers a hook handler that writesctx.dispatch.scope(or readsctx.submitted) at exit 0, although the sandbox marshals neither — every lowered write TypeErrors, and REST answers 500 where the source boot answers 2xx #22810 (domain:cli, p2,pm:queue) coversobjectstack buildlowering a handler that writesctx.dispatch.scope/ readsctx.submitted. Note6107432274there covers moving this PR'slbd_stashdivergence pin when that card's fix lands. - Carried to this card's close-out (from the ACCEPT's acceptance notes; none filed):
- The JSDoc and changeset say
servecomposes the artifact boot "the same way". That holds for a non-host configuration only; the PR body carries the qualifier. - On the serve side (unmeasured),
mergeBootConfigserves the boot result'spluginswhole. A non-host config's non-instancepluginsentries therefore do not reachserve's artifact boot, while the handle mounts them. This is item 1's territory. - The
onEnablegraft stays cli's. There is no measured pull.
- The JSDoc and changeset say
- hotcrm:
runShippedHookcan move ontobootStack(config, { artifact })afterobjectstack build, once a release carries this door. - Mis-close scan: the PR body, its commits and the squash message carry no closing keyword. The merge closed nothing, and verify: the in-process handle boots a leaner stack than
serveand has no door for eight things an app's tests need (requires[] capabilities, system/predicate update, the form door, user-less triggers, …), measured by hotcrm#2013 #22301 stays open. - Still on this card:
- Item 1's remaining divergence:
bootStackdoes not mountserve's MCP and pinyin-search host defaults (process-env decisions). - The serve-side
mergeBootConfigreading above. - Every item from 2 to 9 has now landed or has a stated decision. The card's close-out also carries item 6's C1 and C2 needs (
6106511170).
- Item 1's remaining divergence:
Release:
session_016njDy8ozy9B9Ns5Y8kAWEK· why: a partial landing (Part of #22301) · to:pm:queue, unassigned. This act moves the cardpm:dispatched→pm:queueand removes the assigneemarchtian.
Generated by Claude Code
- Landed: through the merge queue as
objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsClose-out: every item has a handle door or a stated decision. Acceptance is met, and the card closes
completeddomain:specseat 2 (#18549) ·marchtian· sessionsession_016njDy8ozy9B9Ns5Y8kAWEK· 2026-10-11T09:52Z. ⛔ Not a claim: this act closes the card frompm:queue.Acceptance, verbatim: "Each item gets a handle door, or a stated decision that the door is out of scope."
Item by item, every PR merged and read on
origin/main:Item Door / decision Landed 1. bootStackcomposes whatservecomposesthe app's requiresproviders and ownplugins(ruling6070767186A), then the always-on slate and the providers built from the app's configurationPR #22381 → 97610a533; PR #22747 →7098acaef91, remainder stated decision: serve's MCP endpoint (OS_MCP_SERVER_ENABLED) and pinyin search (OS_SEARCH_PINYIN_ENABLED) are decisions about a server PROCESS, not the configuration. They are not composed, and a suite that exercises either passes the provider inextraPlugins. Stated inbootStack's TSDoc (harness.ts"Not composed here") and incapability-composition.ts"The boundary, measured"7098acaef92. system-context update hooks.run(…, { system: true })on updatePR #22517 → 5910b5e3ed3. predicate ( multi: true) updatehooks.updateWherePR #22517 → 5910b5e3ed4. the anonymous form door pinned as served through the handle's dispatcher PR #22543 → 37c71144965. user-less trigger, deleted record hooks.runtakes the system principal on insert and deletePR #22596 → b4ce5e281b6. what a hook handed the engine stack.observeWrites(fn)+settled(match)PR #22781 → a8f24b092c7. the lowered body bootStack(config, { artifact })PR #22808 → 27f0d83fb88. automation.evaluateConditionstack.automation.evaluateConditionPR #22553 → fbb065fd4b9. cel-dated seed replay a cel-dated seed row reaches every hook in its stored form PR #22353 → 3f80f17167hotcrm can delete each matching local path in
test/helpers/verify-stack.ts(extraPlugins,systemUpdate,predicateUpdate,guestInsert,runRecordFlow,recordEngineWrites,runShippedHook,conditionHolds) once it takes a release carrying these doors. The release is the Version Packages PR, a human act. A gap found in that port reopens the matching thread as its own card, with measured reach.Carried, not filed. None has a measured reach. Each becomes a card only if a port or a measurement finds it:
- Item 6, C1: observing a write before the write gates (an
IObjectQLEngineseam plusengine.ts). - Item 6, C2: an engine-owned drain of fire-and-forget hook runs (
hook-wrappers.tsplus an engine member). - Item 7: the artifact door refuses a config carrying
onEnablerather than grafting it.graftAuthoredRuntimeMembersis cli's. - Item 7:
bootStack's JSDoc and changeset sayservecomposes an artifact boot "the same way". That holds for a non-host configuration only; PR feat(verify): bootStack boots the compiled artifact, so a handle test runs each hook's lowered body in the sandbox #22808's body carries the qualifier. This is wording for a later text round. - Serve side, read from source, unmeasured:
mergeBootConfigserves the boot result'spluginswhole. So a non-host config's non-instancepluginsentries do not reachserve's artifact boot, while the handle mounts them.
Routed from this card while open:
- spec: the stack definition has no
email,smsorappNamekey, so theconfig.email/config.smscontractservereads is unreachable from adefineStackconfig #22748 (stack-definitionemail/sms/appName): closed. - objectql: an
afterInserthook withonError: 'abort'(the default) that throws rejects the write, but the row stays stored —HookSchema.onErrorsays abort rolls the transaction back, and a plain write opens none #22782 (afterInsertabort leaves the row;domain:engine). - finding:
objectstack buildlowers a hook handler that writesctx.dispatch.scope(or readsctx.submitted) at exit 0, although the sandbox marshals neither — every lowered write TypeErrors, and REST answers 500 where the source boot answers 2xx #22810 (objectstack buildlowersctx.dispatch.scope;domain:cli).
Generated by Claude Code
- Item 6, C1: observing a write before the write gates (an
Ruled: 6070767186 · letter A (item 1) · 2026-10-08T23:05Z
Filing gate: ① product defects in a published package, reach measured. Class (a). reach: named producer. objectstack-ai/hotcrm's test suite was ported onto
@objectstack/verify17.7.0 (PR objectstack-ai/hotcrm#2013 for objectstack-ai/hotcrm#1595, the hotcrm consequence of objectstack#15951). Each item below was measured there with the handle's own calls.Who acts on it: the objectstack triage seat routes it; the fixes land in
packages/verify(item 1 also touchespackages/cli). Found by the dev of hotcrm#1595 (sessionsession_012zh91QzFgePbkmuHnugLN3); therepo:hotcrmseat located the sites. ⛔ Not a claim.Why it matters: the maintainer's B′ ruling (2026-09-05, on objectstack#15951) put test execution on the platform: an app's tests reach the real engine through this handle and nothing hand-built. hotcrm#1595's rule: "a behaviour the handle cannot express is a platform finding … keep that one local helper path until the fix is pinned … ⛔ never re-grow a local stand-in". hotcrm therefore keeps exactly one local path per item, in
test/helpers/verify-stack.ts. Each path calls the engine's own service on the verify-booted kernel; none re-implements the engine. Every item closed here deletes one of them.The gaps (measured on 17.7.0; sites at the
@objectstack/*@17.7.0commit)bootStackignores the app'srequires[].objectstack servemounts the capability providers an app requires.verify/src/harness.ts:516-730boots a fixed plugin set, offering onlyautomationandextraPlugins. The mapping lives on theServeclass (CAPABILITY_PROVIDERS,cli/src/commands/serve.ts:1867;CapabilitySpecunexported at:959). The handle cannot reuse it, so an app names the plugins by hand. Measured on hotcrm without them: nosys_inbox_message, nosys_approval_request, nosys_activity, and no record-change flow fired on a write. hotcrm names five: triggers, approvals, messaging, audit, email.seedonly inserts (handle.ts:401-405), andhooks.runalways runs as a person.multi: true) update door.hooks.runaddresses one row byinput.id, and RESTupdateManyiterates by id. The engine's predicate path, where 17.7.0 binds each row's pre-image, has no handle door.POST /api/v1/forms/:slug/submit(registered byrest/src/rest-server.ts:10720) answersENDPOINT_NOT_FOUNDthrough the handle's dispatcher. An app's web-to-lead / web-to-case branches can only be reached by reproducing the door's execution context (publicFormGrant,guest_portal, anonymous).seedskips record-change flows. An integration's or system job's write, and a record deleted between the trigger and the flow'sget_record, cannot be driven.async: truehook's completion is invisible (the write that fired it has already returned), and a refusal cannot be staged without a spy on the engine.automation.evaluateConditionis not fronted. A truth table over row shapes that no write produces needs the kernel service.bootStackrefuses cel date values. hotcrm's seed uses the documentedcel`daysFromNow(..)`form (content/docs/data-modeling/seed-data.mdx:387-397). Each verify boot logs ~478 insert WARNs "must be a valid datetime (ISO-8601)" (campaign, case, event, opportunity, lead and account seeds), and the rows are missing.serveresolves these (seed-loader.ts:1138→formula/src/seed-eval.ts:74). The only warn-level insert-failure line is AppPlugin's raw-insert fallback (runtime/src/app-plugin.ts:1527-1534,:1542-1548), which runs when no metadata service is mounted or SeedLoaderService throws. Root cause NOT MEASURED. objectstack#21663 (closed) named these raw-cel paths.Acceptance
Each item gets a handle door, or a stated decision that the door is out of scope. Each then lets hotcrm delete the matching local path in
test/helpers/verify-stack.ts:extraPluginslist,systemUpdate,predicateUpdate,guestInsert,runRecordFlow,recordEngineWrites,runShippedHook,conditionHolds. Item 9: a verify boot of an app with cel-dated seeds stores those rows.Duplicate check
gh searchis refused in this container (GraphQL and REST search answer 403). So all 9,565 objectstack issues were listed and matched case-insensitively:verify handle: 82 (open: verify: classify hotcrm's "platform-semantics pins" — each becomes a derived proof family in@objectstack/verifyor a platform regression test in dogfood, never an app test (epic hotcrm#1579, step 5c) #15953, docs + scaffold:plugin-spec.mdxstops promising the non-existent@objectstack/testingand points at@objectstack/verify; thecreate-objectstackblank template ships a test story (epic hotcrm#1579, step 5b) #15952, [PM seat] domain:cli — 🟢 marchtian · session_01B5CHJNXuuqzChM4w6hkTN4 #6024)CAPABILITY_PROVIDERS: 10 (all closed, serve-side)bootStack requires: 7verify systemUpdate: 0evaluateCondition verify: 0forms submit ENDPOINT_NOT_FOUND: 1 (a QA run)seed cel valid datetime: 8None is a duplicate. The origin is #15951 (closed). Open #15953 (derived proof families) and #15952 (docs + scaffold) are siblings; #21663 (closed) is item 9's nearest record.
Generated by Claude Code