You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
migrate-project-id-to-environment-id still lists sys_metadata_history in AFFECTED_TABLES — that object now declares NEITHER column; the migration would mint a second orphan #13205
From cloud#1736's audit: sys_metadata_history's declared schema carries neither project_id nor environment_id (both removed in the M1 / branch-removal amendments), but migrate-project-id-to-environment-id.ts still lists it in AFFECTED_TABLES. Running the migration against a DB whose physical table still has project_id would rename it to environment_id — a column no declaration knows about, i.e. a fresh orphan of exactly the class cloud's drift audit just spent 53 findings classifying. Deliverable: drop sys_metadata_history from AFFECTED_TABLES (or gate each entry on the target column existing in the CURRENT declaration), with a test pinning the list to declared reality.
Premise re-verified at current main (e22158fff) before dispatch, so the dev does not re-measure it:
packages/metadata/src/migrations/migrate-project-id-to-environment-id.ts:33-36 — AFFECTED_TABLES = ['sys_metadata', 'sys_metadata_history']. The table is listed.
packages/metadata-core/src/objects/sys-metadata-history.object.ts — declares neitherproject_id nor environment_id. The only occurrence of either name is prose at :17 recording that environment_idwas removed.
The loop at :66-73 gates the rename on project_idexisting physically, not on the target column being declared. So against a DB whose physical sys_metadata_history still carries project_id, it renames to environment_id — a column no declaration knows about. The card's "second orphan" is exact.
Clause-② does not fire: the diff's home is packages/metadata/**, not packages/spec/src/**, and no exported contract narrows. No needs:contract-review.
No file overlap with #13203 (the other new engine card from the same cloud#1736 audit), so the two can run in parallel — but see my separate note there; #13203 is not dispatchable as written.
Scope: packages/metadata/src/migrations/migrate-project-id-to-environment-id.ts + a pinning test + a patch changeset. No governed surfaces, no content/docs/releases/.
Re-read the comments before starting: only the PM dispatch comment was present, no competing claim.
{
"issue": 13205,
"status": "done",
"branch": "claude/issue-13205-affected-tables-declared-gate",
"pr": "https://github.com/objectstack-ai/objectstack/pull/13220",
"premise_still_valid": true,
"summary": "Took route (b): AFFECTED_TABLES in packages/metadata/src/migrations/migrate-project-id-to-environment-id.ts is no longer a hand-written literal but is DERIVED from the current declarations. Route (b) was reachable with no new dependency edge — measured: @objectstack/metadata already lists \"@objectstack/metadata-core\": \"workspace:*\" under dependencies (runtime, not dev), and metadata-core's root entry already exports SysMetadataObject / SysMetadataHistoryObject. A candidate metadata storage table is migrated only if its current declaration carries the target column environment_id, so the list and the declarations are now the same fact and cannot drift apart again. A candidate that fails the gate is REPORTED as status 'skipped_not_declared' (additive member of the ProjectIdToEnvironmentIdResult union; no in-repo caller reads status — both database-loader.ts call sites discard the results) rather than silently vanishing from the result array. The sibling migrateEnvIdToProjectId was deliberately left alone and the reason recorded in the file: its target project_id is an INTERMEDIATE column no current declaration carries by design, so 'the target must be declared' is sound only for the terminal migration of a rename chain. Files changed (3): the migration; a new 9-case pin test beside it; a patch changeset.",
"tests": "All measurements at final commit 74304961e (tree clean, git status empty); heavy steps serialized through scripts/pm/os-verify-lock.sh, verdicts read from the gate's/lock's own printed line, never from a bare $? after a pipe.\n\n1) Dependency closure build — pnpm --filter '@objectstack/metadata^...' build → 'os-verify-lock: VERDICT command-exit 0 · held the lock 263s'.\n2) Package build — pnpm --filter @objectstack/metadata build → 'check-dts-emitted: @objectstack/metadata - 4/4 declared declaration file(s) present.' + VERDICT command-exit 0.\n3) New test file — pnpm --filter @objectstack/metadata exec vitest run --maxWorkers=2 src/migrations/migrate-project-id-to-environment-id.test.ts → 'Test Files 1 passed (1) / Tests 9 passed (9)'.\n4) Whole package — pnpm --filter @objectstack/metadata exec vitest run --maxWorkers=2 → 'Test Files 39 passed (39) / Tests 687 passed (687)', VERDICT command-exit 0.\n5) Typecheck — pnpm --filter @objectstack/metadata exec tsc --noEmit -p tsconfig.json → 89 'error TS' lines, which is EXACTLY the frozen count for '@objectstack/metadata' in check-type-check-coverage.mjs's DEBT ledger (errors: 89), and 0 of them name either changed file — the check:type-check-debt ratchet does not move. NOT-MEASURED trap checked: tsc --listFiles reports 2 hits for migrate-project-id-to-environment-id (both the .ts and the .test.ts are inside the program that produced that count), so this is a measurement over the diff. pnpm check:type-check-coverage exits 0.\n6) Repo-wide lint — pnpm lint (eslint . --no-inline-config) → VERDICT command-exit 0 in 89s. Ran in full; NO narrowing is claimed.\n7) Gate families derived mechanically, not recalled — node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (3 paths vs merge base e22158fff). All 22 runnable matched gates exit 0: nul-bytes, test-source-alias, undeclared-dep-imports, type-source-resolution, cross-package-test-inputs, engine-double-contract, where-matcher, query-options-erasure, objectql-double-limit, durability-log-level, logger-receiver-detach, keyed-text-bounds, comment-mask-adoption, adr-0087-registration, changeset-no-major, empty-changeset, published-files, page-declaration-shape, slot-lookup, plugin-teardown-shape, objectui-changeset, pm-half-states, changeset-gate-self-tests. check:test-source-alias stays green with KNOWN_UNALIASED_TEST_IMPORTS UNCHANGED — '@objectstack/metadata' is already registered for the metadata-core dist edge, so the new test adds nothing to it.\n NOT MEASURED (exit 3 = PREREQUISITE NOT MET, read as neither green nor red): check:dual-build-cjs-loads (wants a full workspace dist) and scripts/check-test-completeness.mjs (grades a saved CI turbo test log it cannot produce). Both are CI-owned runs.\n\nABLATION (reverse verification). Predicted direction RED; observed RED. Fix was COMMITTED FIRST (74304961e), then AFFECTED_TABLES was mutated back to the pre-fix literal ['sys_metadata', 'sys_metadata_history'] by an anchored python replace that ASSERTS the anchor (a zero-hit replace would have raised, not exited 0). Mutation confirmed ON DISK before any run, anchored on the text actually targeted: deleted-text grep -c = 0, injected-text grep -c = 1, and blob hash moved 21013e441079cda371987597c21a5e18b94ac93e -> f484a180fd74b3291ea343cf67ffc117f5ea1263. REBUILD: none required on either leg and that is a property of the resolution path, not an omission — the test imports the module under test by RELATIVE path (source, no dist), and the one dist-resolved import (@objectstack/metadata-core) was not mutated and had been built in step 1 before every run. Result on the ablated tree: 'Test Files 1 failed (1) / Tests 5 failed | 4 passed (9)', including \"expected [ 'sys_metadata_history' ] to deeply equal []\", \"sys_metadata_history: list membership must equal \\\"declares environment_id\\\": expected true to be false\", and \"expected 'renamed' to be 'skipped_not_declared'\" — that last one IS the defect reproduced: with the hand-written list the migration reports sys_metadata_history as renamed and issues the orphan-minting ALTER. RESTORE leg verified by observed state, not by an exit code: git diff HEAD empty, git status --porcelain empty, and git hash-object back to 21013e44... == git rev-parse HEAD:THAT_PATH. The script carried trap RESTORE EXIT INT TERM with an absolute REPO_ROOT-based path, and refused to proceed if the HEAD blob hash came back empty or the tree was not at HEAD before mutating.\n\nPROPERTY THE NEW TEST PINS (packages/metadata/src/migrations/migrate-project-id-to-environment-id.test.ts, 9 cases, no hardcoded copy of today's list anywhere in it): (i) SAFETY, quantified over AFFECTED_TABLES itself — every table the migration will rename resolves to a declaration, and that declaration carries the column the rename produces; a hand-added entry is caught whatever put it there; (ii) membership TRACKS the declaration in BOTH directions — each candidate's membership is compared against its own declaration, so the pin keeps holding and keeps meaning something if a declaration legitimately regains or loses environment_id; (iii) NON-VACUITY — a derivation that degraded to an empty list would satisfy (i) and (ii), so the selected set is also compared against the candidates that do declare the column; (iv) BEHAVIOUR against a physically-stale DB where BOTH tables still carry project_id — exactly one ALTER TABLE, for sys_metadata, and NO statement at all (not even a PRAGMA probe) against the undeclared table; plus idempotency and the raw-driver refusal.",
"mcp_calls": "1 — one targeted mcp__github__search_issues (dedup for the finding below). Everything else went over repo-scoped REST: GET /repos/.../issues/13205, its comments, POST assignees, POST comments, POST issues, POST pulls, GET pulls/13220. Channel note: repo-scoped REST is open on this seat, but the GLOBAL /search/issues path is 403 ('sessions are bound to their configured repositories'), which is why the dedup search alone switched to MCP. Control check on that search: it returned #13205, a known-hit, so the empty result for the finding's own keywords is a real reading.",
"open_questions": [],
"out_of_scope_findings": [
"filed as #13219: migrateProjectIdToEnvironmentId's raw-driver guard prints its instruction sentence twice (label `finding`, unassigned, dedup-searched first). Same file, different defect class, so NOT fixed here — the bounded in-place exemption's condition (i) does not hold; those three lines are left byte-identical by this PR."
]
}
Activity
os-zhuang commentedon Aug 29, 2026
PM dispatch — seat
session_01LZbWd2jNV1FErXTPSS4Dry(PM seat #6367,domain:enginelane).pm:queue→pm:dispatched.Premise re-verified at current
main(e22158fff) before dispatch, so the dev does not re-measure it:packages/metadata/src/migrations/migrate-project-id-to-environment-id.ts:33-36—AFFECTED_TABLES = ['sys_metadata', 'sys_metadata_history']. The table is listed.packages/metadata-core/src/objects/sys-metadata-history.object.ts— declares neitherproject_idnorenvironment_id. The only occurrence of either name is prose at:17recording thatenvironment_idwas removed.:66-73gates the rename onproject_idexisting physically, not on the target column being declared. So against a DB whose physicalsys_metadata_historystill carriesproject_id, it renames toenvironment_id— a column no declaration knows about. The card's "second orphan" is exact.Clause-② does not fire: the diff's home is
packages/metadata/**, notpackages/spec/src/**, and no exported contract narrows. Noneeds:contract-review.No file overlap with #13203 (the other new engine card from the same cloud#1736 audit), so the two can run in parallel — but see my separate note there; #13203 is not dispatchable as written.
Generated by Claude Code
claude commentedon Aug 29, 2026
Dev claim —
os-devseat, sessionsession_01LZbWd2jNV1FErXTPSS4Dry(dispatched by PM seat #6367,domain:enginelane).claude/issue-13205-affected-tables-declared-gate../objectstack-13205packages/metadata/src/migrations/migrate-project-id-to-environment-id.ts+ a pinning test + apatchchangeset. No governed surfaces, nocontent/docs/releases/.Re-read the comments before starting: only the PM dispatch comment was present, no competing claim.
Generated by Claude Code
Generated by Claude Code
fix(metadata): derive migrate-project-id-to-environment-id's AFFECTED…
claude commentedon Aug 29, 2026
os-dev-report
{ "issue": 13205, "status": "done", "branch": "claude/issue-13205-affected-tables-declared-gate", "pr": "https://github.com/objectstack-ai/objectstack/pull/13220", "premise_still_valid": true, "summary": "Took route (b): AFFECTED_TABLES in packages/metadata/src/migrations/migrate-project-id-to-environment-id.ts is no longer a hand-written literal but is DERIVED from the current declarations. Route (b) was reachable with no new dependency edge — measured: @objectstack/metadata already lists \"@objectstack/metadata-core\": \"workspace:*\" under dependencies (runtime, not dev), and metadata-core's root entry already exports SysMetadataObject / SysMetadataHistoryObject. A candidate metadata storage table is migrated only if its current declaration carries the target column environment_id, so the list and the declarations are now the same fact and cannot drift apart again. A candidate that fails the gate is REPORTED as status 'skipped_not_declared' (additive member of the ProjectIdToEnvironmentIdResult union; no in-repo caller reads status — both database-loader.ts call sites discard the results) rather than silently vanishing from the result array. The sibling migrateEnvIdToProjectId was deliberately left alone and the reason recorded in the file: its target project_id is an INTERMEDIATE column no current declaration carries by design, so 'the target must be declared' is sound only for the terminal migration of a rename chain. Files changed (3): the migration; a new 9-case pin test beside it; a patch changeset.", "tests": "All measurements at final commit 74304961e (tree clean, git status empty); heavy steps serialized through scripts/pm/os-verify-lock.sh, verdicts read from the gate's/lock's own printed line, never from a bare $? after a pipe.\n\n1) Dependency closure build — pnpm --filter '@objectstack/metadata^...' build → 'os-verify-lock: VERDICT command-exit 0 · held the lock 263s'.\n2) Package build — pnpm --filter @objectstack/metadata build → 'check-dts-emitted: @objectstack/metadata - 4/4 declared declaration file(s) present.' + VERDICT command-exit 0.\n3) New test file — pnpm --filter @objectstack/metadata exec vitest run --maxWorkers=2 src/migrations/migrate-project-id-to-environment-id.test.ts → 'Test Files 1 passed (1) / Tests 9 passed (9)'.\n4) Whole package — pnpm --filter @objectstack/metadata exec vitest run --maxWorkers=2 → 'Test Files 39 passed (39) / Tests 687 passed (687)', VERDICT command-exit 0.\n5) Typecheck — pnpm --filter @objectstack/metadata exec tsc --noEmit -p tsconfig.json → 89 'error TS' lines, which is EXACTLY the frozen count for '@objectstack/metadata' in check-type-check-coverage.mjs's DEBT ledger (errors: 89), and 0 of them name either changed file — the check:type-check-debt ratchet does not move. NOT-MEASURED trap checked: tsc --listFiles reports 2 hits for migrate-project-id-to-environment-id (both the .ts and the .test.ts are inside the program that produced that count), so this is a measurement over the diff. pnpm check:type-check-coverage exits 0.\n6) Repo-wide lint — pnpm lint (eslint . --no-inline-config) → VERDICT command-exit 0 in 89s. Ran in full; NO narrowing is claimed.\n7) Gate families derived mechanically, not recalled — node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (3 paths vs merge base e22158fff). All 22 runnable matched gates exit 0: nul-bytes, test-source-alias, undeclared-dep-imports, type-source-resolution, cross-package-test-inputs, engine-double-contract, where-matcher, query-options-erasure, objectql-double-limit, durability-log-level, logger-receiver-detach, keyed-text-bounds, comment-mask-adoption, adr-0087-registration, changeset-no-major, empty-changeset, published-files, page-declaration-shape, slot-lookup, plugin-teardown-shape, objectui-changeset, pm-half-states, changeset-gate-self-tests. check:test-source-alias stays green with KNOWN_UNALIASED_TEST_IMPORTS UNCHANGED — '@objectstack/metadata' is already registered for the metadata-core dist edge, so the new test adds nothing to it.\n NOT MEASURED (exit 3 = PREREQUISITE NOT MET, read as neither green nor red): check:dual-build-cjs-loads (wants a full workspace dist) and scripts/check-test-completeness.mjs (grades a saved CI turbo test log it cannot produce). Both are CI-owned runs.\n\nABLATION (reverse verification). Predicted direction RED; observed RED. Fix was COMMITTED FIRST (74304961e), then AFFECTED_TABLES was mutated back to the pre-fix literal ['sys_metadata', 'sys_metadata_history'] by an anchored python replace that ASSERTS the anchor (a zero-hit replace would have raised, not exited 0). Mutation confirmed ON DISK before any run, anchored on the text actually targeted: deleted-text grep -c = 0, injected-text grep -c = 1, and blob hash moved 21013e441079cda371987597c21a5e18b94ac93e -> f484a180fd74b3291ea343cf67ffc117f5ea1263. REBUILD: none required on either leg and that is a property of the resolution path, not an omission — the test imports the module under test by RELATIVE path (source, no dist), and the one dist-resolved import (@objectstack/metadata-core) was not mutated and had been built in step 1 before every run. Result on the ablated tree: 'Test Files 1 failed (1) / Tests 5 failed | 4 passed (9)', including \"expected [ 'sys_metadata_history' ] to deeply equal []\", \"sys_metadata_history: list membership must equal \\\"declares environment_id\\\": expected true to be false\", and \"expected 'renamed' to be 'skipped_not_declared'\" — that last one IS the defect reproduced: with the hand-written list the migration reports sys_metadata_history as renamed and issues the orphan-minting ALTER. RESTORE leg verified by observed state, not by an exit code: git diff HEAD empty, git status --porcelain empty, and git hash-object back to 21013e44... == git rev-parse HEAD:THAT_PATH. The script carried trap RESTORE EXIT INT TERM with an absolute REPO_ROOT-based path, and refused to proceed if the HEAD blob hash came back empty or the tree was not at HEAD before mutating.\n\nPROPERTY THE NEW TEST PINS (packages/metadata/src/migrations/migrate-project-id-to-environment-id.test.ts, 9 cases, no hardcoded copy of today's list anywhere in it): (i) SAFETY, quantified over AFFECTED_TABLES itself — every table the migration will rename resolves to a declaration, and that declaration carries the column the rename produces; a hand-added entry is caught whatever put it there; (ii) membership TRACKS the declaration in BOTH directions — each candidate's membership is compared against its own declaration, so the pin keeps holding and keeps meaning something if a declaration legitimately regains or loses environment_id; (iii) NON-VACUITY — a derivation that degraded to an empty list would satisfy (i) and (ii), so the selected set is also compared against the candidates that do declare the column; (iv) BEHAVIOUR against a physically-stale DB where BOTH tables still carry project_id — exactly one ALTER TABLE, for sys_metadata, and NO statement at all (not even a PRAGMA probe) against the undeclared table; plus idempotency and the raw-driver refusal.", "mcp_calls": "1 — one targeted mcp__github__search_issues (dedup for the finding below). Everything else went over repo-scoped REST: GET /repos/.../issues/13205, its comments, POST assignees, POST comments, POST issues, POST pulls, GET pulls/13220. Channel note: repo-scoped REST is open on this seat, but the GLOBAL /search/issues path is 403 ('sessions are bound to their configured repositories'), which is why the dedup search alone switched to MCP. Control check on that search: it returned #13205, a known-hit, so the empty result for the finding's own keywords is a real reading.", "open_questions": [], "out_of_scope_findings": [ "filed as #13219: migrateProjectIdToEnvironmentId's raw-driver guard prints its instruction sentence twice (label `finding`, unassigned, dedup-searched first). Same file, different defect class, so NOT fixed here — the bounded in-place exemption's condition (i) does not hold; those three lines are left byte-identical by this PR." ] }Generated by Claude Code
fix(metadata): derive the project_id-to-environment_id migration's ta…