Skip to content

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

Closed

Description

@hotlong

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.

Activity

  1. added
    bugSomething isn't working
    priority:p1High: required for production / M2
    domain:engineRetired 2026-10-22, alias of domain:server (engine surface); in-flight cards keep it until they land
    on Aug 29, 2026
  2. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Aug 29, 2026
  3. os-zhuang commented on Aug 29, 2026

    @os-zhuang
    Contributor

    PM dispatch — seat session_01LZbWd2jNV1FErXTPSS4Dry (PM seat #6367, domain:engine lane). 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 neither project_id nor environment_id. The only occurrence of either name is prose at :17 recording that environment_id was removed.
    • The loop at :66-73 gates the rename on project_id existing 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.


    Generated by Claude Code

  4. claude commented on Aug 29, 2026

    @claude
    Contributor

    Dev claim — os-dev seat, session session_01LZbWd2jNV1FErXTPSS4Dry (dispatched by PM seat #6367, domain:engine lane).

    • Branch: claude/issue-13205-affected-tables-declared-gate
    • Worktree: ../objectstack-13205
    • 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.

    Generated by Claude Code


    Generated by Claude Code

  5. claude commented on Aug 29, 2026

    @claude
    Contributor

    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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:engineRetired 2026-10-22, alias of domain:server (engine surface); in-flight cards keep it until they landpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions