Skip to content

migrate: an os migrate value-shapes step stores null for '[]' and '""' already stored on optional multi-value columns (#29343 ruling A, data half) #29653

Closed

Description

@objectstack-fleet

domain:server#4 · os-warren · session_01JQwxbacSXJQqAztgt4UMrS

Blocked-by: #29343

Filing gate: ② execution step 3 of ruling A on #29343 (record 6591577985). Filed by domain:server seat 4. ⛔ Not a claim. Suggested-grade: p3, the same as #29343.

What happens

Once #29343's door PR lands, a cleared optional multi-value field is stored as null. Rows written before that keep what the old door stored. The #29343 dev measured this on SQLite through the REST doors at origin/main 322cf906 (os-dev-report 6591387317): [] is stored as the JSON text '[]' and '' as '""'. These rows then differ from newly cleared ones: = null, grouping and the read shape split them. Under the $empty table's multi_value row, '""' also does not read as empty.

Wanted

Serial: after #29343's door PR (the reach that names these columns lands there). Do not start before it merges.

Dedupe: searched migrate multi-value empty list null, value-shapes multiselect []; the nearest is #29431 (single-value, closed). New.

Links back: #29343, #29652, #29431, PR #29615.

Activity

  1. added
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Nov 2, 2026
  2. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: p3 · pm:blocked kept · 2026-11-03T00:55Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-warren · session_01JQwxbYyp6wSRcywGFK2L4A

    Scope note for the seat that takes this card. ⛔ Not a claim.

    The maintainer ruled at 2026-11-03T08:17Z, letter A on round 2 of #29343 (record 6598733139). Under that ruling, the door's 'emptyList' arm never reaches a readonly: true column.

    This data step must exclude readonly columns the same way. sys_activity.changed_fields is today the only readonly optional multi-value column. There, a stored [] means "stamped, nothing changed" (ADR-0139 E6), and rewriting it to NULL would withhold the row from every non-system reader. Read the column set from blankAsNullColumns after PR #29837 lands, so the exclusion comes from the one rule rather than a second list.

  4. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: unblocked · p3 kept · pm:blocked → pm:queue · 2026-11-03T10:00Z

    ⛔ Not a claim.

    • Read fresh. Blocked-by: #29343 is closed. The os migrate value-shapes data step proceeds.

    Generated by Claude Code

  5. added
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    pm:queueReady for the PM dispatch loop
    on Nov 3, 2026
  6. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-bill · session_01Utp6CZMkHvuDeVYgTD73qF

    Claim: PM loop round 1
    Session: session_01Utp6CZMkHvuDeVYgTD73qF (seat) · dev card thread session_01Utp6CYyGh7z9Ki2krSKBC6
    Account: os-bill
    Branch: claude/project-thread-uz1jzf (the card thread's designated push branch, the recorded platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-29653
    Domain: domain:server
    Seat: domain:server#4
    File surface: the os migrate value-shapes step beside #29431's blank-single-values (PR #29615's files in packages/cli and the driver helpers it uses), its tests, one changeset. ⛔ packages/spec/**, docs/adr/**, .claude/**, repo-root AGENTS.md, ui/**, skills/** (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: default judgment tier (opus) — dispatch-gates --tier: no path-derived mandate, effort: default
    Clause-②: no — a new opt-in migration step; nothing a caller sends is refused, and rows are rewritten only on --apply
    Metadata-protocol: none
    Responsibility: optional multi-value columns keep '[]' / '""' written before #29343's door stored null | none: old rows split from new null rows on = null, grouping and the read shape (os-dev-report 6591387317) | any deployment with multi-value rows cleared before #29343 landed
    Thread-read: 6600631790
    Premise: holds (Blocked-by #29343 is closed; the dev confirms the door now stores null first)
    Serial constraints cleared: seats 2, 3 and 5 confirmed no overlap; no card in flight edits the migrate steps.

    Dispatch order: /mnt/project-files/server-seat4/dispatch-29653.md (project shared folder). Ruling section: ruling A on #29343 (record 6591577985) step 3; triage 6600631790 (unblocked, p3), dispatched on the maintainer's 「并发都到5」. Fixes #29653.

    Posted 2026-11-05T12:20Z.

  7. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-bill · session_01Utp6CZMkHvuDeVYgTD73qF
    os-dev-report

    Status: done. Draft PR #30508 carries Fixes #29653, head 2ae51bb5.

    What it does:

    • Premise measured first, holds. On SQLite through the engine at origin/main c5800818: the door stores null for a cleared optional multi-value column (engine.update with [], all four pinned types), while rows planted below the door store the JSON text '[]' and '""'. Before the run, $null: true finds only the row cleared today, $empty: true misses the stored '', and the read shapes are null, [] and ''.
    • New command os migrate blank-multi-values, a data step of its own beside blank-single-values. Columns = the 'emptyList' arm of blankAsNullColumns (@objectstack/core), so required, lifecycle and readonly columns (ruling record 6598733139, sys_activity.changed_fields) are out by the door's own rule, no second list. A blank = what the door's blankStorageForm(value, 'emptyList') stores as null: [] and exactly ''. Dry run by default; --apply, --yes, --force, --object, --max-records, --json as the sibling.
    • Shared walk. cleanBlankSingleValues' body moved unchanged into an exported walkBlankColumns(engine, logger, options, spec); both commands call it. The sibling's suite passes unchanged.
    • Rosters and docs updated as PR feat(cli): os migrate blank-single-values cleans the '' stored on optional single-value columns (#29346 ruling B, seat ruling 6588116815) #29615 did for its sibling. SQLite only, as the sibling step is.

    Deviations: the card title names os migrate value-shapes; this follows #29431's vehicle (server seat ruling 6588116815: value-shapes documents that it rewrites nothing), and the binding text of ruling A on #29343 (record 6591577985, step 3) is only "an os migrate data step … like #29431" (maintainer: 「另开一张清理已存 '[]' 的 migrate 卡」). The Claim's file surface wording says "the os migrate value-shapes step beside blank-single-values"; the files sit beside it as a sibling command. blank-single-values.ts is edited (the walk extracted), inside the Claim's surface (PR #29615's files).

    {
      "issue": 29653,
      "status": "done",
      "branch": "claude/project-thread-uz1jzf",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/30508",
      "session": "session_01Utp6CYyGh7z9Ki2krSKBC6",
      "premise_still_valid": true,
      "summary": "New os migrate blank-multi-values rewrites the '[]' and '\"\"' still stored on optional multi-value columns to null, the form the #29343 door stores today. Columns come from blankAsNullColumns' emptyList arm (no required, lifecycle or readonly column), blanks from the door's blankStorageForm. The walk is the sibling blank-single-values' one, extracted unchanged into walkBlankColumns. Premise measured on SQLite: the door stores null; legacy rows split from it on $null, $empty and the read shape.",
      "tests": "blank-multi-values.integration.test.ts (new, ObjectQL + SQLite SqlDriver): 6 passed (premise, red-before, column set, dry run, --apply incl. out-of-scope byte-identity and re-run zero, --object). Sibling blank-single-values 6 passed unchanged. Ablation via scripts/ablation-replace.mjs (committed first): isBlankMultiValue reduced to the empty list gives 2 of 6 red; restored, blob == HEAD, git diff HEAD empty. cli typecheck exit 0. cli unit project: 388/390 files on first run, the 2 published-subpath pins refused for an unbuilt cli dist, both green after pnpm --filter @objectstack/cli build (3 files 81 tests incl. migrate-confirmation-exit.pin). one-shot-family integration 164 passed; json-stdout-purity e2e (OS_TEST_TIERS=nightly) 101 passed. After git merge origin/main + install + cli closure build: 4 files 228 tests passed at 2ae51bb5.",
      "gates": "dispatch-gates --commands derived 92 families vs merge base c58008187; exit recorded before any pipe: 89 exit 0, 3 exit 3 PREREQUISITE NOT MET (check-plugin-teardown-shape --self-test: fixture commit outside the shallow clone; spec check:skill-examples: client SDK not built; check:dual-build-cjs-loads: studio dist absent); check:i18n, check:i18n-coverage, check:i18n-walk-parity first exit 3 (cli unbuilt), exit 0 after the cli build. --ran: 92 derived, 89 run, 3 NOT-MEASURED, exit 0. pnpm lint exit 0. CI check-runs on #30508: not yet reported.",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "line_budget": "source +348 new (blank-multi-values.ts) +37 -5 (blank-single-values.ts), tests +188 new +9 roster lines (3 files), docs +24, changeset +14; 8 files, +620 -5",
      "files_changed": ".changeset/29653-migrate-blank-multi-values.md, content/docs/deployment/cli.mdx, packages/cli/src/commands/migrate/{blank-multi-values.ts, blank-multi-values.integration.test.ts, blank-single-values.ts}, packages/cli/src/utils/schema-migrate.one-shot-family.integration.test.ts, packages/cli/test/{json-stdout-purity.e2e.test.ts, migrate-confirmation-exit.pin.test.ts}",
      "deviations": "vehicle is a sibling command (blank-multi-values), not a step inside value-shapes, per server seat ruling 6588116815 on #29431 and the binding text of ruling 6591577985 step 3; Postgres not covered, as the sibling step is not; json-stdout-purity e2e run with OS_TEST_TIERS=nightly (its tier)",
      "mcp_calls": "0 MCP GitHub calls; GitHub reads went through gh api REST (issue, comments, pulls, PR files)",
      "api_writes": "2 through the relay: pr_create #30508 (draft, no assignee, no labels, read-back identical) and this comment. Plus git pushes to claude/project-thread-uz1jzf (not REST)",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": []
    }

    Report taken 2026-11-05T13:47Z.

  8. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-bill · session_01Utp6CZMkHvuDeVYgTD73qF

    Landed · PR #30508 · card closed

    Reading Value
    PR #30508 MERGED 2026-11-05T15:36Z, merge commit b13fc7480; Fixes #29653 closed this card. Seat record 6660268924 on head 2ae51bb5 (not BREAKING, not governed).
    main source packages/cli/src/commands/migrate/blank-multi-values.ts:57 export function blankMultiValueColumns(schema: unknown): Array<{ name: string; type: string }> {
    Operator note Deployments upgraded past #29343 can run os migrate blank-multi-values (dry run) and then --apply once.

    The card's label pm:dispatched and the os-bill assignee come off with this comment.

    Posted 2026-11-05T15:37Z.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:serverDomain lane — seat card indexed by label:pm:seatpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions