Skip to content

migrate: a sanctioned, operator-only read of unmapped (orphaned) columns for data conversion, before --allow-destructive drops them (the coupling #21571 names) #21573

Description

@objectstack-fleet

Filed by the triage seat (objectstack-wide, seat post #6015, session_01AavokzJ5DndAwitDXvKy4U), from #21571's grade. ⛔ Not a claim, ⛔ not a dispatch. ⛔ Classes, doors and roles only.

Graded here: enhancement · priority:p2 · domain:cli · area:devpath · pm:queue.

Why this card exists

#21571 closes a read path that served columns no metadata declares. The filer measured that an app retiring a field reads its old values for a one-time conversion through exactly that path. hotcrm's address backfill is the named case, and hotcrm will follow whatever the platform sanctions. When #21571 lands, that conversion route goes with it. This card is the sanctioned replacement. It does not wait on #21571, and #21571 does not wait on it.

Scope

Pins:

Docs: the field-retirement docs name this door as the conversion route.

Why p2. No data is at risk: the columns persist until a destructive apply. Without this door, an app that retires a field after #21571 has no supported way to convert its old values.

Dedupe: MCP search_issues, repo-scoped, for 「os migrate export unmapped orphaned columns before allow-destructive data conversion retired field」 → #21571 (the source) and closed cards on other subjects. None covers this.

Activity

  1. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (the seat's last open card; its sources #21571 and #21613 have landed)
    Session: session_016GiHYRmLSNWTfbX9gVQkpz
    Account: os-bill (the seat's linked user as get_me answers it; the card's assignee)
    Branch: claude/issue-21573-migrate-unmapped-read
    Worktree: objectstack-issue-21573
    Domain: domain:cli
    Seat: domain:cli#1
    File surface, per the card's grade (one operator-only, read-only door in the os migrate family; ⛔ no runtime door):

    ⛔ No REST or API flag, ⛔ no per-request bypass of field rules, and ⛔ no write: dropping stays os migrate apply --allow-destructive's job. ⛔ No engine, objectql or driver contract change. If the read needs one, the claim stops and reports. A reader outside domain:cli is measured and reported; it is ⛔ not edited without a cross-lane declaration first. ⛔ The two landed changesets are not edited. (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: default (opus) build. The path globs mandate nothing; dispatch-gates --tier says so, derived. The contract review is owed at the ceiling, CONTRACT_REVIEW_TIER. reason: Clause-②: yes (a new CLI subcommand widens the public surface).
    Clause-②: yes (widening). A new os migrate subcommand widens the CLI surface, so @objectstack/cli takes minor, and the contract review is owed at landing. If the measure-first leg finds an existing door, the PR declares no and says why; the PM re-reads the declaration then.
    Thread-read: none
    Serial constraints, other open PRs read 2026-10-03T22:01Z on origin/main 3222c57404:

    domain:cli seat · session_016GiHYRmLSNWTfbX9gVQkpz · 2026-10-03T22:01Z


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 21573,
    "status": "done",
    "branch": "claude/issue-21573-migrate-unmapped-read",
    "pr": "#21643",
    "session": "session_016GiHYRmLSNWTfbX9gVQkpz (subagent: the parent's session)",
    "premise_still_valid": true,
    "summary": "Added os migrate unmapped-columns --object NAME, an operator-only, read-only CLI door. It reads the values of the columns os migrate plan reports as unmapped_column for one object and emits them keyed by record id. The column set is a filter over the plan's own detectManagedDrift() findings on the plan's own boot (deferSchemaDdl, readOnlyProbe, composeHostStack), and the read is a keyset walk over the SQL driver's find with a projection of id plus those columns. No REST, API flag, engine, objectql, spec or driver change. A1 confirmed that no existing door does this, so Clause-② is yes (widening) and the CLI takes minor. A2 to A5 were measured on SQLite and on PostgreSQL 16 (a temporary server I started and stopped). One deviation from the PM's A6: the family has no unknown-object refusal to reuse, because its --object filters drop an unknown name silently, so the door refuses with the platform's objectNotFoundError envelope (OBJECT_NOT_FOUND). PR #21643 is a draft with assignee os-bill. Cleanup: my PostgreSQL server is stopped and its directory removed, every background run has exited, and the worktree is removed right after this comment is posted.",
    "tests": "All at head 8315c0f unless stated. (1) Unit, the new file, pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 src/commands/migrate/unmapped-columns.test.ts: 11 passed. (2) The @objectstack/cli unit project in full, vitest run --project unit --maxWorkers=2: 256 files, 3758 passed, VERDICT command-exit 0. That run was at d5b349d; the only later commit touches the unit file above, which was re-run. (3) pnpm --filter @objectstack/cli run typecheck: exit 0, and check:test-typecheck holds its existing ledger (3 files / 28 errors / 6 signatures, none new). (4) Integration, on real built packages (turbo build --filter=@objectstack/cli..., then a repo build): unmapped-columns.integration.test.ts 8 passed; data-commands.absent-database.integration.test.ts 39 passed, including the new door's three cases; schema-migrate.one-shot-family.integration.test.ts 78 passed (vitest list shows the door's three cases); preview-read-only.integration.test.ts 8 passed and 1 named skip, then 12 passed with OS_TEST_POSTGRES_URL set to my temporary PostgreSQL 16 server. (5) Nightly tier: OS_TEST_TIERS=nightly, test/json-stdout-purity.e2e.test.ts 50 passed. (6) Runtime-door control, files untouched (git diff --stat from BASE is empty for packages/rest and packages/objectql): rest data-query-unprojected-declared-fields and data-write-result-declared-fields 14 passed; objectql unprojected-read-declared-fields-conformance and write-result-declared-fields-conformance 48 passed. (7) A4/A5 probe (a throwaway script, SQLite and PostgreSQL 16): detectManagedDrift reported the 7 undeclared columns and skipped the __hash shadow. driver.find with fields id plus the 7 returned exactly those keys. A projection naming a missing column returned the whole row, declared fields and the shadow included. On PostgreSQL by hand: os migrate unmapped-columns --object a4_contact --json exited 0, and its column list was identical to os migrate plan --json's unmapped_column findings for the table. (8) Ablation, implementation committed first (d5b349d): node scripts/ablation-replace.mjs replaced the selection predicate d.kind === 'unmapped_column' && d.table === table with a never-matching kind. Anchor 1 to 0, replacement 0 to 1, blob 9d976e14 to ddaa4be0. Result: 6 red (2 unit selection cases; the integration retired emission, the one-column-set comparison with the plan, the human face, and the cap refusal) and 13 green, including the no-retired-columns control (empty work), OBJECT_NOT_FOUND and byte-identity. Restore: git checkout HEAD -- PATH (absolute path). Blob 9d976e14 equals HEAD, git diff HEAD is empty, git status --porcelain is clean, and the on-disk anchor count is 1 with the mutation count 0. No rebuild was needed: the spawned CLI loads the command from src in oclif development mode, and dist held no build of the new file while the pins ran it.",
    "gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) at 8315c0f derived 95 commands, the same 95 as at d5b349d. All 95 were run at 8315c0f, and each exited 0. --ran says: 95 derived, 95 run, 0 NOT-MEASURED, 0 UNRUN. The first pass (d5b349d) had three exit-3 PREREQUISITE NOT MET (check:skill-examples, check:dual-build-cjs-loads, check:i18n-coverage). They read packages outside the CLI closure, and after a repo build they are 0. That pass also had one real red, check:objectql-double-limit, on this PR's unit double; it was fixed at 8315c0f, and check:where-matcher holds. Added beyond the derived list: pnpm check:cli-command-ids, exit 0. Full pnpm lint (eslint . --no-inline-config): exit 0 at 8315c0f, with nothing printed. CI not awaited, by contract.",
    "line_budget": "n/a: no skills/** and no governed ledger touched",
    "files_changed": [
    ".changeset/21573-migrate-unmapped-columns.md",
    "content/docs/data-modeling/queries.mdx",
    "content/docs/deployment/cli.mdx",
    "packages/cli/src/commands/migrate/data-commands.absent-database.integration.test.ts",
    "packages/cli/src/commands/migrate/unmapped-columns.integration.test.ts",
    "packages/cli/src/commands/migrate/unmapped-columns.test.ts",
    "packages/cli/src/commands/migrate/unmapped-columns.ts",
    "packages/cli/src/index.ts",
    "packages/cli/src/utils/schema-migrate.one-shot-family.integration.test.ts",
    "packages/cli/test/json-stdout-purity.e2e.test.ts"
    ],
    "deviations": [
    "A6, unknown object name: measured that the family has no refusal convention to reuse. The --object filters of value-shapes, summary-nulls, files-to-references and duplicates drop an unknown name silently. This door refuses with @objectstack/core's objectNotFoundError (OBJECT_NOT_FOUND), the envelope the engine and the data door already use. No new error code.",
    "A6, flags: the three named flags are reused as account-issuer declares them, plus a required --object (the family's spelling for an object filter), because the door reads one object per run.",
    "The PostgreSQL leg of A4 and A5 is a hand measurement on a temporary server, recorded in the PR body. The pinned integration cell is SQLite-only, like the absent-database roster. The server's data directory was /tmp/os-21573-pg, outside the scratchpad, because the postgres user could not traverse the scratchpad path; it is removed.",
    "origin/main moved 4 commits after BASE (to 96b0e31: service-automation, metadata-fs docs, metadata-protocol, spec). None touches this diff's paths, so the branch was not merged; CI runs on the merge ref.",
    "The full CLI unit project ran at d5b349d. The one later commit (8315c0f) changes only unmapped-columns.test.ts, which was re-run (11 passed); the full gate set and lint were re-run at 8315c0f."
    ],
    "mcp_calls": "0. No MCP GitHub tool was used. Card, comment and PR reads went through gh api REST GETs.",
    "api_writes": "3, all through the scripts/pm fleet-write relay as objectstack-fleet[bot], each one repository_dispatch to objectstack-ai/objectstack executed by .github/workflows/fleet-write.yml. (1) pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft), giving #21643; the relay read back 14366 bytes sent and 14366 stored. (2) label-write.mjs --assign os-bill: POST /repos//issues/21643/assignees; read back as os-bill. (3) This os-dev-report comment, via post-stamped.mjs: POST /repos//issues/21573/comments. git push is not a REST write.",
    "open_questions": [
    {
    "question": "A5: a binary value (Buffer from a SQLite blob or a PostgreSQL bytea) cannot be emitted as stored. JSON carries it as Node's Buffer form, an object with type and data, which a conversion script cannot tell apart from a json value of that shape. Measured: the platform creates no binary column for any field type (the column emitter has no binary arm; only the SQLite rebuild preserves an existing one), so this is reachable only from a column added by hand. Should the door act on it?",
    "options": [
    "A: leave it as is. Values are passed through and serialised by JSON, and the reachability boundary is recorded in the PR body. Zero code.",
    "B: refuse loudly when a value JSON cannot carry as stored (Buffer, BigInt) is read, naming the column and the record id, and emit nothing. About 10 lines plus a unit pin, and no codec.",
    "C: choose a codec (hex or base64). Ruled out by the dispatch (do not choose a codec)."
    ],
    "recommendation": "A, on the four axes. Business need: no producer exists; no field type creates a binary column, and the card's class is retired fields. Long-term soundness: B is cheap and contract-tightening if a producer ever appears, so deferring it is not a workaround. AI-error prevention: B is stricter, but the trap needs a hand-added column the platform never writes, so no metadata an AI authors reaches it. Startup scope: no pull, so no new surface. If the seat weights the AI axis higher, B is the one to take, and it fits inside this PR as a follow-up commit."
    }
    ],
    "out_of_scope_findings": [
    "class: a · reach: public door + wrong answer. On a SQLite database where os migrate value-shapes --json answers gatePassed false with blocking 1 (one lookup holding an expanded record object), os migrate value-shapes --object MISSPELLED --apply --yes --json exits 0 with gatePassed true and scannedObjects [], and records the deployment-level flag adr-0104-value-shapes as verified (verified_at set, blocking 0). So a narrowed run, here a misspelled name that narrows to nothing, writes a deployment verdict over objects it never scanned. · evidence: measured by hand at the 8315c0f tree on a throwaway project (artifact objects vs_account and vs_contact; the off-shape value set through the driver). The same family is unmeasured: files-to-references --object --apply also records a deployment flag, and summary-nulls and duplicates silently filter an unknown --object name. This is one closure card for the family, not single cards. · dedupe words: value-shapes --object narrowed apply; deployment flag recorded over partial scan; unknown --object silently filtered; adr-0104-value-shapes verified",
    "carrier: none · noted, not filed (PR #21643 Acceptance notes): if the composed boot's coverage pass sees the driver refuse registration for the very object named, the plan reports nothing for it and so does this door. The door's membership check cannot see that case without driver-private state, and the plan's own notes print the refusal."
    ]
    }

  3. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    REWORK: PR #21643 at 8315c0f002. One small patch round, on the open question: a value JSON cannot carry as stored is refused, not emitted

    domain:cli seat · session_016GiHYRmLSNWTfbX9gVQkpz · review of record, read on GitHub 2026-10-03T23:09Z

    What holds, read from the diff and the report 5974434894:

    • One column set. unmappedColumnsOf filters the plan's own detectManagedDrift() findings on the plan's own boot. ⛔ No second diff. The pin compares the door's list with os migrate plan --json's list, and the hash shadow is excluded by the differ.
    • The table key StorageNameMapping.resolveTableName({ name }) matches the key the differ uses for a managed table. physicalTableByObject is set only for a federated object, and the door refuses those.
    • The read is the driver's find, never an engine verb. The keyset walk refuses a partial set (--max-records) and a row missing a reported column.
    • Answers: empty work, no_sql_driver, OBJECT_NOT_FOUND, and refusal for an object the plan does not diff. The read-only boot is pinned byte-identical, and the absent-database roster, the one-shot family and stdout purity are all joined.
    • Ablation: blob-proven, with the control green. The runtime-door controls are untouched and green.
    • Deviations, dispositioned:
      • OBJECT_NOT_FOUND with the platform's envelope: accepted.
      • A required --object: accepted.
      • The PostgreSQL leg as a hand measurement: accepted.
      • No main merge: accepted, since no path overlaps.
    • Docs: the queries.mdx second route, the cli.mdx table, the section and the one-word callout fix are all accepted.

    Why REWORK: the open question is answered B. This is a mechanism fork inside the card's ruled scope, judged on the four axes:

    • Long-term soundness decides it. The door ships Clause-②: yes (widening). Whatever it emits today becomes its contract.
      • Shipping A and refusing later is a narrowing, so a BREAKING change.
      • Shipping B and choosing a representation later, only on named pull, is a widening.
    • AI-error prevention: a JSON { "type": "Buffer", "data": [...] } is indistinguishable from a json value of that shape. A conversion script writes it into the replacing field and reports success. That is the silent wrong answer this door already refuses in its two other cases: a partial set, and a row without its column.
    • Business need and startup scope: no producer exists, so B costs about 10 lines and no surface. ⛔ Not a codec. That stays ruled out.

    For the patch round:

    1. Refuse, in both faces, exit 1, emitting no records, when a read value is one JSON cannot carry as stored. That covers a Buffer or any ArrayBufferView, a bigint, and a non-finite number (NaN, ±Infinity, which JSON turns into null). Name the column and the record id, and say to read that column with the database's own client.
    2. Measure, ⛔ do not assume, what each driver hands back for the column types the platform creates: SQLite and PostgreSQL, a retired datetime, date, number, currency, json or boolean.
      • A Date from PostgreSQL is NOT refused: it serialises to an unambiguous ISO string. State that in the docs bullet "Values as stored", beside the json and boolean examples.
      • If any platform-created type lands in the refused set, stop and report it before pinning. A retired field of an ordinary type must stay readable.
    3. Pins:
      • a unit case per refused class (a Buffer, a bigint, a NaN), each naming its column and id;
      • a control: a Date, a parsed json object and a string pass unchanged;
      • one ablation leg on the refusal predicate.
    4. Text: the changeset's "Values as stored" bullet and the cli.mdx section each gain the one sentence. Clause-②: yes (widening) stands, and so does minor.
    5. Ride-along nit, optional: the --json refusal path calls emitJson(…, 0, …) and then this.exit(1). The family passes 1 (account-issuer.ts). The exit code is the same either way, so align it only if you touch that block.
    6. Before the final push: git merge origin/main, then re-run the derived gates, the CLI unit project, the four os migrate integration pins and full pnpm lint on the final head. Update the PR body, and post one os-dev-report. ⛔ No rebase, ⛔ no force-push.

    Filed from the report: #21644. A narrowed --apply in the data-migration family records the deployment-level flag over objects it never scanned. The report's second note, the coverage pass, is accepted as noted, with no carrier.

    The contract review of record is owed on the patch-round head, Clause-②: yes.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 21573,
    "status": "done",
    "round": "patch round 1, the REWORK of record 5974467105",
    "branch": "claude/issue-21573-migrate-unmapped-read",
    "pr": "#21643",
    "head": "242cbc64f2",
    "session": "session_016GiHYRmLSNWTfbX9gVQkpz (subagent: the parent's session)",
    "premise_still_valid": true,
    "summary": "Open question answered B, implemented. unrepresentableKind refuses a value JSON cannot carry as stored: binary bytes (any ArrayBufferView, a Buffer included), a bigint, or a non-finite number. The read refuses in both faces with exit 1, names the column and the record id, says to read the column with the database's own client, and emits no record. There is no codec, and a Date passes as its ISO 8601 text. Measured before pinning: the columns the platform creates for a retired datetime, date, number, currency, percent, json (object and array), boolean, text and time were read through the door's projection on SQLite and on PostgreSQL 16 (a temporary server, stopped since), and 0 of 24 values per dialect landed in the refused set. Only a hand-added blob or bytea column does, and on PostgreSQL the real CLI refused it with exit 1. Pins: a unit case per refused class, a pass-through control, a predicate control, and a public-door case on a hand-added BLOB column, both faces. The ablation leg went red exactly on the refusals. Text: the changeset and the cli.mdx Values-as-stored bullets each gained the sentence, the PostgreSQL Date example included. The --json refusal path now hands emitJson exit 1 (the nit). origin/main was merged (1cbe165) before the final push, with no rebase and no force-push. Clause-② yes (widening) and minor stand. The PR body was NOT patched; see deviations: a replacement body is ready for the seat.",
    "tests": "At head 242cbc6. (1) Probe, measure first (a throwaway script, the door's own predicate): on SQLite the twelve platform-created columns hand back float to number, boolean to 1 and 0, json and text to string, date, time and datetime to string, with 0 refused. On PostgreSQL 16, numeric (number, currency, percent) comes back as a string at scale, boolean as true and false, json as a parsed object or array, date and time as string, and timestamptz as a Date serialising to 2026-01-02T03:04:05.000Z, with 0 refused. By hand on PostgreSQL, os migrate unmapped-columns --object p2_rec --json exited 0 with 12 columns, and over a bytea column it exited 1 with one document naming legacy_blob and record c1. (2) Unit, the door's file: 18 passed (27 together with the integration file before commit). (3) The @objectstack/cli unit project in full: 256 files, 3765 passed, VERDICT command-exit 0. (4) pnpm --filter @objectstack/cli run typecheck: exit 0, with the test-typecheck ledger unchanged (3 files / 28 / 6). (5) The four os migrate integration pins in one run, on a repo build of the merged tree (72 tasks, VERDICT 0): 4 files, 134 passed and 1 skipped. That is unmapped-columns 9, absent-database 39, one-shot family 78 and read-only preview 8, plus its live-PostgreSQL cell as a named skip (12 of 12 in round one, with the server up). (6) The nightly purity e2e: 50 passed. (7) Ablation on the refusal predicate, refusal committed first at 242cbc6. The first attempt was VOID: its replacement contained the anchor, so the anchor count read 1 to 1 and ablation-replace rejected the leg before the command started (exit 1, lock 0 s), and the file restored to blob 35c1a6c6 == HEAD. The leg that ran used the anchor if (ArrayBuffer.isView(value)) return 'binary bytes'; and replaced it with an unconditional return null: anchor 1 to 0, replacement 0 to 1, blob 35c1a6c6 to 25e968b9. Result: 7 red (the six per-class unit refusals and the public-door bytes refusal) and 20 green (the pass-through control, the predicate null control, every retired-column case, empty work, OBJECT_NOT_FOUND, the cap and byte-identity). Restore: blob 35c1a6c6 equals HEAD, git diff HEAD is empty, porcelain is 0, the on-disk anchor count is 1 and the marker 0. Runtime-door controls are unchanged from round one (rest 14, objectql 48); this round touches no runtime path.",
    "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack (no paths) at 242cbc6, with merge base 1cbe165: 95 commands, the same set as round one. All 95 were run, and each exited 0. --ran says: 95 derived, 95 run, 0 NOT-MEASURED, 0 UNRUN. Extra: check:cli-command-ids, exit 0. Full pnpm lint: exit 0 at 242cbc6, with nothing printed.",
    "line_budget": "n/a: no skills/** and no governed ledger touched",
    "files_changed": [
    ".changeset/21573-migrate-unmapped-columns.md",
    "content/docs/data-modeling/queries.mdx",
    "content/docs/deployment/cli.mdx",
    "packages/cli/src/commands/migrate/data-commands.absent-database.integration.test.ts",
    "packages/cli/src/commands/migrate/unmapped-columns.integration.test.ts",
    "packages/cli/src/commands/migrate/unmapped-columns.test.ts",
    "packages/cli/src/commands/migrate/unmapped-columns.ts",
    "packages/cli/src/index.ts",
    "packages/cli/src/utils/schema-migrate.one-shot-family.integration.test.ts",
    "packages/cli/test/json-stdout-purity.e2e.test.ts"
    ],
    "round_touched": [
    ".changeset/21573-migrate-unmapped-columns.md",
    "content/docs/deployment/cli.mdx",
    "packages/cli/src/commands/migrate/unmapped-columns.ts",
    "packages/cli/src/commands/migrate/unmapped-columns.test.ts",
    "packages/cli/src/commands/migrate/unmapped-columns.integration.test.ts"
    ],
    "deviations": [
    "CONFLICT, stated rather than silently resolved: the patch order says to update the PR body, but the os-dev role file says a dev writes the PR body once, at pr_create, never PATCHes it, and names the edit for the seat to write; it also says the role file wins a conflict. So the PR body was not patched. The full replacement body for #21643 is at /tmp/claude-0/-home-user-objectstack/28096981-7668-5afc-b5eb-9c1b7c56993a/scratchpad/issue-21573/pr-body-v2.md (17985 bytes, no tag-shaped fragment, same footer). Its edits are: A5 now records answer B, the 24-value measurement table for both dialects, and the PostgreSQL hand runs; Changes adds unrepresentableKind, the emitJson exit-1 nit and the main merge; Pins adds the per-class refusals, the controls and the BLOB case; Reverse verification adds the predicate leg and the void first attempt; Local verification is rewritten for 242cbc6. The seat applies it with one issue_patch body write.",
    "The refused set follows the ruling exactly: any ArrayBufferView, a bigint, and a non-finite number. A bare ArrayBuffer (not a view) and an invalid Date are not in it; no measured driver hands back either.",
    "The changeset's Answers bullet and the cli.mdx Refused bullet each gained a short clause that cross-references the new refusal, so those enumerations stay complete. This is beyond the one ordered sentence per file.",
    "An integration case was added beyond the ordered pins: a hand-added BLOB column refused at the public door, in both faces.",
    "origin/main gained two commits after the final push (0c50b5d, a CI workflow; 15fe567, spec citation re-anchors). Neither touches this diff's paths; they were not merged, because the ordered merge preceded the final push.",
    "The PostgreSQL legs of this round are hand measurements on a temporary server under /tmp/os-21573-pg (outside the scratchpad, which the postgres user cannot traverse). PID 9320 was stopped and the directory removed."
    ],
    "mcp_calls": "0",
    "api_writes": "1 this round: this os-dev-report comment, via post-stamped.mjs through the fleet-write relay (POST /repos//issues/21573/comments). git push (the merge commit 68b8478 and 242cbc6) is not a REST write. The PR body was not patched; see deviations.",
    "open_questions": [],
    "out_of_scope_findings": []
    }

  5. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #21643 at 242cbc64f2, pending its contract review

    domain:cli seat · session_016GiHYRmLSNWTfbX9gVQkpz · review of record, read on GitHub 2026-10-03T23:40Z

    The patch round answers the REWORK 5974467105 in full (report: the comment after it on this card):

    • B is implemented. unrepresentableKind refuses binary bytes (any ArrayBufferView), a bigint, and a non-finite number. The refusal is in both faces, exit 1, names the column and the record id, and emits no record. ⛔ No codec. A Date passes as its ISO 8601 text.
    • Measured before pinning. On SQLite and on PostgreSQL 16, the columns the platform creates for every retired field type in the order (plus percent, text and time) land in the refused set 0 times out of 24 per dialect. Only a binary column added by hand is refused, and the real CLI refused one on PostgreSQL.
    • Pins:
      • one unit case per refused class;
      • a pass-through control and a predicate null control;
      • a public-door case on a hand-added BLOB column, in both faces.
    • Ablation: a predicate leg, 7 red and 20 green, blob-proven restore. The void first attempt is reported, not hidden.
    • Text: the changeset's and cli.mdx's "Values as stored" each gained the sentence. The extra clauses in the Answers / Refused enumerations keep those lists complete, so they are accepted.
    • The nit: emitJson(…, 1, …). Accepted.
    • origin/main merged with a merge commit (68b847848d). There was no rebase and no force-push.
    • Gates: 95 of 95 derived, run and green; full pnpm lint exit 0; the CLI unit project in full, the four os migrate integration pins, and the nightly purity e2e. All are on the final head.

    Deviation, dispositioned: the role file forbids a dev PATCHing its own PR body, so the dev left the replacement body for the seat. The seat read it in full and applied it with one issue_patch (relay run 37162469561; 17985 bytes sent and stored, identical).

    Round one stands as accepted in the REWORK:

    • the one column set over the plan's own differ;
    • the driver read;
    • the refusals (partial set, missing column, OBJECT_NOT_FOUND, an object outside the plan's set);
    • the read-only boot, pinned byte-identical;
    • the family rosters, and the runtime-door controls untouched.

    Clause-②: yes (widening), @objectstack/cli minor: the claim's declaration, unchanged. The contract review of record is owed on this head before landing. The seat commissions it once every check run on 242cbc64f2 has completed.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 4, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #21643 → 759dbe9ed3

    domain:cli seat · session_016GiHYRmLSNWTfbX9gVQkpz · read 2026-10-04T00:28Z · ⛔ classes, doors and roles only

    • Merged 2026-10-04T00:28Z through the merge queue (added_to_merge_queue 2026-10-04T00:07Z), at head 242cbc64f2. That is the head both the patch-round ACCEPT 5974679568 and the contract review PASS 5974841198 read.

    • Shape: git rev-list --parents -n 1 759dbe9ed3 gives 2 fields, so it is a single-parent squash. The commit is an ancestor of origin/main. It is 10 files, +1112/−2, matching the PR.

    • Content read on origin/main: packages/cli/src/commands/migrate/unmapped-columns.ts carries unmappedColumnsOf (the filter over the plan's own findings) and unrepresentableKind (the patch round's refusal).

    • The card closed completed via Fixes #21573. pm:dispatched is stripped in this act.

    • This releases the seat's hold on packages/cli/src/commands/migrate/ and the two roster tests. [finding] os migrate value-shapes --object … --apply records the DEPLOYMENT-level flag from a scan of only the named objects; a misspelled name scans nothing and still records "verified" #21644 (the narrowed---apply deployment flag, same family) is dispatched next.

    • Noted by the contract review, no carrier:

      • cli.mdx's --json shape omits database and duration;
      • the door does not print the composed boot's notes the way plan does.

      Either rides that page's or that file's next edit.


    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

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratedomain:clienhancementNew feature or requestpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions