fix(metadata): state the raw-driver remedy once in migrateProjectIdToEnvironmentId - #13243
fix(metadata): state the raw-driver remedy once in migrateProjectIdToEnvironmentId#13243os-zhuang wants to merge 1 commit into
Conversation
…EnvironmentId (#13219) The guard concatenated its instruction sentence twice, so an operator calling the migration with a driver that has no `raw()` read the same remedy twice in one message. A copy-paste artifact: the sibling `migrateEnvIdToProjectId` carries the correct single-sentence form of the identical guard. Cosmetic and operator-facing only — the guard fires on the same condition and names the same remedy. The surviving line keeps the trailing space inside its literal, which is what separates it from the sentence naming the conforming drivers; trimming it would run the two sentences together, this defect inverted. The package's refusal case now pins the properties of the assembled message (the instruction appears exactly once, no sentence runs into the next, and the supporting sentence is still present) instead of substring-matching it, which could not see a second copy and so passed either way. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZbWd2jNV1FErXTPSS4Dry
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 12 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin c57cb74096ad3f96496615cedfc6e96719b56108 && git checkout c57cb74096ad3f96496615cedfc6e96719b56108
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 9e0ba21a1bf863c44e341fdad1bbecb755d450c2 f2ff3b77d9081461bb7a042644433ea8e7a3fae5 && git checkout -B drift-repro 9e0ba21a1bf863c44e341fdad1bbecb755d450c2 && git merge --no-ff f2ff3b77d9081461bb7a042644433ea8e7a3fae5
node scripts/docs-audit/affected-docs.mjs --json 9e0ba21a1bf863c44e341fdad1bbecb755d450c2 |
|
PM review — seat Verified
Independently checked the docs surface the drift bot flagged as uncovered: no page in The test is the part worth readingIt pins three properties and none of them is a copy of today's wording:
It also records why the defect survived: the previous assertion was The reverse verification was test-first rather than an ablation — assertions written and run before the source change, observed red on the new case only with the eight pre-existing cases staying green. That is the stronger form here: no implementation was ever mutated, so there is no restore leg to go wrong. One process note, and the dev was rightThe report flags that my dispatch said the issue was already claimed and told it not to touch the assignee, while the issue was in fact unassigned. Also noted: the guard had moved to Not landing yetCI just started. Holding in draft until every check is Generated by Claude Code |
Fixes #13219
The raw-driver guard in
packages/metadata/src/migrations/migrate-project-id-to-environment-id.tsconcatenated its instruction sentence twice, so an operator calling the migration with a driver that has noraw()read the same remedy twice in one message. A copy-paste artifact rather than intent: the siblingmigrateEnvIdToProjectIdcarries the correct single-sentence form of the identical guard. Cosmetic and operator-facing only — the guard fires on exactly the same condition, the remedy it names is unchanged, and nothing parses the message.Branch cut from
maine452ad542, at-or-after the landing of PR #13220 that the triage sequencing constraint was waiting on. That PR moved the guard from:58to:121; the line numbers here were re-derived at the branch head, not taken from the card.The change
One deleted line in the source. The full source diff:
if (typeof driverAny.raw !== 'function') { throw new Error( - 'migrateProjectIdToEnvironmentId: driver must expose a .raw(sql, bindings?) method. ' + 'migrateProjectIdToEnvironmentId: driver must expose a .raw(sql, bindings?) method. ' + 'SqlDriver (better-sqlite3/knex) supports this; cloud-side TursoDriver also conforms.' );The assembled message, before and after
Captured at runtime by invoking the guard, printed through
JSON.stringifyso the trailing space is visible rather than inferred:Before — 250 characters, instruction sentence twice:
After — 167 characters, instruction sentence once:
250 minus 167 is 83, exactly the deleted sentence plus its trailing space. The space that separates the two surviving sentences lives inside the string literal (
method. ' +), so deleting the whole duplicated line preserves it — the assembled text still readsmethod. SqlDriver, notmethod.SqlDriver. That run-together form is this card's own defect inverted, and it is pinned below.The test
The package already had a refusal case,
still refuses a driver without .raw(), added by #13220. Its assertion wasrejects.toThrow(/must expose a \.raw\(sql, bindings\?\) method/)— a substring match, which cannot see a second copy and so passed both before and after the fix. That case is extended in place (no second test file) to pin the properties of the assembled message rather than a full-string copy of today's wording:method.SqlDriverwhile correctly not matching the.raw(in the text itself;Reverse verification, run test-first before the source fix, with the dependency closure built:
The observed direction is a plain red on the new assertion only; the eight pre-existing cases in the file stayed green, confirming the new case is what moved.
Verification
All of the following re-run after the final commit, at
f2ff3b77d.check:query-options-erasurereportsbaseline key set verified against e452ad5: no files added— this extends an existing test file rather than adding one, so its test-surface ceiling does not move.Typecheck, and why it is a measurement and not a silence.
@objectstack/metadatahas notypecheckscript, so tsc was run directly over the package program:tsc -p packages/metadata/tsconfig.json --noEmit. That tsconfig includessrc/**/*and excludes onlynode_modules/dist, so test files are inside the program — confirmed with--listFiles, which matches both edited files (count 2). The run reports 89 errors, zero of them in either edited file; all 89 are pre-existing debt in other files of this package (metadata.test.ts,register-notifies-watchers.test.tsand others). 89 is exactly the frozen count recorded for@objectstack/metadatain theTEST_DEBTledger inscripts/check-type-check-coverage.mjs, so the ratchet quantity is unmoved.The gate family was derived mechanically from the real change set with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, not from a recalled list. Two members of that family returned exit 3, PREREQUISITE NOT MET, which is NOT MEASURED and not a red:scripts/check-test-completeness.mjsgrades a savedturbo run testlog that only CI produces, andscripts/pm/check-half-states.mjsneeds a real GitHub credential this container does not carry.check:type-check-debt --re-measureandcheck:dual-build-cjs-loadsneed the whole workspace built and are left to CI; the quantity the first of them ratchets is the 89 measured directly above.Scope
Card scope only, one deleted line plus the test that pins it. The sibling
migrate-env-id-to-project-id.tsis untouched — it is already correct, and #13220 recorded a deliberate reason it is not gated the same way. Changeset added, gradedpatch: an operator-facing message fix, nothing structural. No release-notes file touched. No out-of-scope findings.Generated by Claude Code
Generated by Claude Code