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 — ⏳ vacant #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 — 🟢 os-project-manager #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:
102 remaining items
objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsClaim: PM loop round 2 (item 6 of this card: no observation of what a hook handed the engine. A refused write leaves no row, an
async: truehook's completion is invisible, and a refusal cannot be staged without a spy on the engine) · 2026-10-11T05:18Z
Session:session_016njDy8ozy9B9Ns5Y8kAWEK
Account:marchtian(the seat's linked user asGET /useranswers it; the card's assignee from this act)
Branch:claude/issue-22301-item6-hook-observation
Worktree:objectstack-issue-22301
Domain:domain:spec
Seat:domain:spec#2(seat post #18549)
File surface (atorigin/main996aa86e0a; stop on breach and explain in the report):packages/verify/src/handle.ts: a handle door, or doors, for item 6's three needs: observe the engine writes a hook made (a refused one included), await anasync: truehook's completion, and stage a refusal. Its overload set and JSDoc are in scope.packages/verify/src/harness.ts: the docblock door roster only, plus whatever wiring the door needs at boot.packages/verify/README.md.- Pins in
packages/verify(wherever the item 2–5 pins live), each ablation-verified. - Any consumer that hand-builds the handle's type and fails its typecheck (the item 8 lesson:
packages/qa/dogfood/test/rls-runner.test.ts's fake stack). .changeset/22301-*.md:@objectstack/verifyminor,Clause-②: yes (widening).- ⛔ Zero re-implemented engine 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 observes or reaches the real engine and never stands in for it. - ⛔ Stop-and-report on
packages/objectql/src/engine.tsandpackages/spec/src/contracts/data-engine.tswhile PR feat(spec,objectql,metadata-protocol,client): a write answer carries its advisory validation-rule hits as warnings #22749 (this seat's, enqueued) is open. If item 6 needs an engine seam, it waits for that merge and the seam is named in the report first. - ⛔ Item 7, and item 1's remaining divergence (MCP / pinyin host defaults), are 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, so the contract review atCONTRACT_REVIEW_TIERis owed before enqueue.
Clause-②: yes (widening)
Responsibility:@objectstack/verify's handle has no door that shows what a hook handed the engine. So an app test cannot see a refused hook write, wait for an async hook, or stage a refusal without a spy | no platform path covers it (rowsreads committed rows only) | who reaches it: hotcrm'srecordEngineWriteslocal path (test/helpers/verify-stack.ts), per the card body
Thread-read: 6105282590
Prior rulings read:6070767186(A, item 1; "Items 2–8 … are their own stages and do not wait"); 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);recordEngineWrites,engine spy,hook observation,async hook,verify handle,bootStack,engine middleware→ 4 ADR term hits (ADR-0094 D3, ADR-0106 D6/D7, ADR-0127 D2; none rules a test-handle door).
Serial constraints cleared: - The file lists of the 10 open PRs were read in this act.
- None touches
packages/verify/**. - PR feat(spec,objectql,metadata-protocol,client): a write answer carries its advisory validation-rule hits as warnings #22749 (enqueued) touches
packages/objectql/src/engine.tsandcontracts/data-engine.ts; that is the boundary above. - Item 1's gap (PR feat(core,verify,cli): bootStack mounts the always-on slate and builds each provider from the app's configuration — item 1 gap of #22301 #22747) has landed (
7098acaef9), and 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 6 only (claim 6105769280)",
"branch": "claude/issue-22301-item6-hook-observation",
"pr": "#22781",
"head": "6f6793c636",
"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-11T06:30Z",
"summary": "Item 6 gets a handle door: stack.observeWrites(fn) records every insert, update and delete the booted engine receives while fn runs, each with the engine's answer (pending, then resolved with its result, or refused with the very error the engine threw and that error's own code). It is one global middleware on the engine's own chain (registerMiddleware, a member of the IObjectQLEngine contract), registered on the first call and a pass-through with no observation open. It changes no payload, answer or error, and no engine or spec file is touched. Need 1 (a refused write leaves no row) is served for every refusal past the write gates. Need 2 (an async: true hook's completion) is served for the write the hook makes, through observation.settled(match). Need 3 (staging a refusal) is documentation plus pins: a real refusal arranged through seeded state (DUPLICATE_RECORD) or a declared validation rule (VALIDATION_FAILED). A switch that refuses in the engine's place is out of scope by #15951 B-prime, with the four-axis reasoning in the PR body. Two carried needs are named, not built, because each needs a new engine seam. C1: observing a write before the write gates, so a gate refusal (permission, RLS, tenant wall) of a hook write and the two pre-chain refusals are visible. C2: an engine-owned record or drain of fire-and-forget hook runs, so an async hook that writes nothing is observable. The boot was not changed: an outermost observer would need a plugin added to bootStack, which ruling 6070767186 (A) rules out. The stop boundary on engine.ts and data-engine.ts was moot (PR 22749 landed as 490cb6d, the worktree base) and was not crossed anyway.",
"mechanism_measurements": "Scratch probe (deleted) on a booted stack at 490cb6d, with a global middleware registered after boot, which is exactly the door. (1) A hook's ctx.api write is ObjectRepository.insert, which calls engine.insert(name, row, { context }) and so passes the middleware chain. An afterInsert hook's write refused by a validation rule was recorded refused VALIDATION_FAILED, and no row was found. (2) A hook's write refused by the permission gate was not recorded at all, because plugin-security's write middleware throws before next(). assertWriteAllowed and enforceTransactionOrigin refuse before the chain on insert, update and delete. (3) wrapDeclarativeHook fires async hooks as void runWithErrorPolicy(detached), with no queue, promise or drain kept. The hook metrics recorder is read at bind time, so it cannot be swapped after boot. The async write was absent at return and present 300 ms later. (4) Refusal staging: a seeded unique holder gives DUPLICATE_RECORD (recorded); a validation rule gives VALIDATION_FAILED (recorded); a permission refusal happens but is past the observer; strictReadonlyWrites is a per-call option the writer chooses, which a test cannot set on a hook write. (5) A global registration leaves hasObjectMiddleware false. (6) An afterInsert hook throw (onError abort, the default) rejected the insert while both rows stayed stored, because neither hooks.run nor REST createData opens a transaction. Hence the docs say an outcome is the engine's answer, not row state; see out_of_scope_findings.",
"tests": "All runs went through os-verify-lock (slot issue-22301), and each verdict line read command-exit 0 unless stated. Builds: turbo build verify closure 47/47 at 490cb6d; dogfood closure plus verify 63/63 at 6f6793c. New pins: src/handle.observe-writes.test.ts, 11 passed (11) at 60d6583. Full verify suite: vitest run --maxWorkers=2, Test Files 29 passed (29), Tests 234 passed (234), at 60d6583 and again at 6f6793c. pnpm --filter @objectstack/verify typecheck (tsc --noEmit plus check:test-typecheck: OK, 0 files/0 errors) at 6f6793c; tsc -p tsconfig.test.json --listFiles reaches 29/29 test files, the new one included. pnpm --filter @objectstack/dogfood typecheck exit 0 at 6f6793c, after verify was rebuilt as a cache miss (dist/index.d.ts names observeWrites 3 times). Reverse check: deleting the fake stack's observeWrites line gave TS2741 Property 'observeWrites' is missing ... required in type 'VerifyStack' (exit 2), restored through ablation-replace (blob == HEAD 8628a117f19d, git diff HEAD empty). Ablations on committed 60d6583 via node scripts/ablation-replace.mjs in wrap mode (anchor 1 to 0, blob changed; restored to HEAD blob 62a14c3b71ef with git diff HEAD empty; predictions recorded before the first run; the subject is reached by a relative src import, so there is no dist leg). A1 refusal not recorded: predicted red T1,T2,T4,T5; observed 4 failed / 7 passed, exactly those. A2 settled accepts pending: predicted T3,T4; observed 2 failed / 9 passed. A3 observation never closed: predicted T8; observed 1 failed / 10 passed. A4 rethrow a copy of the engine's error: predicted T2; observed 1 failed / 10 passed. Lint: a targeted eslint --no-inline-config --format json over the 5 touched .ts files linted 5 files with 0 errors and 0 warnings. eslint.config.mjs never enables type-aware linting, so verdicts on untouched files cannot move. The repo-wide pnpm lint is CI-owned and not claimed. packages/cli is not touched, so no integration tier is owed.",
"gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) derived 68 commands at 6f6793c (7 paths vs merge base e84aeb3), and all 68 ran there. 67 exit 0. pnpm check:dual-build-cjs-loads exit 3 (PREREQUISITE NOT MET: it reads a whole-workspace build) is NOT MEASURED, reason: a whole-workspace build is outside this dispatch; left to CI. --ran reconciliation: 68 derived, 67 run, 1 NOT-MEASURED (derived from the recorded exit 3), 0 UNRUN. check:api-surface and check:export-origins were not derived (they are spec-package gates; no spec path is touched). CI on PR 22781: in_progress at report time (not awaited, per the contract).",
"line_budget": "n/a: no skills/** or governed path. PR size +798/-9 over 7 files, under the 3000-line class.",
"files_changed": [
".changeset/22301-verify-observe-writes-door.md",
"packages/qa/dogfood/test/rls-runner.test.ts",
"packages/verify/README.md",
"packages/verify/src/handle.observe-writes.test.ts",
"packages/verify/src/handle.ts",
"packages/verify/src/harness.ts",
"packages/verify/src/index.ts"
],
"carried_needs": [
"C1, observing a write before the write gates: a seam ahead of the middleware chain (a write-observer member at the engine's operation door, or a position option on registerMiddleware), so a gate refusal (permission, RLS, tenant wall) of a hook's write, and the external-datasource and cross-driver pre-chain refusals, are observable. Home: IObjectQLEngine (packages/spec/src/contracts/objectql-engine.ts) plus packages/objectql/src/engine.ts.",
"C2, an engine-owned record of fire-and-forget hook runs: wrapDeclarativeHook registers each detached run with the engine, which exposes an awaitable drain, so an async: true hook's completion (and its own failure, today an error log line only) is observable when it writes nothing. Home: packages/objectql/src/hook-wrappers.ts plus an engine member."
],
"deviations": [
"Commit 60d6583 ends with AGENTS.md's model-free trailer pair (Claude-Session plus Co-authored-by: Claude), not the harness reminder's model-named Co-Authored-By line: AGENTS.md takes precedence and pre-push refuses a model identifier there.",
"Merge commit 6f6793c carries git's default merge message with no Claude-Session trailer; the hooks accepted it. Not rewritten (pushed history).",
"The PR body ends with AGENTS.md's session-URL footer, not the harness reminder's '🤖 Generated with' form.",
"Zero label writes: the dispatch budget names git push, one pr_create and one comment only, and no skip-changeset applies (a changeset ships)."
],
"mcp_calls": "0 (no MCP GitHub tool used)",
"api_writes": "2 relay strokes from this session: POST /repos/objectstack-ai/objectstack/dispatches x2, each executed by fleet-write.yml as objectstack-fleet[bot]. Stroke 1 (run 38118042240): POST /repos/objectstack-ai/objectstack/pulls (PR 22781, draft forced) plus POST /repos//issues/22781/assignees (marchtian); read-back 14180 bytes sent and 14180 stored, identical. Stroke 2: POST /repos//issues/22301/comments (this report). git push x3 (empty branch probe, 60d6583, 6f6793c) is not REST.",
"cleanup": "Scratch probe deleted before the first commit (never committed). The worktree is removed after this comment lands (node_modules first, no --force). No process of mine left running: the gate battery PID 2086 exited.",
"hotcrm": "Not read (out of this session's scope). The card body is the only statement of recordEngineWrites used.",
"open_questions": [],
"out_of_scope_findings": [
"class: b · reach: hooks.run (the published @objectstack/verify in-process door) measured on a booted stack at 490cb6d: an afterInsert hook (onError omitted, so 'abort') that throws makes hooks.run(object,'insert',...) reject with the hook's error, while the triggering row AND an earlier afterInsert hook's ctx.api row both stay stored (rows() read them back). REST createData (metadata-protocol protocol.ts, engine.insert with no transaction) is the same door shape, by code reading, not measured over HTTP. · contract: HookSchema onError JSDoc, packages/spec/src/data/hook.zod.ts, 'abort: Rollback transaction (if blocking)' · Seam: spec:HookSchema.onError → runtime:packages/objectql/src/engine.ts insert, afterInsert dispatch after the driver write (no transaction opened by the write door) · dedupe words: onError abort rollback; afterInsert throw row persists; hook abort transaction; after hook rollback; abort leaves row"
]
}objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actions✅ ACCEPT — PR #22781 at
6f6793c636(item 6,Part of #22301). Contract review PASS; ready and enqueueddomain:specseat 2 (#18549) ·marchtian· sessionsession_016njDy8ozy9B9Ns5Y8kAWEK· 2026-10-11T06:45Z · holder of claim6105769280. Report:6106246523.Checked against the diff and the record, not the report:
- The door.
stack.observeWrites(fn)resolves with every insert, update and delete the booted engine received whilefnran. Each entry carries the engine's answer:pending, thenresolved, orrefusedwith the SAME error object and its owncode.observation.settled(match)waits for a write that lands afterfnreturns, which is theasync: truehook case, under a finite timeout. - 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′ holds. The door is one global middleware, added through the engine's ownregisterMiddleware(anIObjectQLEnginemember) and appended innermost.- It only records. It never writes a payload, answer or error, and it never short-circuits.
- It rethrows the identical error, pinned with
toBe. - When no observation is open it passes straight through.
- No engine or spec file is touched.
- What it cannot see is stated: a write-gate refusal (permission, RLS, tenant wall) of a hook's write, and the two pre-chain refusals. The JSDoc names all three; the README and the changeset name the gate class. "An outcome is the engine's answer, not row state" is stated in all three places.
- Need 3, staging a refusal, is documentation plus pins. The pins arrange REAL refusals: a seeded unique holder (
DUPLICATE_RECORD) and a declared rule (VALIDATION_FAILED). A switch that refuses in the engine's place is out by B′. - Concurrency: each observation sees exactly its own window. A throwing
fncloses the observation and its error is rethrown unchanged.settledcannot hang. - Ablations: four, predicted before the runs and observed exactly (A1–A4). The dogfood fake stack gains the member, and a reverse check proves the typecheck catches its absence.
- Semver:
@objectstack/verifyminor,Clause-②: yes (widening). No other published package moved. - Contract review: at-tier PASS
6106360215on6f6793c636102428bc6b09bb1a05a412c6334926. - CI on
6f6793c636: 35 runs: 32 success, 3 skipped (Build Docs,Console Pin Gate,Packed-tarball smoke (opt-in)), 0 failure. All seven required contexts are green. - Paths: 7 files, +798 / −9. Not governed (
check-governed-merges --pr 22781). First linePart of #22301; no closing keyword, so the card stays open.
Item 6's two carried needs. Each needs a new engine seam, so neither is built here. They are recorded on this card so they outlive the PR body. ⛔ Neither is filed: there is no measured reach yet. The hotcrm seat measures a pull when hotcrm ports
recordEngineWritesonto this door; a card follows only if that port finds the gap.- C1: observing a write before the write gates, so that a permission / RLS / tenant-wall refusal of a hook's write, and the two pre-chain refusals, are observable. Home:
IObjectQLEngine(contracts/objectql-engine.ts) pluspackages/objectql/src/engine.ts; a position option onregisterMiddleware, or an operation-door observer. - C2: an engine-owned record, with an awaitable drain, of fire-and-forget hook runs, so that an
async: truehook that writes nothing (or fails, which today is only a log line) is observable. Home:packages/objectql/src/hook-wrappers.tsplus an engine member.
Routed from this stage: #22782 (filed by this seat,
bug,pm:queue). AnafterInserthook with the defaultonError: 'abort'that throws refuses the caller, but the row stays stored, because a plain write opens no transaction.HookSchema.onErrorsays abort rolls back. The PR'soutcomedocs stay correct under either fork.Acceptance notes (carrier: none; wording for a later text round, none blocking):
- the README and the changeset do not name the two pre-chain refusals;
whereis the engine-normalised selection;- the README's refusal bullet does not yet point at
observeWrites/settled.
Still on this card: item 7 (a lowered-body door), and item 1's remaining divergence (
serve's MCP and pinyin-search host defaults).Landing: the PR goes ready and auto-merge is enabled now. The landing record follows the merge.
Generated by Claude Code
- The door.
objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsLanded: PR #22781 →
a8f24b092c(item 6,Part of #22301). The card goes back topm:queuefor item 7 and item 1's remaining divergencedomain:specseat 2 (#18549) ·marchtian· sessionsession_016njDy8ozy9B9Ns5Y8kAWEK· 2026-10-11T07:05Z · holder of claim6105769280, released by this act.- Landed: through the merge queue as
a8f24b092c(2026-10-11T07:05Z). It has one parent,a2e94c2a05, and is an ancestor oforigin/main. - Content check: all 7 PR paths are blob-equal to the reviewed head
6f6793c636. Review chain: ACCEPT6106366315; at-tier contract review PASS6106360215. - What now holds (
@objectstack/verifyminor):stack.observeWrites(fn)resolves with every insert, update and delete the booted engine received whilefnran. Each entry carries the engine's answer:resolved, orrefusedwith the engine's own error andcode.observation.settled(match)waits, under a finite timeout, for a write that lands afterfnreturns (anasync: truehook's write).- The door is one pass-through middleware on the engine's own chain. It is 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′ compliant: it records only, and never answers in the engine's place. - What it cannot see is documented: a write-gate refusal of a hook's write, and the two pre-chain refusals.
- Staging a real refusal (a seeded unique holder, a declared rule) is documented and pinned.
- Item 6's carried needs stay on this card as recorded in the ACCEPT. ⛔ Neither is filed: there is no measured reach. A card follows only if hotcrm's port of
recordEngineWritesonto this door finds the gap.- C1: observing a write before the write gates. Home: an
IObjectQLEngineseam plusengine.ts. - C2: an engine-owned drain of fire-and-forget hook runs. Home:
hook-wrappers.tsplus an engine member.
- C1: observing a write before the write gates. Home: an
- Routed: 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 (bug,pm:queue, awaiting triage). AnafterInsertabort refuses the caller but leaves the row stored. - hotcrm:
test/helpers/verify-stack.ts'srecordEngineWritescan move ontoobserveWritesonce hotcrm takes a release carrying it. The release is the Version Packages PR, a human act. - 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 7 (a lowered-body door), its own stage;
- item 1's remaining divergence:
bootStackdoes not mountserve's MCP and pinyin-search host defaults, which are process-env decisions.
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 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
-
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 — ⏳ vacant #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