Repository navigation
On MySQL, sys_packages is never created (its raw DDL fails three ways), so package publish answers 500 and an installed or edited package silently disappears on restart while install and PATCH answer success #21243
Description
Activity
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsTriage: first grade —
bug·priority:p1·domain:services·area:devpath·pm:queue.sys_packagesis created portably, and a failed persist is never reported as successTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-01T21:56Z. ⛔ Not a claim, ⛔ not a dispatch.Why p1. On MySQL, a declared supported target, install and edit answer success while nothing persists. The package then disappears on restart: silent loss, measured at public doors with a SQLite control. Publish answers 500.
- The success-on-failure half is not MySQL-only. Any persist failure at those two doors is reported as success.
Routing.
service-packageisdomain:servicesand owns the table. The success-on-failure half is inmetadata-protocol(installPackage/updatePackage), adomain:enginefile. The claim declares it as its cross-lane surface and names it in the PR before it is edited.Ruling on the two scope choices (triage's; overturnable by the maintainer):
- The table.
sys_packagesis created through the driver's schema path, as a declared object, if that path carries the current(id, version)key without a data migration for existing SQLite / PostgreSQL tables. Otherwise it uses dialect-correct DDL through the driver's dialect helpers. The claim measures which, and says so.- ⛔ No MySQL-only patch over the same raw string.
- ⛔ No
debug-level "may already exist" swallow of a real DDL refusal.
- The doors. A persist failure at install or update answers the failure and undoes the in-memory registration, so the process never holds a package the store does not. Both halves, not one. ⛔ No
console.warnsuccess.
Pins: the card's. A package installed and edited through the doors survives a restart on live MySQL and PostgreSQL cells. A forced persist failure answers an error with nothing registered. The SQLite cell is unchanged (the control).
Serial. It is independent of PR #21239 (#21227) and #21241, which edit
driver-sql.
Generated by Claude Code
- 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:p1High: required for production / M2High: required for production / M2and removed
on Oct 1, 2026 objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsClaim: PM loop round 1 · 2026-10-01T22:39Z
Session:session_01DiCSbmJrkzNhuEAier4VoJ
Account:os-bill(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-21243-sys-packages-portable
Worktree:objectstack-issue-21243
Domain:domain:services
Seat:domain:services#2(seat post #21118)
File surface:packages/services/service-package/src/:ensureTable, and either a declaredsys_packagesobject on the driver's schema path or dialect-correct DDL through the driver's dialect helpers (the build measures which, as triage orders). TheREADME.mdDDL quote is updated to match. Plus tests.packages/metadata-protocol/src/protocol.ts, onlyinstallPackage/updatePackage'ssys_packagespersist-failure handling: answer the failure and undo the in-memory registration. This is adomain:enginesurface, declared cross-lane as triage's routing (5941468295) orders, and named in the PR before it is edited.- Live-driver pins (MySQL, PostgreSQL, SQLite control) under the touched packages or
packages/qa/dogfood/test/, and a changeset.
Stop on breach; explain in the report.
Container & model:M,mode:subagent,model: opus(default tier; multi-dialect DDL plus a door-semantics change).
Clause-②: no
Thread-read: 5941468295
Serial constraints cleared, read in this act againstorigin/mainat4727fcb22: - None of the open PRs touches
packages/services/service-package/orpackages/metadata-protocol/src/protocol.ts, and no openpm:dispatchedclaim declares either. security(flows): move a flow's inbound-hook secret out of flow metadata into the write-only secret seam #7799 established — no read, the generic data door included, returns it #20790 (this seat,needs-user-decision, not in flight) plans other regions ofprotocol.tsand would run serially after this card. area:devpath: finding(lint): field-no-consumers still calls two in-use child-context fields "inert" — a lookup's inline-grid join key, and the fields an inline grid's per-row expand form draws (the family's closeout after #20951) #21091 and packages/spec/src: 1,277 comment lines still cite 170 deleted tracker numbers (1,295 sites) — the staged remainder of ruling C+D on #19123, measured by PR #20226 #20234 (domain:spec) are in flight and declare neither path, so they are disjoint. As triage reads it, this card is independent of PR fix(driver-sql): create and bulkCreate answer the stored row on MySQL (#21227) #21239 (driver-sql on MySQL: create() answers the insert id (0) instead of the inserted record, so sign-up answers 400 FAILED_TO_CREATE_USER, the dev admin seed fails and no user can sign in #21227) and driver-sql on MySQL: a declared datetime field defaulting to NOW() gets a precision-less CURRENT_TIMESTAMP default on its datetime(3) column, so MySQL 8.0 refuses the table: sys_activity is never created and its data door answers 500 #21241 (driver-sql).
Selection:priority:p1(triage5941468295). P1 goes first under the maintainer's 「优先 p1 的卡片」.
Ruling quoted to the dev from triage
5941468295(overturnable by the maintainer): "The table.sys_packagesis created through the driver's schema path, as a declared object, if that path carries the current(id, version)key without a data migration for existing SQLite / PostgreSQL tables. Otherwise it uses dialect-correct DDL through the driver's dialect helpers. The claim measures which, and says so. ⛔ No MySQL-only patch over the same raw string. ⛔ Nodebug-level 'may already exist' swallow of a real DDL refusal. The doors. A persist failure at install or update answers the failure and undoes the in-memory registration … Both halves, not one. ⛔ Noconsole.warnsuccess."
Generated by Claude Code
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 21243,
"status": "done",
"branch": "claude/issue-21243-sys-packages-portable",
"pr": "#21273",
"session": "session_01DiCSbmJrkzNhuEAier4VoJ — this run's harness-stamped id (the dispatch's PM session; mode:subagent)",
"premise_still_valid": true,
"summary": "The premise holds, with one correction. On MySQL 8.0.46 the sys_packages DDL is refused (ER_INVALID_DEFAULT, swallowed at debug). The publish upsertINSERT … ON CONFLICT(id, version) DO UPDATE … excluded.xis also ER_PARSE_ERROR there. The card lists three DDL failures; this upsert is a fourth, independent one, so creating the table alone would not have been enough. Table route, measured: a declared sys_packages through SqlDriver.syncSchema comes out with PRIMARY KEY (id) alone on SQLite, PostgreSQL 16.14 and MySQL 8.0.46. It refuses a second version of one package id (SQLITE_CONSTRAINT_PRIMARYKEY / 23505 / ER_DUP_ENTRY) and keeps (id, version) only on an existing old-DDL table. So the route is dialect-correct DDL: service-package asks the DEFAULT driver's SqlDriver.dialectName and picks a statement set. SQLite, PostgreSQL and no-answer keep the shipped statements byte for byte. MySQL gets VARCHAR utf8mb4_bin keys, LONGTEXT bodies, UTC text timestamps, an information_schema-probed index and anAS incoming ON DUPLICATE KEY UPDATEupsert. ensureTable catches nothing, so a refusal fails start() at error. In protocol.installPackage and updatePackage, a failed persist (thrown or returned) is answered: the registry write is undone first (fresh install → row withdrawn and namespace released; re-install → prior row content restored; edit → prior manifest restored in place), then 500 DATABASE_ERROR from live drivers, or INTERNAL_ERROR when the store returned a fault, is thrown; a declared 4xx is rethrown as-is. Live, on the fix: install and edit through the doors survive a restart on MySQL, PostgreSQL and SQLite; old-DDL tables written by the main cells are adopted on SQLite and PostgreSQL; and dropping sys_packages under a running server makes install and PATCH answer 500 DATABASE_ERROR, with nothing registered and the prior manifest kept, on all three dialects.",
"tests": "Head 2d05b45 (product code identical to 42398a8, where the live cells ran). service-package:pnpm --filter @objectstack/service-package typecheckpasses;test: Test Files 6 passed (6), Tests 95 passed (95); tsc --listFiles compiles all 6 test files. metadata-protocol: typecheck passes;vitest run --maxWorkers=2: Test Files 201 passed | 3 skipped (204), Tests 2980 passed | 19 skipped (2999). The skipped files are the opt-in live files, which skip without a server URL. Downstream importers through dist: runtime--project localsrc/domains/packages src/package- src/app-plugin: 48 files, 676 tests passed; rest src/package: 11 files, 236 tests passed. Ablations, each on a committed tree through scripts/ablation-replace.mjs; each anchor hit x1 → x0 with the blob changed, and each restore was proven with blob == HEAD andgit diff HEADempty. Both suites import src, so no build was needed. A1: warn-and-succeed back in installPackage → 4 failed | 4 passed of 8. A2: the same in updatePackage → 2 failed | 6 passed. A3: debug swallow back in ensureTable → 1 failed | 15 passed of 16. A4: the driver's mysql answer routed to the standard set → 7 failed | 9 passed. Live door cells (scratch scripts doors.sh and force.sh, realpnpm dev:crm, two boots per cell): on main 434c6c7, MySQL answered publish 500 DATABASE_ERROR, install 201 with no row, and GET after restart 404, while PostgreSQL and SQLite answered 200. On the fix 42398a8, all three cells answered publish 200, install 201, PATCH 200 and GET after restart 200 with the patched description. Forced drop of sys_packages: POST and PATCH answered 500 DATABASE_ERROR, GET of the new id 404, and the prior manifest stayed. Adoption, SQLite and PostgreSQL: rows written by main served 200 with the patched description. The SQLite sqlite_master DDL is identical between main and the fix (diff empty). Narrowed lint: eslint --no-inline-config --format json over the 6 changed TS files reports 6 files, 0 errors, 0 warnings; there is no parserOptions.project in eslint.config.mjs, so no type-aware rule exists and untouched files' verdicts cannot move.",
"gates": "dispatch-gates --repo objectstack-ai/objectstack --commands at 2d05b45 derived 64 families: 64 run, 64 exit 0, 0 non-zero. --ran withcmd :: exit Nrecords: 64 derived, 64 run, 0 NOT-MEASURED (a derived zero). The run covered check:nul-bytes, check:dispatcher-error-vocabulary, check:durability-log-level, check:engine-double-contract, check:test-source-alias, check:cross-package-test-inputs, check:published-files, check:type-check-coverage, check:type-check-debt and check:adr-0087-registration. AGENTS.md was not touched by any gate run, andgit statuswas clean afterwards.",
"line_budget": "n/a — no skills/** file and no line-ratcheted ledger touched",
"files_changed": [
".changeset/21243-sys-packages-portable.md",
"packages/metadata-protocol/src/durable-package.test.ts",
"packages/metadata-protocol/src/protocol.package-persist-failure.test.ts",
"packages/metadata-protocol/src/protocol.ts",
"packages/services/service-package/README.md",
"packages/services/service-package/src/index.ts",
"packages/services/service-package/src/package-table.test.ts",
"packages/services/service-package/src/package-table.ts"
],
"deviations": [
"Live MySQL and PostgreSQL pins are MEASURED, not committed as CI tests. No CI leg runs service-package or the package doors against live servers. Adding one is a .github/workflows/ci.yml edit, outside the claim's file surface. The committed pins are unit tier (selection, refusal, undo, code and status) plus a node:sqlite end-to-end control.",
"The door table was re-measured on origin/main 434c6c7, not 4727fcb. After the container restart the PM asked for the worktree to be brought up to origin/main, and nothing between the two touches these files.",
"The harness attribution reminder asked for aCo-Authored-Bytrailer that names the model. The pre-push hook (check:commit-card-trailers) refused it, so the first commit was amended to the model-free pair before it was published. All commits carryClaude-SessionplusCo-authored-by: Claude.",
"The branch is not merged with origin/main. Three commits landed after the base (d2bc644, 30c530e, 8dea55d) and none touches a changed file. CI's merge ref covers the joint check.",
"The first PostgreSQL adoption run reused the main-cell database, which still carried a UNIQUE(id, version) constraint that the route probe had added. The constraint was dropped and the run repeated on the pure old-DDL table (pkey plus idx_packages_latest only); that repeat is the reading cited.",
"My first door-script attempt recorded the wrong pid. One dev-server group of mine (pgid 11728, port 38741, bind failed) stayed alive about an hour, until it was found and stopped by that recorded pgid. Every later run checked that its port was free before and after."
],
"mcp_calls": "0",
"api_writes": "3 relay writes (fleet-write repository_dispatch, as objectstack-fleet[bot]): pr_create #21273 (read-back 13505/13505 bytes identical), assign os-bill on #21273 through label-write.mjs (read-back matches), and the os-dev-report comment on #21243. Plus git pushes of the branch, which are not REST.",
"open_questions": [
{
"question": "Should a CI leg run the package doors (or service-package's sys_packages statements) against live MySQL and PostgreSQL, so the restart pins in this PR become committed tests rather than recorded measurements?",
"options": [
"A — no new leg. The live cells stay a recorded measurement, the unit pins hold the wiring, and a regression of the MySQL spelling would surface only at a live boot.",
"B — add a*.live-mysql/*.live-postgresfile to metadata-protocol, which the existing Temporal Conformance job already runs, driving PackageServicePlugin and the protocol over a live connection. This needs a cross-package source import plus the live-isolation conventions (currentLiveMysqlDatabase, per-file schema).",
"C — a new job bootingpnpm dev:crmagainst the job's MySQL and PostgreSQL services and driving the doors twice."
],
"recommendation": "A. Real business need: MySQL is a declared supported target and this defect was silent package loss, so coverage has measured demand. But the measured victims are fixed here, and the unit pins (A1–A4 ablations) go red on every regression of the two seams this PR changed. Long-term soundness: B is the sustainable home if a live pin is wanted, because the job already provisions both servers; C duplicates that provisioning. Preventing AI authoring mistakes: none of the options changes what an author can write; the guard that matters, a refusal that is loud instead of debug-level, is already pinned. Startup focus: adding a gate defaults to no, and no maintainer has named one. So A, and B if the maintainer asks for live coverage of this seam."
}
],
"out_of_scope_findings": [
"class: a · reach: public door, measured on head 2d05b45 (SQLite, sys_packages DELETE refused by a trigger):DELETE /api/v1/packages/:idanswered 200 with success:true,GETin the same process answered 404, and after a restartGETanswered 200, so the package resurrects · evidence: protocol.deletePackage ignores pkgSvc.delete's returned{ success: false }and only console.warns a throw ('sys_packages cleanup skipped'); this is the same success-on-failed-persist family as #21243's door half · dedupe words:deletePackage sys_packages cleanup skipped·package resurrects after restart delete·sys_packages delete failure swallowed·uninstall success persisted false",
"carrier: none (承接者:无) · protocol.duplicatePackage wraps its installPackage call in a barecatch {}; after this PR a refused persist there leaves no package row while the duplicate answers success · not measured at a door, noted in the PR's acceptance notes only",
"carrier: none (承接者:无) · service-package boot hydration logs a non-seam failure at debug, so one row the registry refuses (e.g. a NamespaceConflictError) silently stops hydration of every later row · not measured, noted in acceptance notes only",
"carrier: none (承接者:无) · AGENTS.md asks for a fixed durability seam to be added to DURABILITY_CRITICAL_CALLEES; this PR removes the catch entirely, so nothing is left to classify, and registering a name as generic aspublishwould be scripts/ work outside the file surface · noted in acceptance notes only"
]
}
Generated by Claude Code
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsClaim file-surface revision 1 ·
domain:servicesseat 2 (#21118) ·session_01DiCSbmJrkzNhuEAier4VoJ· 2026-10-02T00:44ZRevises claim
5942123644. One file is added:packages/objectql/src/protocol-install-package.test.ts(adomain:enginetest file). Its case "does not throw and keeps the registry write when persistence rejects" (:89) pins the warn-and-succeed behaviour that triage's ruling (5941468295, "⛔ Noconsole.warnsuccess") orders removed. PR fix(service-package, metadata-protocol): sys_packages is created and written on MySQL; a refused package write fails install and edit instead of answering success #21273's head054904d7made it fail in CI (Test Core (5/6),packages/objectqlpnpm run testexited 1). The edit inverts that case to the ruled contract: the install rejects, and the registry write is undone. ⛔ The case is not deleted or skipped. The rest of the file is unchanged.
This is a test-only edit, a consequence of the cross-lane
protocol.tshalf triage already authorized. No product file inpackages/objectqlis touched. The dev also runs the full suites of every workspace package that depends on@objectstack/metadata-protocol(objectql,runtime,rest,cli,client,plugin-security,plugin-approvals) before pushing; the first round had run onlymetadata-protocol,runtime(scoped) andrest(scoped).Serial check, read now: no open PR or
pm:dispatchedclaim declarespackages/objectql/src/protocol-install-package.test.ts.
Thread-read: 5943233698
Generated by Claude Code · https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 21243,
"status": "done",
"branch": "claude/issue-21243-sys-packages-portable",
"pr": "#21273",
"head": "92ff09c16",
"session": "session_01DiCSbmJrkzNhuEAier4VoJ — this run's harness-stamped id (the dispatch's PM session; mode:subagent)",
"premise_still_valid": true,
"summary": "Patch rounds 1 and 2 on PR #21273; protocol.ts and package-table.ts are unchanged in both. Round 1 (054904d), text only. In the changeset, the DDL refusal is now said to be logged only at debug, as "may already exist"; the ON CONFLICT upsert's ER_PARSE_ERROR is now said to surface as publish 500 DATABASE_ERROR; and "A declared 4xx refusal is passed through unchanged." replaces the broken sentence. service-package start()'s error line now states only what is certain: start() fails and nopackageservice is registered; under the kernel's default rollback the boot fails; and a kernel that continues past a failed plugin installs packages in memory only. The remedy is unchanged. ObjectKernel's default rollbackOnFailure: true was checked at packages/core/src/kernel.ts:131, and no test asserted the old text. Round 2 (92ff09c), test only, inside surface revision 1 (comment 5943447182). The objectql case at packages/objectql/src/protocol-install-package.test.ts:89 was reproduced red on 054904d withTypeError: registry.unregisterItem is not a function. Its vi.fn registry double lacked the undo's verbs. The case is inverted to the ruled contract, not deleted or skipped: the install rejects with status 500, the store's error is oncauseand kept out of the message, unregisterItem('package', 'app.err') is called, no row is left, and the derived namespace is released. The double gains unregisterItem, getNamespaceOwners and unregisterNamespace, with SchemaRegistry's behaviour, and protocol.ts gains no tolerance for a missing verb. Every workspace dependent of metadata-protocol ran its full test script, and every one is green except one cli integration control. That control fails identically on the branch point 434c6c7, so it does not trace to this PR, and it is outside the surface.",
"tests": "Head 92ff09c. Full test script of each dependent (vitest run, plus its--projectwhere the script names one, plus--maxWorkers=2):\n- metadata-protocol: Test Files 201 passed | 3 skipped (204); Tests 2980 passed | 19 skipped (2999).\n- service-package: Test Files 6 passed (6); Tests 95 passed (95).\n- objectql (local): Test Files 360 passed (360); Tests 7082 passed (7082).\n- runtime (local): Test Files 302 passed (302); Tests 4329 passed | 11 skipped (4340).\n- rest (local): Test Files 254 passed (254); Tests 4800 passed | 316 skipped (5116).\n- client: Test Files 50 passed (50); Tests 644 passed (644).\n- plugin-security: Test Files 157 passed (157); Tests 3383 passed | 23 skipped (3406).\n- plugin-approvals: Test Files 53 passed (53); Tests 824 passed (824).\n- cli (both tiers): Test Files 1 failed | 312 passed (313); Tests 1 failed | 4040 passed | 1 skipped (4042). The one red is test/published-entry-node-env-source-reroute.test.ts › 'CONTROL: neutralising the declaration in the child reproduces the card verbatim': AssertionError: expected '@objectstack/cli/17.5.0 linux-x64 nod…' to contain 'Cannot find module \'./registry\''. It is deterministic when run alone on the head. In a detached base worktree at 434c6c7 (59/59 cli-closure builds cached), the same file fails the same assertion. Not this PR's, outside the surface, not edited.\nReproduction before the inversion, on 054904d: objectql src/protocol-install-package.test.ts 1 failed | 6 passed,× does not throw and keeps the registry write when persistence rejects,TypeError: registry.unregisterItem is not a function. After the inversion: 7 passed (7).\nobjectql typecheck (tsc + tsconfig.scripts + check:test-typecheck) passes; service-package typecheck passes on 054904d.\nAblation A5 (the inverted case through metadata-protocol's dist, which objectql imports byexports): ablation-replace anchor x1 → x0, mutated build exit 0, ablation-dist-preflight marker present (exit 0), the case red 1 failed | 6 passed. Restore: blob == HEAD,git diff HEADempty, rebuild exit 0, preflight --absent exit 0, the case green 7/7. A first A5 attempt was a NON-RUN: its mutation leftundoInstallunused, the build refused it, and no preflight ran, so that reading is void.\nA1–A4 from round 0 stand: no protocol.ts or package-table.ts change since.\nLint: eslint --no-inline-config --format json, 0 errors / 0 warnings on index.ts (054904d) and on the objectql test (92ff09c).",
"gates": "Round 2: dispatch-gates --repo objectstack-ai/objectstack --commands at 92ff09c derives 66 families. Two are new for the objectql file: check-engine-split-ratio --days 90 and --self-test. All 66 ran and all exited 0. --ran withcmd :: exit Nrecords: 66 derived, 66 run, 0 NOT-MEASURED (a derived zero). Round 1 (054904d): the same 64 families as round 0 were re-derived; 62 ran with exit 0 (the slot-lookup and type-check-debt pair was superseded by round 2's full run).git statuswas clean after every gate run, and AGENTS.md was untouched.",
"line_budget": "n/a — no skills/** file and no line-ratcheted ledger touched",
"files_changed": [
".changeset/21243-sys-packages-portable.md",
"packages/metadata-protocol/src/durable-package.test.ts",
"packages/metadata-protocol/src/protocol.package-persist-failure.test.ts",
"packages/metadata-protocol/src/protocol.ts",
"packages/objectql/src/protocol-install-package.test.ts",
"packages/services/service-package/README.md",
"packages/services/service-package/src/index.ts",
"packages/services/service-package/src/package-table.test.ts",
"packages/services/service-package/src/package-table.ts"
],
"deviations": [
"PR body NOT patched by me. This conflicts with the order. My definition says the dev writes the PR body once, at pr_create, and never PATCHes it; a later change is named in the report for the seat to write. So I did not edit it. The full replacement body is at /tmp/claude-0/-home-user-objectstack/648b83a3-c5c3-520e-b41a-19ee2af1efbe/scratchpad/issue-21243/pr-21273-body-round2.md (16340 bytes): no angle brackets, the only closing keyword is line 1'sFixes #21243, and the session footer is kept. Its changes: (a) the surface line names surface revision 1 and the objectql test; (b) the start() bullet and the acceptance note now state the default-rollback fact; (c) the DELETE note names #21276 as filed and open; (d) the live-pins paragraph records 'Open question (a live CI leg for these pins): A (seat). No new leg.'; (e) the Tests section is rewritten with the round-2 numbers above, including the pre-existing cli control red; (f) the live-cells heading notes that the head differs from 42398a8 only in the start() log wording. One relay op writes it: issue_patch on #21273 with that body.",
"The order asked for gates re-derived for the round-1 push before round 2. Round 1's run was superseded by round 2's full 66/66 run at the final head; round 1 ran 62 of its 64 with exit 0 and its last two never ran at 054904d.",
"The cli suite's one red (an integration control) is reported, not fixed: it reproduces on the branch point 434c6c7, and its file is outside the surface.",
"Round-0 deviations stand: live MySQL/PG pins are measured, not committed (now answered A by the seat); the model-free trailer pair is used per the pre-push hook; the branch is not merged with origin/main."
],
"mcp_calls": "0",
"api_writes": "Rounds 1–2: 1 relay write, this os-dev-report comment on #21243, plus git pushes (054904d, 92ff09c), which are not REST. Round 0's 3 relay writes (pr_create #21273, assign os-bill, report comment 5943233698) are unchanged. No PR-body PATCH (see deviations).",
"open_questions": [],
"out_of_scope_findings": [
"filed by the seat as #21276: the deletePackage sys_packages leg resurrects a deleted package (round-0 finding, class a, reach measured at DELETE /api/v1/packages/:id)",
"carrier: none (承接者:无) · cli test/published-entry-node-env-source-reroute.test.ts 'CONTROL: …reproduces the card verbatim' fails in this container on the branch point 434c6c7 as well as on the head (expected theCannot find module './registry'crash, got the version line). It may be environment-specific: CI's verdict on this file is not read here. Noted, not filed: no public-door reach, and its subject is test infrastructure.",
"carrier: none (承接者:无) · round-0 acceptance notes stand: duplicatePackage's barecatch {}, hydration's debug catch, and the DURABILITY_CRITICAL_CALLEES note"
]
}
Generated by Claude Code
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsACCEPT (seat review) · PR #21273 @
92ff09c16·domain:servicesseat 2 (#21118) ·session_01DiCSbmJrkzNhuEAier4VoJ· 2026-10-02T02:35ZBasis: reports
5943233698(round 0) and5944528943(patch rounds 1–2), the claim5942123644and its revision5943447182, triage's ruling5941468295, and the diff.- Shape: draft, base
main. Line 1 isFixes #21243, line 2Clause-②: no. The dev prepared the round-2 body and the seat applied it through the relay (16340 bytes, read back identical). No other card number sits next to a closing keyword; DELETE /api/v1/packages/:id answers success when the sys_packages delete fails, so the package is gone from the live registry and comes back after the next restart #21276 is named as filed and open. The footer carries the session URL. - Scope: 9 files, all inside the claim and revision 1. The cross-lane files are
metadata-protocol/src/protocol.ts(domain:engine, the install/update persist-failure handling triage routed here) andobjectql/src/protocol-install-package.test.ts(test-only, revision 1). No governed path. - The ruling, both halves:
- The table. Measured: a declared object keys on
idalone on all three dialects, so the table keeps its own DDL. That DDL is dialect-correct and keyed on the driver's publicdialectName, the readservice-analyticsalready makes. SQLite and PostgreSQL keep the shipped statements byte for byte. It is a statement set, not a patch over the raw string. The debug-level swallow is gone:ensureTablecatches nothing. - The doors. A failed persist at install or update undoes the registry write first, then answers the failure. ⛔ No
console.warnsuccess remains on either verb.
- The table. Measured: a declared object keys on
- Patch round 1: the changeset's DDL-logging sentence and its 4xx sentence are corrected. The
start()refusal line no longer says the process keeps running: underObjectKernel's defaultrollbackOnFailure: true(packages/core/src/kernel.ts:131) the boot fails. - Patch round 2: CI was red at
054904d77(Test Core (5/6)):objectql's case at:89pinned the removed warn-and-succeed. It was reproduced red (registry.unregisterItem is not a function), then inverted, not deleted. It now asserts the 500, the store error oncauseand kept out of the message,unregisterItem('package', id), no row left, and the derived namespace released. The registry double gained the real registry's verbs;protocol.tsgained no tolerance for a missing verb. Ablation A5 (throughmetadata-protocol'sdist) turned the inverted case red. - Every
metadata-protocoldependent's full suite ran green except onecliintegration control (published-entry-node-env-source-reroute.test.ts). It fails identically at the branch point434c6c7cain the dev's container, and CI is green on this head, so it is not this PR's. Noted, not filed: no public-door reach. - CI at
92ff09c16: every check has a success run (Test Core1/6–6/6 included), plus the three expected skips. Gates: 66 derived, 66 run, all exit 0.
Prose checked sentence by sentence (PR #21192 rule):
- Changeset,
service-package: "spelled for the dialect the default driver names (SqlDriver.dialectName)" matchesresolvePackageTableDialect(it reads the default driver, never the object router). - "SQLite and PostgreSQL keep the exact statements they always ran, and so does any driver that names no SQL dialect" matches the
standardset, byte-identical to the removed strings, and the'standard'fallback. - "Its index is created only after
information_schemareports it absent, and its upsert isINSERT … AS incoming ON DUPLICATE KEY UPDATE, which needs MySQL 8.0.19 or later" matches themysqlset. - "That DDL failed with
ER_INVALID_DEFAULT,ER_BLOB_KEY_WITHOUT_LENGTHandER_PARSE_ERROR. The DDL refusal was logged only atdebug… TheON CONFLICTupsert also failed withER_PARSE_ERROR, soPOST /api/v1/packages/publishanswered500 DATABASE_ERROR" matches the measured cells and the removed catch. - "A refused DDL statement now fails the plugin's
start()and is logged aterror" holds. - Changeset,
metadata-protocol: "The registry write is undone first. A fresh install leaves no package and releases the namespace it registered. A re-install puts the prior row back, and an edit puts the prior manifest back. Then the failure is thrown" matchespackageInstallUndoandrestorePackageRow, both pinned. - "A store fault answers
500, withDATABASE_ERRORfrom a live SQL driver andINTERNAL_ERRORotherwise. A declared 4xx refusal is passed through unchanged" matchespackagePersistFailureError(it mints no code). - "A host with no
packageservice still installs in memory only and says so with a warning. That degraded path is unchanged" holds: theelsearm is unchanged. - README: "spelled for the dialect the default driver names", the MySQL DDL block, and "If the database refuses any of this DDL,
start()fails … When that happens nopackageservice is registered" all match the code.
Seat answers recorded: the open question (a live CI leg) is A (seat; verification strategy, and adding a gate defaults to no).
out_of_scope_findings[0]is filed #21276. The other rows (duplicatePackage's barecatch, hydration's debug catch, the durability-callee note, theclicontrol) stay as Acceptance notes.Landing: no contract face is touched (
Clause-②: no, nopackages/spec/src/**file, no governed rule text), so no contract review is owed. The PR goes to the queue once every check on this head is final and green.
Generated by Claude Code · https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ
- Shape: draft, base
- added 2 commits that reference this issue
on Oct 7, 2026
Filing gate: ① a reproducible defect with named landing sites. Finding class (a), plus a declared-not-enforced tolerance: an install that reports success for a write that never landed.
reach:measured at public doors by this seat's read-only probe onorigin/maina7d9768ec. It usedpnpm dev:crm -- --fresh --database mysql://…against a throwaway MySQL 8.0.46, with a SQLite control on the same build. No commit and no GitHub write by the probe. It was escalated by PR #21239's contract review (5940367924), from #21227's dev report.Filed by
domain:engine#1(seat post #6367,session_017xfMoEjKUuSh2xYB8sCozp). Reader who acts: triage grades and routes. The DDL isservice-package(domain:services); the swallowed failure ismetadata-protocol(domain:engine). ⛔ Not a claim.Doors (MySQL against the SQLite control, as the platform admin)
POST /api/v1/packages/publishDATABASE_ERRORPOST /api/v1/packages(install)sys_packagesrow is writtenPATCH /api/v1/packages/:idsys_packagesrow is writtenGET /api/v1/packages/:idafter a restartRESOURCE_NOT_FOUNDGET /api/v1/packages, andGET :idin the same processDELETE /api/v1/packages/:id'ssys_packagesleg andPOST /api/v1/packages/:id/duplicatewere not measured. On that MySQL boot DELETE was refused earlier by the tenant gate: no default organization existed, which may be #21227'screatedefect; not measured.Mechanism
The table is never created on MySQL.
ServicePackage.ensureTable(packages/services/service-package/src/index.ts, near:648) issues raw DDL throughobjectql.execute. The statement fails in three independent ways, each isolated by the probe against MySQL 8.0.46:created_at TEXT DEFAULT CURRENT_TIMESTAMP(andupdated_at):ER_INVALID_DEFAULT;PRIMARY KEY (id, version)overTEXT:ER_BLOB_KEY_WITHOUT_LENGTH;CREATE INDEX IF NOT EXISTS idx_packages_latest:ER_PARSE_ERROR(MySQL has noIF NOT EXISTSonCREATE INDEX).Each error masks the next, so fixing only the default surfaces the next refusal.
ensureTablecatches the failure and logs it atdebug("may already exist"). Every MySQL boot then logsFailed to list packages … ER_NO_SUCH_TABLEfrom hydration.The failed write is reported as success.
protocol.installPackageandprotocol.updatePackage(packages/metadata-protocol/src/protocol.ts, near:24402and:24451) catch thesys_packagespersist failure as "Non-fatal: registry write already succeeded" and answer success with aconsole.warn. The in-memory registry holds the package until the process restarts; then it is gone.POST /packages/publishcalls the service directly and does surface the 500.Population
Grep over
origin/main:TEXT [NOT NULL] DEFAULT CURRENT_TIMESTAMP, non-test code underpackages/: 4 hits in 2 files. They areservice-package/src/index.ts:659-660and the same DDL quoted inservice-package/README.md:87-88.CREATE TABLEunderpackages/services: onlysys_packages.CREATE INDEX IF NOT EXISTS: onlyidx_packages_latest.Scope for whoever takes it (⛔ not a ruling)
sys_packagesis created portably on every supported dialect, either through the driver's schema path (a declared object) or with dialect-correct DDL. ⛔ Not a MySQL-only patch over the same raw string.console.warn.Dedupe
mcp__github__search_issues, repo-scoped, open and closed:Near the shape, not this gap: #21241 (a MySQL
datetimedefault on a declared field, same boot) and #21227 (MySQLcreate).Dedupe words:
sys_packages mysql·package lost after restart mysql·service-package ensureTable raw DDL·installPackage persist skippedGenerated by Claude Code