spec(api): reconcile ambient-model spec against implementation#1548
Conversation
Spec reconciliation run against components/ambient-api-server. Fixes stale
spec entries, removes a phantom OpenAPI field, and drops an unused DB column.
Spec changes (specs/api/ambient-model.spec.md):
- Status: Active (was "Proposed — Pending Consensus")
- Agent ERD: added 12 undocumented fields (parent_agent_id, owner_user_id,
display_name, description, repo_url, workflow_id, llm_model,
llm_temperature, llm_max_tokens, bot_account_name, resource_overrides,
environment_variables); documented ignite_handler propagation to Session
- ScheduledSession ERD: added timeout, inactivity_timeout,
stop_on_run_finished, runner_type
- Session field types: int → int32 for llm_max_tokens, timeout,
sdk_restart_count
- Ignite Response example: triggered_by_user_id → created_by_user_id
- Credentials: document scoping gap (impl=project-scoped, spec=global);
added vertex to provider enum table; CLI table updated to ✅ project-scoped
- RBAC endpoints: added GET/{id} and PATCH/{id} for role_bindings as ✅;
added 4 scoped role_binding query endpoints as 🔲
- Coverage matrix: credentials, RBAC full CRUD, project update all ✅
- CLI reference: project update ✅; RBAC list/get/delete ✅
- CLI Known Gaps: removed now-implemented items; added credential bind gap
- Agent section prose: full field table with all 15 fields
OpenAPI changes (components/ambient-api-server/openapi/):
- openapi.credentials.yaml: added vertex to provider enum
- openapi.projects.yaml: removed display_name property
- openapi.sessions.yaml: removed triggered_by_user_id (phantom field —
never existed in model.go)
Code changes (components/ambient-api-server/plugins/projects/):
- model.go: removed DisplayName from Project and ProjectPatchRequest
- grpc_handler.go: removed DisplayName from create/update handlers
- grpc_presenter.go: removed DisplayName from projectToProto()
- migration.go: added dropDisplayNameMigration() (ID 202505090001)
- plugin.go: registered new drop-column migration
- factory_test.go: removed DisplayName from test factory
- grpc_integration_test.go: removed display_name test assertions
- plugins/projectSettings/factory_test.go: removed stale DisplayName
Workflow update (workflows/sessions/ambient-model.workflow.md):
- Added 2026-05-09 run log with 5 lessons learned
Note: proto/ambient/v1/projects.proto still declares display_name (buf not
available); proto3 semantics make this safe — field transmits as zero value.
Follow-up: remove when buf is available.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
❌ Deploy Preview for cheerful-kitten-f556a0 failed.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughRemoves Project.display_name from models, tests, gRPC mapping and SDKs with a DB migration; adds project‑scoped Credential OpenAPI endpoints (including /token) and tightens credential ChangesProject DisplayName Removal
Credentials OpenAPI (project‑scoped) and Token
Sessions OpenAPI and Schema Changes
Sequence DiagramsequenceDiagram
participant Client as Client
participant API as API\ Server
participant DB as Database
participant Runner as Runner/Event\ Stream
Client->>API: POST /api/ambient/v1/sessions (create)
API->>DB: INSERT session
DB-->>API: session record
API-->>Client: 201 Created (session)
Client->>API: POST /api/ambient/v1/sessions/{id}/start
API->>Runner: start runner for session
Runner-->>API: runner events (SSE)
API-->>Client: SSE stream (/events) relays runner events
Client->>API: POST /api/ambient/v1/sessions/{id}/stop
API->>Runner: stop runner
API->>DB: UPDATE session status
DB-->>API: updated record
API-->>Client: 200 OK
Possibly related PRs
Suggested labels: Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 error, 2 warnings)
✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Comment |
Review: spec(api): reconcile ambient-model spec against implementationSolid audit work — the field-level gap analysis on A few things to fix before merge: 🔴 Accidental file mode changes on 10 filesThe diff shows This looks like a local umask or editor artifact. Strip the mode changes before merging: git diff --name-only HEAD~1 | xargs chmod 644
git add -u🟡
|
- Strip accidental 100644→100755 mode changes on all modified files - Add TODO(proto-cleanup) comments to projects.proto on the three display_name fields (Project, CreateProjectRequest, UpdateProjectRequest) so the next engineer with buf available finds them immediately Note on credential:token-reader guard: neither POST nor PATCH /role_bindings currently guards against assigning this role via the user-facing API — both handlers accept any RoleId without validation. This is a security gap in both operations, not just PATCH. Tracking as a follow-up item; out of scope for this reconciliation PR. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Thanks for the review. Here's what was addressed in the fixup commit (6afe44a): 🔴 File mode changes — stripped. All 10 files restored to 100644. Root cause was local umask on the session environment. 🟡 Proto TODO — added 🟡 Credential scoping / security spec alignment — noted. Will open a follow-up ticket referencing both specs (#1514 and this one) so the migration from project-scoped to global has the full context. 🟢 🟡 |
|
CodeRabbit chat interactions are restricted to organization members for this repository. Ask an organization member to interact with CodeRabbit, or set |
Merge Queue Status
This pull request spent 4 minutes 44 seconds in the queue, including 14 seconds running CI. Required conditions to mergeReasonThe pull request #1548 has been manually updated HintIf you want to requeue this pull request, you can post a |
…me migration Migration ID 202505090001 sorts before the table-creation migration 202602150010, so on fresh testcontainer DBs it runs before the projects table exists. Using ALTER TABLE IF EXISTS makes the migration a no-op when the table doesn't exist, fixing the pq: relation "projects" does not exist error in CI. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
components/ambient-api-server/plugins/projects/migration.go (1)
44-54: 💤 Low valueMigration ID is backdated by over a year.
The migration ID
202505090001represents May 2025, but it's being added in a PR from May 2026 and registered after migrations from 2026-02 and 2026-03. While Gormigrate runs migrations in registration order (so this works functionally), the backdated ID is confusing for maintenance and creates wasteful churn on fresh databases (create column → drop column).Consider either:
- Using a current-date ID like
202605090001(2026-05-09)- Or registering this migration before the others if it genuinely should have run first
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/ambient-api-server/plugins/projects/migration.go` around lines 44 - 54, The migration dropDisplayNameMigration currently uses a backdated ID "202505090001" which mismatches the PR timing and the surrounding 2026 migrations; update the Migration.ID in dropDisplayNameMigration to a current-date ID (e.g., "202605090001") to reflect when this migration is introduced, or alternatively move the call that registers dropDisplayNameMigration so it is placed before the 2026-02/2026-03 migrations if it truly must run earlier; ensure the chosen approach keeps the Migrate and Rollback functions (tx.Exec ALTER TABLE ... display_name) unchanged and that registration order in the migrations list is consistent with the new ID choice.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@components/ambient-api-server/openapi/openapi.credentials.yaml`:
- Line 46: Add a new handler GetVertexCredentialsForSession that mirrors the
pattern and signature of the existing handlers (e.g.,
GetGithubCredentialsForSession / GetGoogleCredentialsForSession) to return
vertex provider credentials for a session, implement the same request parsing,
auth checks, and response shape used by the other credential handlers, and then
register this handler in the same endpoint registration block where the other
credential handlers (github, google, jira, gitlab, coderabbit, gerrit) are
registered so the OpenAPI enum value "vertex" is backed by an operative
endpoint.
---
Nitpick comments:
In `@components/ambient-api-server/plugins/projects/migration.go`:
- Around line 44-54: The migration dropDisplayNameMigration currently uses a
backdated ID "202505090001" which mismatches the PR timing and the surrounding
2026 migrations; update the Migration.ID in dropDisplayNameMigration to a
current-date ID (e.g., "202605090001") to reflect when this migration is
introduced, or alternatively move the call that registers
dropDisplayNameMigration so it is placed before the 2026-02/2026-03 migrations
if it truly must run earlier; ensure the chosen approach keeps the Migrate and
Rollback functions (tx.Exec ALTER TABLE ... display_name) unchanged and that
registration order in the migrations list is consistent with the new ID choice.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 17abb553-2559-42d5-b4cf-e2f3c333b805
📒 Files selected for processing (14)
components/ambient-api-server/openapi/openapi.credentials.yamlcomponents/ambient-api-server/openapi/openapi.projects.yamlcomponents/ambient-api-server/openapi/openapi.sessions.yamlcomponents/ambient-api-server/plugins/projectSettings/factory_test.gocomponents/ambient-api-server/plugins/projects/factory_test.gocomponents/ambient-api-server/plugins/projects/grpc_handler.gocomponents/ambient-api-server/plugins/projects/grpc_integration_test.gocomponents/ambient-api-server/plugins/projects/grpc_presenter.gocomponents/ambient-api-server/plugins/projects/migration.gocomponents/ambient-api-server/plugins/projects/model.gocomponents/ambient-api-server/plugins/projects/plugin.gocomponents/ambient-api-server/proto/ambient/v1/projects.protospecs/api/ambient-model.spec.mdworkflows/sessions/ambient-model.workflow.md
💤 Files with no reviewable changes (7)
- components/ambient-api-server/plugins/projects/model.go
- components/ambient-api-server/plugins/projects/grpc_presenter.go
- components/ambient-api-server/plugins/projects/grpc_handler.go
- components/ambient-api-server/plugins/projects/factory_test.go
- components/ambient-api-server/plugins/projectSettings/factory_test.go
- components/ambient-api-server/openapi/openapi.projects.yaml
- components/ambient-api-server/openapi/openapi.sessions.yaml
| schema: | ||
| type: string | ||
| enum: [github, gitlab, jira, google, kubeconfig] | ||
| enum: [github, gitlab, jira, google, vertex, kubeconfig] |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify vertex provider is accepted in backend credential handlers
# Check for provider validation/switch statements in credentials plugin
rg -nP --type=go -C3 'provider\s*(==|:=|case|switch)' components/ambient-api-server/plugins/credentials/
# Check for hardcoded provider lists that might need updating
rg -nP --type=go 'github.*gitlab.*jira.*google' components/ambient-api-server/plugins/credentials/Repository: ambient-code/platform
Length of output: 47
🏁 Script executed:
fd -t f -i "credential" --type f | head -20Repository: ambient-code/platform
Length of output: 1332
🏁 Script executed:
find components -type d -name "*credential*" 2>/dev/nullRepository: ambient-code/platform
Length of output: 251
🏁 Script executed:
rg -t go "github.*gitlab.*jira" --max-count 5Repository: ambient-code/platform
Length of output: 422
🏁 Script executed:
rg -t go "case.*github" --max-count 5Repository: ambient-code/platform
Length of output: 171
🏁 Script executed:
# Search for provider validation in backend handlers
rg -t go "provider" components/backend/handlers/oauth.go -A 5 | head -50Repository: ambient-code/platform
Length of output: 1852
🏁 Script executed:
# Check runtime credentials handler for provider switch/validation
rg -t go "case.*:" components/backend/handlers/runtime_credentials.go | grep -i "github\|gitlab\|jira\|google\|vertex\|kubeconfig"Repository: ambient-code/platform
Length of output: 47
🏁 Script executed:
# Look for provider enums or constants
rg -t go "(github|gitlab|jira|google|kubeconfig)" components/backend/handlers/ -A 2Repository: ambient-code/platform
Length of output: 50378
🏁 Script executed:
# Check if vertex is mentioned anywhere in Go code
rg -t go "vertex" components/Repository: ambient-code/platform
Length of output: 10711
🏁 Script executed:
# Look for credential handler function that validates provider
rg -t go "func.*Credential" components/backend/handlers/ -A 10 | head -80Repository: ambient-code/platform
Length of output: 6468
🏁 Script executed:
# Search for where credentials are actually retrieved/validated
rg -t go "GetCredential\|provider.*==" components/backend/handlers/ -B 2 -A 3Repository: ambient-code/platform
Length of output: 47
🏁 Script executed:
# Check runtime_credentials.go for vertex handling
cat components/backend/handlers/runtime_credentials.go | head -150Repository: ambient-code/platform
Length of output: 5935
🏁 Script executed:
# Search for any vertex credential-specific handling or routing
rg -t go "GetVertexCredentials\|case.*vertex" components/backend/Repository: ambient-code/platform
Length of output: 47
🏁 Script executed:
# Check if there's a router/mux registration for vertex credentials endpoint
rg -t go "credentials/vertex\|/vertex" components/backend/handlers/ | grep -v test | head -20Repository: ambient-code/platform
Length of output: 47
Add backend credential handler for vertex provider.
The vertex enum value was added to credential provider schemas in the OpenAPI spec, but the backend lacks a corresponding credential handler function. Add GetVertexCredentialsForSession() in components/backend/handlers/runtime_credentials.go and register the endpoint, similar to existing handlers for github, google, jira, gitlab, coderabbit, and gerrit.
Also applies to: 287-287, 322-322, 349-349
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@components/ambient-api-server/openapi/openapi.credentials.yaml` at line 46,
Add a new handler GetVertexCredentialsForSession that mirrors the pattern and
signature of the existing handlers (e.g., GetGithubCredentialsForSession /
GetGoogleCredentialsForSession) to return vertex provider credentials for a
session, implement the same request parsing, auth checks, and response shape
used by the other credential handlers, and then register this handler in the
same endpoint registration block where the other credential handlers (github,
google, jira, gitlab, coderabbit, gerrit) are registered so the OpenAPI enum
value "vertex" is backed by an operative endpoint.
Regenerate Go, Python, and TypeScript SDKs to reflect: - Remove display_name from Project type - Remove triggered_by_user_id from Session type - Add vertex to Credential provider options Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…l views Both fields were removed from the SDK types: - Project.DisplayName removed (field deleted from model in migration 202505090001) - Session.TriggeredByUserID removed (phantom field; CreatedByUserID is the correct field) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The NetworkPolicy applied podSelector:{} (all pods) with a single
ingress rule allowing only runner pods. kindnet (kind v1.35 / kind
v0.27) enforces NetworkPolicy, so this blocked all same-namespace
pod-to-pod traffic — including frontend → backend-api requests that
Cypress exercises in E2E tests.
Add an explicit same-namespace allow rule so intra-namespace traffic
continues while still permitting runner pods from any namespace.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@components/manifests/base/runner-networkpolicy.yaml`:
- Around line 10-11: Remove the overly permissive ingress stanza that reads
"from: - podSelector: {}" so the NetworkPolicy no longer allows ingress from any
pod in the namespace; instead keep the constrained ingress rule that matches the
runner-specific label (the existing rule that uses podSelector with the runner
label). Locate the ingress rule containing "from:" and "podSelector: {}" and
delete that bullet entry so only the label-scoped ingress (the rule referencing
the runner podSelector match) remains.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 1014883d-c45e-46fa-a34d-c2a79cdde174
📒 Files selected for processing (1)
components/manifests/base/runner-networkpolicy.yaml
| - from: | ||
| - podSelector: {} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n components/manifests/base/runner-networkpolicy.yamlRepository: ambient-code/platform
Length of output: 484
🏁 Script executed:
# Verify Kubernetes NetworkPolicy ingress rule semantics
# Rule 1 with empty podSelector in same namespace context
python3 << 'EOF'
import yaml
policy_content = """
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-runner-namespaces
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- podSelector: {}
- from:
- namespaceSelector: {}
podSelector:
matchLabels:
app: ambient-code-runner
"""
policy = yaml.safe_load(policy_content)
ingress_rules = policy['spec']['ingress']
print("=== NetworkPolicy Analysis ===")
print(f"Destination podSelector: {policy['spec']['podSelector']}")
print(f" → Matches: ALL pods in this namespace\n")
for i, rule in enumerate(ingress_rules, 1):
print(f"Rule {i}:")
if 'from' in rule:
for selector in rule['from']:
has_ns_sel = 'namespaceSelector' in selector
has_pod_sel = 'podSelector' in selector
if has_ns_sel:
print(f" - namespaceSelector: {selector.get('namespaceSelector', {})}")
print(f" → Matches: ALL namespaces")
if has_pod_sel:
pod_sel = selector.get('podSelector', {})
if not pod_sel or pod_sel == {}:
scope = "SAME namespace" if not has_ns_sel else "ALL namespaces"
print(f" - podSelector: {} (empty)")
print(f" → Matches: ALL pods in {scope}")
else:
print(f" - podSelector: {pod_sel}")
print()
print("=== Security Assessment ===")
print("Rule 1 allows ingress from ANY pod in the same namespace → OVERLY PERMISSIVE")
print("Rule 2 allows ingress from labeled pods (app=ambient-code-runner) → MORE RESTRICTIVE")
print("Violation: Least-privilege principle not followed in Rule 1")
EOFRepository: ambient-code/platform
Length of output: 227
🏁 Script executed:
# Verify Kubernetes NetworkPolicy ingress rule semantics
python3 << 'EOF'
import yaml
policy_content = """
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-runner-namespaces
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- podSelector: {}
- from:
- namespaceSelector: {}
podSelector:
matchLabels:
app: ambient-code-runner
"""
policy = yaml.safe_load(policy_content)
ingress_rules = policy['spec']['ingress']
print("=== NetworkPolicy Analysis ===")
print(f"Destination podSelector: {policy['spec']['podSelector']}")
print(f" → Matches: ALL pods in this namespace\n")
for i, rule in enumerate(ingress_rules, 1):
print(f"Rule {i}:")
if 'from' in rule:
for selector in rule['from']:
has_ns_sel = 'namespaceSelector' in selector
has_pod_sel = 'podSelector' in selector
if has_ns_sel:
print(f" - namespaceSelector: {selector.get('namespaceSelector', {})}")
print(f" → Matches: ALL namespaces")
if has_pod_sel:
pod_sel = selector.get('podSelector', {})
if not pod_sel or pod_sel == {}:
scope = "SAME namespace" if not has_ns_sel else "ALL namespaces"
print(f" - podSelector: (empty)")
print(f" → Matches: ALL pods in {scope}")
else:
print(f" - podSelector: {pod_sel}")
print()
print("=== Security Assessment ===")
print("Rule 1 allows ingress from ANY pod in the same namespace → OVERLY PERMISSIVE")
print("Rule 2 allows ingress from labeled pods (app=ambient-code-runner) → MORE RESTRICTIVE")
print("Violation: Least-privilege principle not followed in Rule 1")
EOFRepository: ambient-code/platform
Length of output: 621
Remove overly permissive ingress rule on lines 10–11.
The from: - podSelector: {} rule allows ingress from any pod in the same namespace, violating least-privilege. With destination podSelector: {}, this opens broad east-west ingress contrary to the policy intent (runner-specific allow). Rule 2 (lines 12–16) already provides the proper constrained rule with label matching.
Suggested fix
ingress:
- - from:
- - podSelector: {}
- from:
- namespaceSelector: {}
podSelector:
matchLabels:
app: ambient-code-runner📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - from: | |
| - podSelector: {} | |
| - from: | |
| - namespaceSelector: {} | |
| podSelector: | |
| matchLabels: | |
| app: ambient-code-runner |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@components/manifests/base/runner-networkpolicy.yaml` around lines 10 - 11,
Remove the overly permissive ingress stanza that reads "from: - podSelector: {}"
so the NetworkPolicy no longer allows ingress from any pod in the namespace;
instead keep the constrained ingress rule that matches the runner-specific label
(the existing rule that uses podSelector with the runner label). Locate the
ingress rule containing "from:" and "podSelector: {}" and delete that bullet
entry so only the label-scoped ingress (the rule referencing the runner
podSelector match) remains.
|
CodeRabbit chat interactions are restricted to organization members for this repository. Ask an organization member to interact with CodeRabbit, or set |
The NetworkPolicy with policyTypes:Ingress creates an implicit deny-all
for every pod it selects. NodePort traffic (kube-proxy → frontend pod)
does not originate from a pod, so it was blocked even after the
same-namespace podSelector rule was added.
Replace the same-namespace rule with a wildcard ingress rule (- {}) that
permits all traffic sources, including NodePort and external clients.
The runner-pod rule is preserved for forward compatibility with any
future default-deny policy.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Merge Queue Status
This pull request spent 45 seconds in the queue, including 6 seconds running CI. Required conditions to merge |
Summary
specs/api/ambient-model.spec.mdagainst the actual implementation incomponents/ambient-api-server/triggered_by_user_idfield, removeddisplay_namefrom Project schema, addedvertexto credential provider enumdisplay_namefrom Project model, gRPC layer, and added a drop-column DB migration (202505090001)workflows/sessions/ambient-model.workflow.mdfrom this runSpec changes (
specs/api/ambient-model.spec.md)timeout,inactivity_timeout,stop_on_run_finished,runner_typeint→int32forllm_max_tokens,timeout,sdk_restart_countGET/{id}+PATCH/{id}for role_bindings as ✅; 4 scoped query endpoints as 🔲OpenAPI changes
openapi.credentials.yamlvertexto provider enumopenapi.projects.yamldisplay_nameopenapi.sessions.yamltriggered_by_user_id(phantom — never in model.go)Code changes (
plugins/projects/)model.goDisplayNamefromProject+ProjectPatchRequestgrpc_handler.goDisplayNamefrom create/updategrpc_presenter.goDisplayNamefromprojectToProto()migration.godropDisplayNameMigration()(ID202505090001)plugin.gofactory_test.go,grpc_integration_test.godisplay_nametest referencesplugins/projectSettings/factory_test.goDisplayNamefrom cross-plugin factoryKnown follow-up
proto/ambient/v1/projects.protostill declaresdisplay_name—bufis not available in the dev environment. Proto3 semantics make this safe (field transmits as zero value, ignored by consumers). Follow-up ticket needed to remove whenbufis available.Test plan
go build ./...passes (verified locally)go vet ./...passes (verified locally)202505090001runs cleanly against a fresh DBdisplay_nameassertions🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Changes
Documentation