Skip to content

fix(driver-memory)!: retire the legacy default export that CJS require() could not reach - #27323

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-26206-driver-memory-default
Oct 28, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-26206-driver-memory-default

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #26206

Clause-②: no (narrowing)
Metadata-protocol: none
Skills-docs: none — no skills/** file and no content/docs/** page names the retired default export or loads the package as a string plugin; the docs teach InMemoryDriver by name, which stays

维护者速读

  • 改了什么:@objectstack/driver-memory 的包入口不再提供 default 导出(一个旧式的 { id, version, onEnable } 对象)。InMemoryDriver 等所有具名导出不变。这是本卡最后一项,前两项已由 fix(metadata-core,service-cluster,create-objectstack): ESM-only subpaths declare their types under import #26321、docs(cli): say the three code subpaths are ESM #26352 落地。
  • 为什么改:这个默认导出不是内核插件(内核约定是 init/start/destroy),而且 CommonJS 用户 require() 时拿不到它,类型声明却说有(attw 报 FalseExportDefault)。退役后 attw 对该包为 0 问题。
  • 风险与回滚:本仓内默认导入者为 0,示例、脚手架模板、文档、技能里也没有人把它当字符串插件写进 plugins。外部若在配置里写了 plugins: ['@objectstack/driver-memory'],原来会注册一个内存驱动,现在会静默不注册(changeset 写明改用 new DriverPlugin(new InMemoryDriver(), 'memory'))。cloud / hotcrm 未能测到。回滚 = revert 本 PR。
  • 先看:packages/drivers/driver-memory/src/index.ts(删掉的默认导出);.changeset/26206-driver-memory-default.md(BREAKING 说明、ADR-0087 处置与 FROM → TO);packages/drivers/driver-memory/src/default-export-retired.pin.test.ts。
  • 两分钟验证:node scripts/check-adr-0087-registration.mjs --base origin/main,预期 exit 0 且打印 verified: @objectstack/driver-memory#default。
  • 需要你做的:无,席位落地(收窄已发布导出面,先过一次契约档复核)。

What changes

  • packages/drivers/driver-memory/src/index.ts: the export default { id, version, onEnable } object leaves the published entry. Every named export stays. A comment at the old site says why the entry has no default and which named door to use.
  • Choice, as the card asks: drop the default in favour of the named exports, not "make the CJS default reachable". Reasons:
    1. A reachable CJS default means require('@objectstack/driver-memory') itself returns the default object. The entry also has 22 named exports, so that needs an export = shape with the named exports merged onto the object. That changes what every CommonJS named import reads today, and it keeps an object that is not a kernel Plugin.
    2. Nothing reads it. In this tree: 0 default imports, 0 require() calls, 0 .default reads after a dynamic import, and 0 string plugins entries naming the package (evidence surface below). Control: 14 files (code, docs and skills) import named exports with a static from, and 3 code files destructure InMemoryDriver from a dynamic import().
  • packages/drivers/driver-memory/README.md: the default row leaves the Key Exports table, and one line says the package has no default export.
  • .changeset/26206-driver-memory-default.md: patch (Metadata-protocol none), BREAKING banner, ADR-0087 not-required (runtime-export-retired @objectstack/driver-memory#default), and one FROM → TO line. No staged window.
  • packages/drivers/driver-memory/src/default-export-retired.pin.test.ts (2 cases): the entry has no default, and the named exports stay and InMemoryDriver still constructs.

What changes for a string plugins entry (PM assumption 3)

packages/cli/src/commands/serve.ts:2765 (mod.default || mod) loads the project's own config file, not a plugin. The string-plugin door is materializeStackPlugin in packages/core/src/stack-plugins.ts:65: it loads the module and takes mod.default || mod, and an object with no init is wrapped in AppPlugin, which runs a bundle's onEnable with a drivers.register that calls ql.registerDriver. Measured at that seam (real core dist, real driver-memory dist, a recording wrapBundle):

  • before: wrapBundle received id, version, onEnable, and calling onEnable with a driver registry registered InMemoryDriver(com.objectstack.driver.memory).
  • after: wrapBundle receives the module namespace (InMemoryDriver, …, no id, no onEnable). AppPlugin then takes its no-id, no-payload branch (packages/runtime/src/app-plugin.ts) and becomes an empty no-op plugin, so no driver is registered and nothing says so.

So a config that writes plugins: ['@objectstack/driver-memory'] changes behaviour. No producer of that spelling was found (search below). The changeset names the replacement for that case.

Tests

attw 0.18.5 on an npm-pack staging of driver-memory: before and after

Before (base 65b492081, attw exit 1):

node10: ❗️ Incorrect default export
node16 (from CJS): ❗️ Incorrect default export
node16 (from ESM): 🟢 (ESM)
bundler: 🟢

After (161653aed, attw exit 0):

 No problems found 🌟
node10: 🟢
node16 (from CJS): 🟢 (CJS)
node16 (from ESM): 🟢 (ESM)
bundler: 🟢

The packed dist/index.d.ts has 0 export default lines after the change.

driver-memory build, typecheck and tests at 161653a

pnpm --filter @objectstack/driver-memory build && … typecheck && … test under the verify lock: VERDICT command-exit 0; Test Files 73 passed (73), Tests 1760 passed (1760). tsc --noEmit --listFiles counts the new pin test once (it is compiled by the typecheck program).

Ablation (one-off, no permanent file)

The fix was committed first. node scripts/ablation-replace.mjs replaced the comment line at the old site with export default { id: 'ablated' }; (anchor 1 → 0, blob a1f49a03fbd9 → f5bf6cfac4ce) and ran the pin test: × the published entry has no default export, AssertionError: expected true to be false, Tests 1 failed | 1 passed (2). Restored: blob == HEAD (a1f49a03fbd9), git diff HEAD empty. The test imports ./index.js, so it reads src, and no build was needed for this leg.

A first attempt used a replacement that contained its own anchor. The tool refused it before running anything ("the anchor count moved 1 -> 1"), so that attempt measured nothing.

Full dogfood battery at 161653a (the title carries a bang)

After pnpm turbo run build --filter='./packages/**' --concurrency=2 (75/75 tasks, 0 cached, VERDICT command-exit 0), pnpm --filter @objectstack/dogfood test under the verify lock, no shard, no path: VERDICT command-exit 0, Test Files 348 passed | 3 skipped (351), Tests 2806 passed | 20 skipped (2826). No failures.

Acceptance notes

  • Evidence surface for readers of the default (tree objectstack-ai/objectstack at 65b492081): git grep over the whole tree (examples/**, packages/create-objectstack/** templates, content/docs/**, skills/**, ui/**, every package's tests and scripts/**) for import X from / import X, { … } from / import * as X from of the package (0), require('@objectstack/driver-memory') (0), a dynamic import() whose result is read as .default or destructured as default (0; the 3 code hits all destructure InMemoryDriver, the other 2 hits are comments), a plugins: line naming the package (0), and the default's id com.objectstack.driver.memory (hits are the driver class's own name, tests and CLI banner text, none of which reads the default object). content/docs/plugins/index.mdx and content/docs/plugins/packages.mdx teach InMemoryDriver by name.
  • NOT MEASURED: objectstack-ai/cloud and objectstack-ai/hotcrm (this session's GitHub reads are bound to this repository; org code search answered 403), and npm dependants.
  • The four sibling drivers (driver-sql, driver-mongodb, driver-sqlite-wasm, driver-turso) export the same legacy { id, version, onEnable } default. attw 0.18.5 on npm-pack stagings of all four at 161653aed reports no problems (exit 0). So they are not part of this card's acceptance, and this PR does not touch them. Why attw flags driver-memory's build and not theirs (the CJS output has the same shape) was not established.
  • The silent no-op above is not specific to this package: a string plugins entry naming any module without a default export and without init gets the module namespace wrapped into an empty AppPlugin. That was read at the seam, not measured through os serve, so it is recorded here and not filed.
  • ADR-0087 gate (PM assumption 1): check-adr-0087-registration --base origin/main exit 0, verified: @objectstack/driver-memory#default (export, packages/drivers/driver-memory/src/index.ts). The blocker fix cdb949a288 is an ancestor of the base (merge-base --is-ancestor exit 0).

Verification

Local, at 161653aed, derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack over this diff (58 commands). Every command ran, and each exit code was captured before any pipe.

  • 55 exited 0 on the first run.
  • 3 exited 3, PREREQUISITE NOT MET, so they measured nothing: check-plugin-teardown-shape --self-test (its pinned fixture commit was outside the shallow clone) and check:dual-build-cjs-loads / check:lean-entry-closure (they read built output). They were re-run after git fetch --depth=1 of the fixture commit and the full packages build, and all 3 exited 0: 112 published require entry point(s) across 69 package(s) load, Admitted set held exactly (15 packages), 48 cases pass.
  • dispatch-gates --ran: 58 derived, 58 run, 0 NOT-MEASURED, 0 UNRUN (exit 0).
  • Outside the derived list: check-platform-checklist --changed-from origin/main exit 0 (0 anchored files), check:published-readme-exports exit 0, check:published-readme-links exit 0, check-nul-bytes exit 0.
  • test:repo and repo-wide lint were not run locally. They belong to CI (Test Core, Lint & Repo Gates) on this PR.

Generated by Claude Code

…e() could not reach

The package entry's default was a legacy { id, version, onEnable } object,
not a kernel plugin, and the CJS build put it on require(...).default while
the declarations said export default (attw FalseExportDefault). It leaves the
entry; every named export stays. ADR-0087 runtime-export-retired.

Claude-Session: https://claude.ai/code/session_017xRBcVyH9FzSkzAGmz8LNE
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tests tooling labels Oct 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️ 1 changed file(s) yielded no anchor (packages/drivers/driver-memory/README.md), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/drivers/driver-memory/README.md) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 11 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 19f49ef35418b5f5b11b5fd04337268335121fbd → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 8d0d2e969c2da6e7a1fd31201eff724164d8639d — the merge of head 161653aed2ee5d28e2db9f09d5ebbcc64a7ffcd5 into base 19f49ef35418b5f5b11b5fd04337268335121fbd, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 8d0d2e969c2da6e7a1fd31201eff724164d8639d && git checkout 8d0d2e969c2da6e7a1fd31201eff724164d8639d
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 19f49ef35418b5f5b11b5fd04337268335121fbd 161653aed2ee5d28e2db9f09d5ebbcc64a7ffcd5 && git checkout -B drift-repro 19f49ef35418b5f5b11b5fd04337268335121fbd && git merge --no-ff 161653aed2ee5d28e2db9f09d5ebbcc64a7ffcd5

node scripts/docs-audit/affected-docs.mjs --json 19f49ef35418b5f5b11b5fd04337268335121fbd

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:devx#1 · sunhaolin · session_017xRBcVyH9FzSkzAGmz8LNE

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 161653aed2ee5d28e2db9f09d5ebbcc64a7ffcd5
Local-runs: none
Scope: packages/drivers/driver-memory/src/index.ts, packages/drivers/driver-memory/src/default-export-retired.pin.test.ts + packages/drivers/driver-memory/package.json, packages/core/src/stack-plugins.ts, packages/runtime/src/app-plugin.ts, packages/runtime/src/driver-plugin.ts, packages/core/src/types.ts, packages/core/src/plugin-registration.ts, packages/cli/src/commands/serve.ts, packages/verify/src/harness.ts, scripts/check-adr-0087-registration.mjs, scripts/check-changeset-no-major.mjs, scripts/pm/clause2-line.mjs

Face: narrowing (Clause-②: no (narrowing) on the PR body and on .changeset/26206-driver-memory-default.md). Inputs: card #26206 body and its 13 comments, PR #27323 body, file list (4 paths, +58/-18) and net diff against main, the head's 45 check-runs, and the cited code read on origin/main at 19f49ef354. The changeset and README were read to judge ② and are not reviewed under ④.

Check-runs on the head at review time: 18 success, 14 skipped, 13 in progress, 0 failure. Green and relevant: Check Changeset (this job runs check-adr-0087-registration --base MERGE_BASE and check-changeset-no-major), PR-body guards, Governed Surface Queue Guard, Lint · ESLint, Lint · docs & code guards, Lint · repo & tooling gates, Check PR Size. Still in progress: Test Core (1-8/8), Build Core, Type Check · workspace / source gates / consumer gates, Lint · CI, release & engine gates. Their conclusions are the gate verdicts; this record does not pre-empt them, and the seat lands only on all-green.

① Derived judgments

The published surface is the root entry alone: packages/drivers/driver-memory/package.json exports has one key, ., with import → dist/index.mjs / dist/index.d.mts and require → dist/index.js / dist/index.d.ts, both generated by tsup from src/index.ts. So every change to src/index.ts's export statements is a change to what is published, on both conditions.

  1. Removed from the published entry: the default export (the legacy { id, version, onEnable } object). Reaches: the ESM import x from '@objectstack/driver-memory' binding, the TypeScript-under-CJS esModuleInterop default binding, require('@objectstack/driver-memory').default, and import('@objectstack/driver-memory') then .default. All four lose a value they had on main. Right to name this a narrowing of the published surface and BREAKING; the PR does so.
  2. Every named export stays (22 value and type exports: InMemoryDriver, InMemoryStrategy, FileSystemPersistenceAdapter, LocalStoragePersistenceAdapter, MemoryAnalyticsService, the tenancy-guard and unique-constraint helpers and their types). I diffed the export statements on main against the hunk: no named export moves. Right.
  3. Accept-set change at the string-plugin door. materializeStackPlugin (packages/core/src/stack-plugins.ts:63-72) takes mod?.default || mod and, for an object without init, calls wrapBundle, which serve.ts:4708 and verify/src/harness.ts:605 both implement as new AppPlugin(bundle). On main the default object has id, so AppPlugin's constructor names it plugin.app.com.objectstack.driver.memory and its start() (app-plugin.ts:1628-1667) runs onEnable with a drivers.register that calls ql.registerDriver. After this PR the module namespace carries no manifest, id, name or any APP_CATEGORY_KEYS payload, so the constructor takes the empty-env branch (app-plugin.ts:454-491): name = 'plugin.app.empty-empty', requiresServices = [], and init() logs at debug, which the CLI's warn floor discards. So plugins: ['@objectstack/driver-memory'] moves from "registers an InMemoryDriver" to "registers nothing, says nothing". The PR body and changeset name this. Right, and it is the one behavioural narrowing beyond the import binding; judged under ③.
  4. Not a metadata-protocol change. No *.zod.ts, no object definition, no stored shape, no authorable key moves; Metadata-protocol: none. Right.
  5. The type surface the CJS condition publishes (dist/index.d.ts) loses its export default line, which is what FalseExportDefault measured: the .d.ts said export default X while the CJS build put X on .default. With no default in source, tsup emits neither, so the attw finding disappears by construction, not by configuration. Right, and it is the card's acceptance.
  6. The dev's PR body says the four sibling drivers (driver-sql, driver-mongodb, driver-sqlite-wasm, driver-turso) keep the identical legacy default; confirmed on main (src/index.ts of each has export default {). They are out of this card's acceptance and the PR does not touch them. Right not to widen.

② Semver level

patch with a BREAKING banner is this repo's rule for this diff, and the declaration lines match it.

  • AGENTS.md Post-Task Checklist step 3: "The level follows the kind alone: a kind takes at least minor on a package the diff moves, none takes patch; a non-protocol change, widening or breaking, ships patch and keeps its BREAKING banner and ADR-0087 disposition." The workflow prose the author is pointed to (pr-automation.yml:775-779) spells the same: Metadata-protocol: none covers "drivers, services … feat: and fix!: alike → patch".
  • scripts/check-changeset-no-major.mjs, the level axis: a declared none must grade every entry it introduces patch. The changeset grades '@objectstack/driver-memory': patch. The launch-window guard forbids major; none is declared. Check Changeset concluded success on this head, and that job also runs check-adr-0087-registration --base MERGE_BASE.
  • Clause-②: no (narrowing) is the right value per scripts/pm/clause2-line.mjs:111-118: "no (narrowing) — NOT a widening, but breaking. This is the whole point of the arm." The diff widens nothing and removes a published runtime export, so the arm is owed, and the claim's bare no (comment 6473747892) was the under-declared form; the dev's amendment is correct and the dogfood battery the arm triggers was run on this head per the report.
  • ADR-0087 marker: exactly one, not-required (runtime-export-retired @objectstack/driver-memory#default), the category built for a retired published runtime export (check-adr-0087-registration.mjs:4397-4470). Operand default is an identifier under EXPORT_SPELLING_RE, predicate 2 now reads a default operand through the import arm only (defaultImportReference, check-adr-0087-registration: runtime-export-retired can never certify an operand named default (predicate 2 word-scans default across every metadata surface) #26324, the blocker this card waited on), and predicate 3 wants the FROM block to name exactly the operand: the body carries one line, - FROM default TO …, which names it. The gate's own verdict is the Check Changeset success above.
  • BREAKING banner present (**BREAKING** (a runtime export is removed)), FROM → TO line present, no staged window claimed, which is right for a startup-stage removal (the import either resolves or it does not; there is nothing to stage).

No disagreement with the declared level or lines.

③ Boundary flags

The open question, verbatim from the report: "After this lands, a stack config that writes plugins: ['@objectstack/driver-memory'] silently registers nothing: materializeStackPlugin (packages/core/src/stack-plugins.ts:65) wraps the default-less module namespace into an AppPlugin that takes its no-id, no-payload branch. … Should the string-entry door refuse that loudly?"

Answer: yes, the door should refuse loudly, and that refusal is a core change outside this card. Option A, with one tightening: the follow-up card is filed now, on the dev's seam reading, not conditionally after a later os serve measurement. Reasons, read against the tree and the repo's principles:

  1. What this PR makes worse, that a user can reach today. On main, plugins: ['@objectstack/driver-memory'] registers a memory driver at start(); after this PR the same line boots an empty AppPlugin named plugin.app.empty-empty and registers no driver. That is a real regression of a reachable spelling. Its reach is bounded: the README on main already labelled the default "legacy … not a kernel Plugin; register through DriverPlugin", no example, scaffold template, doc page or skill writes the string form (I re-ran the search on origin/main: 0 string plugins entries, 0 default bindings, the 3 dynamic imports destructure InMemoryDriver), and cloud / hotcrm are not measured. The changeset names the replacement in the plugins array, and an instance entry passes materializeStackPlugin untouched (DriverPlugin has init), so the prescription is correct.
  2. It is not silent everywhere, and where it was loudest it never worked. serve.ts:5493-5545's no-storage boot guard judges "by the end of plugin init". The legacy onEnable ran in start(), after that judgement, so on a production boot with no OS_DATABASE_URL under the single posture the string spelling was already refused before this PR, and on other production boots the guard already printed its one line and let the boot go on. What changes is the step after: before, a driver then arrived in start(); after, the first query fails on a missing driver. Loud, but late, and not at the door that was declared.
  3. The defect is in core and is not specific to this package. mod?.default || mod is precisely the lenient || fallback Prime Directive Add comprehensive test suite for Zod schema validation #12 forbids in a consumer, and AppPlugin's empty-env branch exists for a different caller (an empty environment artifact) and is reached here by accident. Any string entry naming a module with no default and no init takes the same path today. Prime Directive chore: version packages #10 says file an issue for a contract violation and never bury it; the dev's seam probe is already the measurement the card needs (a wrapBundle that received a namespace with no id and no onEnable), so "a seat measures through os serve and, if it holds, files" defers a card whose evidence exists. File it against materializeStackPlugin: a string entry whose module has no default and no init is refused by name, with the one-line fix. That is the "declared capability must not silently do nothing" principle applied at the right seam, and it closes the hole for every default-less package at once.
  4. Why not B. The claim's file surface (6473747892) is packages/drivers/driver-memory/** and one changeset, "no other package"; a core change on this PR breaches it. The refusal rule is also a contract decision on the string-plugin door (stack-plugins.ts header: "its default export (or, failing that, the module itself) is the plugin"), which is the seat's or the maintainer's to make, not something to settle inside a driver card. Holding a 0-known-producer narrowing on that decision buys nothing the follow-up card does not.

Escalation flag for the seat, not a hold: whether the string spelling plugins: ['@objectstack/driver-memory'] was ever a supported door, or only an accident of the legacy default, decides how the follow-up card is worded (refuse, or refuse-and-name-DriverPlugin). Either way this PR's changeset already tells that reader what to write.

No other boundary crossed: no package added or removed, no exports key changed, no .github/**, no ui/, no governed path.

④ Design judgment

packages/drivers/driver-memory/src/index.ts

  • The hunk removes lines 64-80 on main (the export default { id, version, onEnable } block) and replaces them with a seven-line comment. Against the card's acceptance ("0 FalseExportDefault"): the finding is "the .d.ts declares export default but the CJS build does not return it from require()". With no export default in the only source of both conditions' output, tsup emits no default in dist/index.js and no export default in dist/index.d.ts / dist/index.d.mts, so the finding cannot recur from this tree. Holds by construction; the PR body's attw run is consistent with it but is not what I judged.
  • Against the code it cites: the comment says the object "was not a kernel Plugin (the contract is init/start/destroy)"; packages/core/src/types.ts:155 Plugin requires name and lifecycle methods, and the object had id / onEnable. Correct. It names new DriverPlugin(new InMemoryDriver(), 'memory') from @objectstack/runtime; driver-plugin.ts:34-37 takes exactly (driver, driverName?). Correct.
  • InMemoryDriver was imported at line 2 for both the named re-export and the default's onEnable; the re-export keeps it live, so no unused import is left (ESLint is green on the head).
  • Tried to break it: the empty-AppPlugin path above is the only behavioural reach, judged under ③. No other reader of default in this package's tests or in the repo (0 default bindings; the two mod.default ?? mod hits in driver-memory tests are sql.js, not this entry).

packages/drivers/driver-memory/src/default-export-retired.pin.test.ts

  • Case 1 pins 'default' in entry === false over import * as entry from './index.js'. Under vitest's ESM namespace that is a true pin of the source entry; the dev's ablation (an export default { id: 'ablated' } at the old site made it fail) is the right shape and the report records it, but the tree alone shows the pin is live: the namespace has no default key unless the source exports one.
  • Case 2 pins five named classes as functions and constructs InMemoryDriver, asserting driver.name === 'com.objectstack.driver.memory'. That is the replacement door the changeset names, pinned to still work. Good.
  • Should-fix (not blocking): the pin reads src, so it does not itself prove the tarball shape the card's acceptance names. The structural argument above covers it, and no CI gate runs attw, but a one-line note in the test header saying the published-build claim rests on tsup generating both conditions from this entry would stop a future reader treating this test as the attw proof. Cosmetic; no code change required.
  • Adding a file to src/ is compiled by tsc --noEmit; the report says the typecheck program counts it once. Type Check · workspace is still in progress on the head; its conclusion governs.

Prose faces, read for ② only: the README drops the default row from Key Exports and adds "The package has no default export." check-published-readme-exports reads import lines and documented symbols against the built types; removing a documented symbol that no longer exists is the safe direction. The changeset carries both declaration lines, one marker, the banner and one FROM → TO line.

Findings: 0 must-fix, 1 should-fix (test-header note), plus the ③ follow-up card against materializeStackPlugin, which belongs to the seat, not to this diff.

Implemented-by: claude/issue-26206-driver-memory-default
Reviewed-by: session_017xRBcVyH9FzSkzAGmz8LNE

VERDICT: PASS
isolated subagent at CONTRACT_REVIEW_TIER

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:devx#1 · sunhaolin · session_017xRBcVyH9FzSkzAGmz8LNE

席位核对 · ACCEPT · head 161653aed2ee5d28e2db9f09d5ebbcc64a7ffcd5 · 2026-10-28T17:09Z

Verdict: ACCEPT. Contract review PASS at CONTRACT_REVIEW_TIER on this head (6475505430: 0 must-fix, 1 should-fix). The seat checked against GitHub and origin/main, not against the report.

  1. Shape. Draft, base main, first line Fixes #26206. That is the card's last item; fix(metadata-core,service-cluster,create-objectstack): ESM-only subpaths declare their types under import #26321 and docs(cli): say the three code subpaths are ESM #26352 landed the rest. A full-body scan finds no other closing keyword. Files: 4 (+58/−18), all inside claim 6473747892's surface (packages/drivers/driver-memory/** + one root changeset). No governed path. Under 3,000 lines, so not Tier H.
  2. Premises measured. Assumptions 1 and 2 hold. Assumption 3 is corrected by the dev, and the seat accepts the correction publicly: serve.ts:2765 loads the project config, and the string-plugin door is packages/core/src/stack-plugins.ts materializeStackPlugin (mod?.default || mod, then wrapBundle when there is no init). The seat re-read that function, packages/runtime/src/driver-plugin.ts:35 constructor(driver, driverName?) (exported at packages/runtime/src/index.ts:65), and the kernel contract packages/core/src/types.ts:202–215 (init / start? / destroy?) on origin/main. The changeset's sentences about all three are true.
  3. Gates (check-runs pinned to this head). Lint & Repo Gates success, TypeScript Type Check success, Check Changeset success (it runs check-adr-0087-registration and check-changeset-no-major). Every other completed run is success or skipped. Test Core (3/8) and (4/8) are in progress at this reading. ⛔ No landing step until they are green. The dogfood battery the (narrowing) arm owes ran on this head (dev report: 348 files passed, 0 failed).
  4. Accepted deviations.
    • The PR body and changeset say Clause-②: no (narrowing); the claim says no. The arm is correct for a removed published export (scripts/pm/clause2-line.mjs), and the claim's two-spelling key stays no.
    • Commit trailers follow AGENTS.md (model-free).
    • The dev wrote the shared FETCH_HEAD once, with git fetch --depth=1 of a fixture commit; nothing reads it.
  5. Out of scope, one line each.
    • String plugins entry naming a default-less module becomes a silent empty AppPlugin: seam-read only, no public-door reach measured, so it is Acceptance notes and not filed. Put to the maintainer as A/B (A = land and follow up in core). The contract review recommends A.
    • The four sibling drivers keep the legacy default, and attw is clean on all four: dropped, because it is not a defect on this card's measure.
    • Should-fix from the review (a test-header note that the pin reads src, not the tarball): rides the next push to this PR if one comes; no push for it alone. Skills-docs: none — … holds: the review re-ran the search, with 0 string entries and 0 default bindings.
  6. Landing. Not governed and below the threshold, so the seat lands it: pr_ready then automerge_enable, once every check on this head is green. The maintainer's veto window on A/B stays open until then. A push after this voids the reading.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 28, 2026 17:18
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 28, 2026
Merged via the queue into main with commit 1c5fe3c Oct 28, 2026
49 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-26206-driver-memory-default branch October 28, 2026 17:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tests tooling

Projects

None yet

2 participants