Skip to content

storage create-table --if-not-exists: skipped envelope returns requested schema, not actual existing table schema #349

Description

@ottomansky

Context

Surfaced by the iteration-3 convergence reviewer and the /kbagent:review subagent on PR #348 ("feat(0.47.0): fresh-CREATE writeback + semantic-layer reads + sync/storage ergonomics").

When a caller invokes `kbagent storage create-table --if-not-exists` and the table already exists at the expected id, `services/storage_service.py:782-815` returns:

```json
{
"project_alias": "...",
"table_id": "in.c-foo.bar",
"name": "bar",
"bucket_id": "in.c-foo",
"primary_key": [...],
"columns": [...],
"auto_created_bucket": false,
"legacy_branch_storage": false,
"action": "skipped",
"skip_reason": "table already exists"
}
```

The `primary_key` and `columns` fields reflect the USER'S REQUEST (the args passed to the create call), not the existing table's actual schema. A caller that trusts the envelope to discover the real shape would see the wrong values whenever the existing table has a different PK or column set than the caller asked for.

Repro

```bash
kbagent storage create-table --project P --bucket-id in.c-test --name t1 --column id:INTEGER --column name:STRING --primary-key id

returns action:created, columns:[id,name], primary_key:[id]

kbagent storage create-table --project P --bucket-id in.c-test --name t1 --column id:INTEGER --column name:STRING --column DIFFERENT:STRING --primary-key DIFFERENT --if-not-exists

returns action:skipped, columns:[id,name,DIFFERENT], primary_key:[DIFFERENT]

^ but the actual remote table still has columns:[id,name] and primary_key:[id]

```

Fix

When `if_not_exists=True` and we've confirmed the existing table is real via `client.get_table_detail(target_id, branch_id=branch_id)`, return the `columns` / `primary_key` / `name` from that response payload, not from the user's args. The lookup is already happening (`storage_service.py:807`); the response just needs to be re-shaped.

Suggested envelope:
```json
{
"project_alias": "...",
"table_id": "in.c-foo.bar",
"name": "",
"bucket_id": "in.c-foo",
"primary_key": [/* from get_table_detail /],
"columns": [/
from get_table_detail /],
"auto_created_bucket": false,
"legacy_branch_storage": false,
"action": "skipped",
"skip_reason": "table already exists",
"requested_primary_key": [/
user's args /],
"requested_columns": [/
user's args */]
}
```

Optionally surface a warning when the requested and actual shapes diverge — a caller may want to know they hit an existing-but-different table rather than silently moving on.

Why deferred

PR #348 introduces the `--if-not-exists` flag for the primary use case (FIIA `scaffold_storage.py` parallel-worker pattern) where the table is being CREATED for the first time anyway. The mis-reported schema only matters when a caller hits a pre-existing table with a different shape — a real but secondary scenario that's worth its own focused PR rather than expanding the original surface.

Severity

Non-blocking on PR #348 (pre-existing scope of the `--if-not-exists` design surface, not a regression introduced by it). Suitable for a follow-up PR after #348 merges.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions