Skip to content

feat(AJDA-3460): workspace create --governed - #892

Open
Matovidlo wants to merge 5 commits into
mainfrom
martinvasko-ajda-3460-kbagent-workspace-create-governed
Open

Matovidlo wants to merge 5 commits into
mainfrom
martinvasko-ajda-3460-kbagent-workspace-create-governed

Conversation

@Matovidlo

@Matovidlo Matovidlo commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Why

Row-level security in Query Service is enforced on workspaces created with the Storage login type governed: such a workspace has no user, password or key, and only Query Service can obtain its credentials. Connection accepts that login type on create since keboola/connection#9034, but kbagent workspace create could not ask for it, so there was no way to create one from the CLI or from an agent. Linear: AJDA-3460.

What changes

  • kbagent workspace create --governed creates a Snowflake workspace with loginType: governed and no public key. The result carries governed: true and login_type: governed, never a private key, and an empty password; the human output says that the workspace has no credentials and is read only through Query Service.
  • Snowflake and headless only, checked before any sandbox config is created: --ui and a non-Snowflake backend are both INVALID_ARGUMENT (exit 2), like the same kind of refusal in storage create-table (changed from CONFIG_ERROR after review, before the first release that has the option).
  • The REST route POST /workspaces/{project} takes governed: true (it mirrors the CLI; before this, an unknown body field was ignored and a credentialed workspace was created).
  • governed in the result comes from the login type the API answered, not from the flag.
  • Not in this PR: a governed toggle and a hidden password reveal in the web UI (Workspaces.tsx).
  • governed is deliberately not in QUERY_SERVICE_COMPATIBLE_LOGIN_TYPES yet, so workspace list --qs-compatible does not report it until a query on such a workspace is verified end to end.
  • Documentation surfaces updated with vNEXT tags (no version bump, no changelog entry): CLAUDE.md, kbagent context, docs/cli-manifest.json and the generated _context_commands.py, commands-reference.md, gotchas.md, workspace-workflow.md and keboola-expert.md.

Checks

  • ruff, ruff format --check, ty, skill-check, version-check, version-gate-check, loc-check, check-error-codes, command-sync-check and cli-spec-rules pass; service and CLI tests for the new option pass.
  • E2E test_governed_workspace_has_no_credentials_and_refuses_a_login skips on a stack whose Connection does not accept the login type.
  • Verified by hand on the AWS dev stack with Connection from keboola/connection#9034: workspace create --governed created a workspace with login_type: governed, empty user and password; workspace detail reports governed; workspace password is refused with 400 workspace.resetPasswordNotSupported. A query through Query Service on it is answered 403 storage.workspace.governedOperationNotAllowed until the Query Service ServiceAccount carries the governed scope (keboola/kbc-stacks#27522).

🤖 Generated with Claude Code

`kbagent workspace create --governed` creates a Snowflake workspace with the login type
`governed`: it has no user, password or key, nobody but Query Service can obtain its
credentials, and the row- and column-level policies of the project apply to it.

- send loginType "governed" without a public key; the result carries `governed` and
  `login_type`, never a private key, and an empty password
- Snowflake and headless only: `--ui` and a non-Snowflake backend fail with CONFIG_ERROR
  (exit 5) before any sandbox config is created
- the governed login type is deliberately not in QUERY_SERVICE_COMPATIBLE_LOGIN_TYPES yet,
  so `workspace list --qs-compatible` does not report it
- tests for the service and the CLI, an E2E test that skips on a stack whose Connection
  does not accept the login type yet, and the documentation surfaces (vNEXT tags)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@Matovidlo
Matovidlo requested a balanced review from Copilot October 21, 2026 09:05
@linear-code

linear-code Bot commented Oct 21, 2026

Copy link
Copy Markdown

AJDA-3460

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

… --governed

- `POST /workspaces/{project}` takes `governed: true`; before, the route silently dropped it and
  created a credentialed workspace
- `--governed` with `--ui` is INVALID_ARGUMENT (exit 2), a bad flag combination, raised before any
  API call; a non-Snowflake backend stays CONFIG_ERROR (exit 5)
- the result's `governed` and the human message come from the login type the API answered, not from
  the flag, so a workspace that came back with a login is never reported as governed
- one governed / not governed branch in _create_workspace_direct, no default for its governed
  parameter, typed print helper
- tests: the truth table of governed x backend x ui x read_only as one parametrized test, the REST
  route, _patched_services reuse, the not-governed assertion folded into the success test
- docs: the exit codes, the governed workspace heading, and that `workspace list --qs-compatible`
  does not list it yet

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@Matovidlo
Matovidlo marked this pull request as ready for review October 21, 2026 09:32

@keboola-pr-reviewer-bot keboola-pr-reviewer-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: needs_human (risk 2/5) · profile _default

Clean, additive, well-tested feature, but it edits agent-instruction files, which policy says never auto-approve.

Concerns:

  • CLAUDE.md: Agent-instruction file changed; policy forbids auto-approval (edits appear benign/additive)
  • plugins/kbagent/agents/keboola-expert.md: Agent system prompt edited; needs human eyeball per agent-instruction rule

@soustruh soustruh left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One point on the CLI interface, checked against docs/cli-interface-spec.md. The rest fits the 1.x line: the change is additive (C1), login_type has the same name as in workspace list and workspace detail, and the REST body field has the same name as the flag.

--governed on a backend other than Snowflake should be INVALID_ARGUMENT (exit 2), not CONFIG_ERROR (exit 5). The same kind of refusal in storage create-table (--source-table-id or the partition flags on a project that is not BigQuery) is INVALID_ARGUMENT with exit 2: storage_service.py, storage.py. This PR also uses INVALID_ARGUMENT for --governed with --ui. Please change it before the first release that contains the option, because after the release a change of the error code is a breaking change (C1). The service test, the E2E skip condition (exit_code == 5) and the documents that name CONFIG_ERROR change with it.

…ID_ARGUMENT

The same kind of refusal in `storage create-table` (a BigQuery-only option on a
project that is not BigQuery) is INVALID_ARGUMENT, exit 2, and so is `--governed`
with `--ui` in this change. Raise the same code for a non-Snowflake backend
instead of CONFIG_ERROR (exit 5), so the code does not change after the first
release that has the option (CLI interface spec, C1).

The service test, the E2E skip condition and the documents that named
CONFIG_ERROR change with it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@Matovidlo
Matovidlo requested a review from soustruh October 22, 2026 11:14
Main moved by 8 commits (short flags for every option, #902, among them). The merge is
clean, `--governed` has no short flag in the registry, and the CLI spec gate, command
sync, lint and the workspace tests pass on the result.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@Matovidlo

Copy link
Copy Markdown
Contributor Author

@soustruh the point of your review is fixed: --governed on a backend other than Snowflake is now INVALID_ARGUMENT (exit 2), like --ui and like the same refusal in storage create-table, in bd733c8 (service, its test, the E2E skip condition and the docs that named CONFIG_ERROR). main is merged in as well (2b2b2de, so #902's short flags are covered: --governed has none in the registry and the CLI spec gate passes). Could you take another look when you have a moment?

Keep main's optional --project on workspace create next to --governed in
CLAUDE.md, the context text and the agent prompt, and keep the governed row of
the tool-selection matrix. Main's prompt sits 21 bytes under the 70000 byte
budget, so the ad-hoc SQL row and the new row are reworded to fit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

This branch has not been deployed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants