Skip to content

fix(operator): apply feature flags when scheduled sessions trigger#1572

Open
janisz wants to merge 19 commits into
ambient-code:mainfrom
janisz:fix/scheduled-session-feature-flags
Open

fix(operator): apply feature flags when scheduled sessions trigger#1572
janisz wants to merge 19 commits into
ambient-code:mainfrom
janisz:fix/scheduled-session-feature-flags

Conversation

@janisz
Copy link
Copy Markdown

@janisz janisz commented May 13, 2026

Fixes #1506 - Scheduled sessions were not respecting workspace feature flags like jira-write.

Root Cause

The session-trigger command creates AgenticSession CRs directly from a pre-stored template without re-evaluating workspace feature flags at trigger time. When regular sessions are created via the backend API, they check the jira-write flag and set JIRA_READ_ONLY_MODE=false if enabled. Scheduled sessions skipped this check entirely.

Solution

Added applyFeatureFlagOverrides() function that:

  1. Reads the feature-flag-overrides ConfigMap from the workspace namespace
  2. Checks if jira-write is set to "true"
  3. Sets JIRA_READ_ONLY_MODE=false in the session template's environment variables before creating the CR

The implementation follows the same pattern as CreateAgenticSession in the backend: read ConfigMap, check flag value, inject environment variable. Errors are logged but non-fatal to ensure degraded operation.

Summary by CodeRabbit

  • New Features

    • Workspace-level feature-flag overrides can now toggle Jira write behavior, updating session environment variables accordingly.
  • Bug Fixes

    • Reading missing or unreadable workspace overrides no longer blocks session creation; warnings are logged and creation continues.
  • Tests

    • Added unit tests verifying override application, preservation of existing environment variables, and non-application for non-true flag values.

Scheduled sessions were not respecting workspace feature flags like
`jira-write` because the session-trigger command creates AgenticSession
CRs directly from a pre-stored template without re-evaluating feature
flags at trigger time.

This fix adds applyFeatureFlagOverrides() to read the workspace's
feature-flag-overrides ConfigMap and apply settings (e.g., setting
JIRA_READ_ONLY_MODE=false when jira-write is enabled) before creating
the session CR.

The implementation follows the same pattern as CreateAgenticSession in
the backend: read ConfigMap, check flag value, inject environment
variable. Errors are logged but non-fatal to ensure degraded operation.

Fixes ambient-code#1506

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
@netlify
Copy link
Copy Markdown

netlify Bot commented May 13, 2026

Deploy Preview for cheerful-kitten-f556a0 canceled.

Name Link
🔨 Latest commit 2d28e6c
🔍 Latest deploy log https://app.netlify.com/projects/cheerful-kitten-f556a0/deploys/6a16020571db4d00086390d6

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 13, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: d1c5a247-3345-49c2-8de4-73f824265edd

📥 Commits

Reviewing files that changed from the base of the PR and between 3526563 and f3eca7e.

📒 Files selected for processing (2)
  • components/operator/internal/trigger/trigger.go
  • components/operator/internal/trigger/trigger_test.go

📝 Walkthrough

Walkthrough

Adds workspace-level feature-flag overrides: operator reads the feature-flag-overrides ConfigMap and, when jira-write: "true", injects JIRA_READ_ONLY_MODE="false" into the session template before creating the AgenticSession. ConfigMap read failures are logged and do not block session creation.

Changes

Workspace Feature-Flag Overrides for JIRA Write Access

Layer / File(s) Summary
Constants for config and env var keys
components/operator/internal/types/resources.go
New exported constants: FeatureFlagOverridesConfigMap = "feature-flag-overrides", JiraWriteFlagKey = "jira-write", JiraReadOnlyModeEnvVar = "JIRA_READ_ONLY_MODE".
Kubernetes client initialization and reuse
components/operator/internal/trigger/trigger.go
RunSessionTrigger now calls config.InitK8sClients() and uses config.DynamicClient for reuse/creation instead of constructing an in-file dynamic client. Import wiring updated for typed client usage and API-not-found helpers.
Feature-flag override helper
components/operator/internal/trigger/trigger.go
Added applyFeatureFlagOverrides which reads the feature-flag-overrides ConfigMap in the target namespace; if data["jira-write"] == "true" it ensures spec.environmentVariables["JIRA_READ_ONLY_MODE"] = "false" in the session template. Missing/errored lookups are logged and treated as non-fatal.
Session creation integration
components/operator/internal/trigger/trigger.go
createNewSession calls applyFeatureFlagOverrides when config.K8sClient != nil using a ~10s timeout before creating the AgenticSession. The dynamic-client Create call uses a separate 30s timeout context.
Unit tests for override behavior
components/operator/internal/trigger/trigger_test.go
Added TestApplyFeatureFlagOverrides (table-driven) using a fake Kubernetes clientset and ConfigMap permutations to assert that JIRA_READ_ONLY_MODE is added only when jira-write: "true", and that existing env vars are preserved.

Sequence Diagram

sequenceDiagram
  participant Operator as Operator (trigger)
  participant ConfigClient as K8s API (ConfigMap)
  participant Dynamic as DynamicClient (AgenticSession CR)
  participant Template as Session Template

  Operator->>ConfigClient: GET ConfigMap "feature-flag-overrides" (namespace)
  alt ConfigMap exists and jira-write == "true"
    ConfigClient-->>Operator: ConfigMap data
    Operator->>Template: set spec.environmentVariables["JIRA_READ_ONLY_MODE"]="false"
  else ConfigMap missing / jira-write != "true"
    ConfigClient-->>Operator: NotFound or non-true value
    Operator-->>Template: leave env vars unchanged
  end
  Operator->>Dynamic: CREATE AgenticSession CR (30s ctx) with Template
  Dynamic-->>Operator: creation result
Loading

Possibly Related PRs

  • ambient-code/platform#1138: Implements the same jira-write workspace feature-flag behavior via the feature-flag-overrides ConfigMap and JIRA_READ_ONLY_MODE injection.
  • ambient-code/platform#1136: Refactors RunSessionTrigger to use shared config.InitK8sClients() and config.DynamicClient for reuse/creation flows.
🚥 Pre-merge checks | ✅ 6 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning Changes implement the core requirement from #1506 by adding applyFeatureFlagOverrides to read jira-write flags and inject JIRA_READ_ONLY_MODE before CR creation. Critical blocker: tests and code write env var to wrong nesting level (template["spec"]["environmentVariables"] instead of template["environmentVariables"]). Tests must validate correct contract; production code must write to correct path for fix to work.
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR title follows Conventional Commits format (fix(operator): ...) and clearly describes the main change: applying feature flags during session triggering.
Out of Scope Changes check ✅ Passed Changes are focused and in-scope: new ConfigMap read logic, environment variable injection, typed constants, and corresponding unit tests directly support the jira-write feature flag application objective.
Performance And Algorithmic Complexity ✅ Passed No meaningful performance regressions. Code uses bounded O(n) ops, single K8s API calls, proper timeouts, no N+1 patterns. List lacks Limit but filters by label selector with expected <10 items.
Security And Secret Handling ✅ Passed PR passes security check: no hardcoded secrets, service account token properly read from K8s mount, ConfigMap read authenticated, no injection vulnerabilities, no sensitive data in logs.
Kubernetes Resource Safety ✅ Passed Namespaced AgenticSession CRs created with proper labels; ConfigMap reads granted by existing operator RBAC; no wildcard verbs or unsafe resource patterns.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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/operator/internal/trigger/trigger_test.go`:
- Around line 179-185: The test fixture nests the session spec under "spec"
which doesn't match the real runtime input consumed by
applyFeatureFlagOverrides; update the test to pass the spec map directly (not
wrapped as {"spec": ...}) and adjust assertions to inspect top-level keys (e.g.,
check environmentVariables on the map passed to applyFeatureFlagOverrides),
replacing usages of template["spec"] and any other occurrences in this file
(also update the similar block around the other failing assertions) so the test
exercises the same contract as applyFeatureFlagOverrides.

In `@components/operator/internal/trigger/trigger.go`:
- Around line 268-282: The code is writing environmentVariables into
template["spec"] producing spec.spec.environmentVariables; treat template as the
AgenticSession spec instead and set environmentVariables directly on template.
Update the logic that creates/reads spec and envVars (the variables named
template, spec and envVars) so you either assign spec =
template.(map[string]interface{}) or read envVars from
template["environmentVariables"] (not template["spec"]["environmentVariables"]),
then set envVars["JIRA_READ_ONLY_MODE"]="false" and store it back to
template["environmentVariables"] (or spec["environmentVariables"] if you
switched spec to reference template).
🪄 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: 058bc2ee-3c97-41f4-9304-fb8add42c69b

📥 Commits

Reviewing files that changed from the base of the PR and between ecdcd89 and adf081b.

📒 Files selected for processing (2)
  • components/operator/internal/trigger/trigger.go
  • components/operator/internal/trigger/trigger_test.go

Comment thread components/operator/internal/trigger/trigger_test.go Outdated
Comment thread components/operator/internal/trigger/trigger.go Outdated
janisz and others added 2 commits May 13, 2026 17:33
- Add constants for feature flag strings (FeatureFlagOverridesConfigMap,
  JiraWriteFlagKey, JiraReadOnlyModeEnvVar) to replace hardcoded values
- Reuse config.K8sClient and config.DynamicClient initialized in
  RunSessionTrigger instead of creating clients on every session
- Flatten nested conditionals in createNewSession using early returns
- Remove WHAT comments that duplicate code, keeping only WHY comments

This addresses code review findings: stringly-typed code, redundant
client creation, and unnecessary comments.

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
components/operator/internal/trigger/trigger.go (1)

256-269: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Write the override into the session spec, not spec.spec.

Line 256 treats template as if it contains a nested spec, but Line 304 already stores template as the CR spec. This produces spec.spec.environmentVariables, so JIRA_READ_ONLY_MODE won’t be applied on the expected path.

Suggested fix
-	spec, ok := template["spec"].(map[string]interface{})
-	if !ok {
-		spec = map[string]interface{}{}
-		template["spec"] = spec
-	}
-
-	envVars, ok := spec["environmentVariables"].(map[string]interface{})
+	envVars, ok := template["environmentVariables"].(map[string]interface{})
 	if !ok {
 		envVars = map[string]interface{}{}
 	}
 
 	envVars[types.JiraReadOnlyModeEnvVar] = "false"
-	spec["environmentVariables"] = envVars
+	template["environmentVariables"] = envVars
🤖 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/operator/internal/trigger/trigger.go` around lines 256 - 269, The
code is writing the env override into a nested spec map (creating
spec["environmentVariables"] under template["spec"]), which yields
spec.spec.environmentVariables; instead, locate the session spec map that was
stored as the CR spec (the top-level template map) and set its environment
variable there—so remove the intermediate spec nesting and write
types.JiraReadOnlyModeEnvVar = "false" directly into
template["environmentVariables"] (or into the sessionSpec map variable if one
exists), ensuring you update the same map that is later used as the CR spec (the
template variable), not a nested spec field.
🤖 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.

Duplicate comments:
In `@components/operator/internal/trigger/trigger.go`:
- Around line 256-269: The code is writing the env override into a nested spec
map (creating spec["environmentVariables"] under template["spec"]), which yields
spec.spec.environmentVariables; instead, locate the session spec map that was
stored as the CR spec (the top-level template map) and set its environment
variable there—so remove the intermediate spec nesting and write
types.JiraReadOnlyModeEnvVar = "false" directly into
template["environmentVariables"] (or into the sessionSpec map variable if one
exists), ensuring you update the same map that is later used as the CR spec (the
template variable), not a nested spec field.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 27f959f8-b42b-4b92-a943-4db44d18de89

📥 Commits

Reviewing files that changed from the base of the PR and between adf081b and 3526563.

📒 Files selected for processing (3)
  • components/operator/internal/trigger/trigger.go
  • components/operator/internal/trigger/trigger_test.go
  • components/operator/internal/types/resources.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/operator/internal/trigger/trigger_test.go

@markturansky
Copy link
Copy Markdown
Contributor

Claude Code Review

Summary

Clean, targeted fix that correctly ports the feature-flag override pattern from CreateAgenticSession (backend) to the operator's session-trigger path. The approach is sound: non-fatal ConfigMap reads, typed constants, table-driven tests covering all flag states. No blockers or criticals.

Findings

Blocker

None.

Critical

None.

Major

1. config.K8sClient != nil guard is misleading after a successful InitK8sClients()
createNewSession gates on config.K8sClient != nil before calling applyFeatureFlagOverrides. But RunSessionTrigger calls config.InitK8sClients() and fatally exits on error — so if we reach createNewSession, InitK8sClients already succeeded. A successful InitK8sClients should guarantee both DynamicClient and K8sClient are non-nil. The nil guard therefore implies it's an acceptable degraded state for the typed client to be missing, which contradicts the fatal-exit contract above and means feature flags silently won't apply in a case that should never occur. Either:

  • Remove the guard (trust the fatal-exit invariant), or
  • Make InitK8sClients explicitly guarantee the typed client is set and document why the guard exists.

Minor

2. RBAC not verified in diff
applyFeatureFlagOverrides adds a CoreV1().ConfigMaps(namespace).Get() call from the operator SA. The diff shows no RBAC manifest changes. CodeRabbit notes this was covered in PR #1138 — if that's confirmed, no action needed. But worth a comment in the PR linking to the existing ClusterRole rule so reviewers can verify without archaeology.

3. Removed explanatory comment on name-budget constraint
The original createNewSession had a comment explaining the 55-char K8s resource name budget derivation (session-N prefix + sanitized name + timestamp). That's a non-obvious invariant — a future reader adding to the name format could easily break it without the comment. Worth reinstating as a single-line note.

Nit

  • applyFeatureFlagOverrides always returns nil — all error paths log and return nil. The error return type creates an expectation of recoverable errors that's never fulfilled. Either remove the return type (making non-fatal explicit in the signature) or return actual errors and let the caller decide on severity.
  • 15 commits for a focused fix; squash on merge will handle it, but keeping the branch tidy makes git bisect easier during development.

Positive Highlights

  • Correct non-fatal design. Logging and continuing when the ConfigMap is missing or unreadable is exactly right — don't block session creation for feature-flag degradation.
  • Constants in types/resources.go. FeatureFlagOverridesConfigMap, JiraWriteFlagKey, JiraReadOnlyModeEnvVar are all in the right file alongside peer constants. No magic strings scattered in implementation code.
  • Test coverage is solid. Six table-driven cases cover: flag enabled, flag disabled, no ConfigMap (NotFound), pre-existing env vars preserved, unrelated flags ignored, non-"true" values rejected. This is the right test surface for the function.
  • No panic(), typed clients used correctly, existing env vars are additive not replaced. Conventions clean.

Recommendations

  1. (Major) Remove the config.K8sClient != nil guard or document why a nil typed client after successful init is possible.
  2. (Minor) Add a link to the existing RBAC rule in the PR description or a code comment, so the ConfigMap get permission is traceable.
  3. (Minor) Reinstate the one-line comment about the name-budget constraint in createNewSession.

Amber · standards loaded from CLAUDE.md, specs/standards/control-plane/

Copy link
Copy Markdown
Contributor

@markturansky markturansky left a comment

Choose a reason for hiding this comment

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

Amber Review — PR #1572

Summary

Correct approach overall — mirrors the backend CreateAgenticSession pattern for feature-flag injection into scheduled sessions. However, there is an unresolved structural bug that means JIRA_READ_ONLY_MODE is never actually applied at runtime. Both the implementation and its tests have the same mismatch.

Findings

Blocker — Wrong nesting level in applyFeatureFlagOverrides

template is already the AgenticSession spec — confirmed by:

  • template["displayName"] accessed directly in createNewSession (not template["spec"]["displayName"])
  • template["initialPrompt"] accessed directly in tryReuseLastSession
  • createNewSession wraps it as "spec": template when building the CR

So template["spec"] produces an extra level of nesting: the env var lands at spec.spec.environmentVariables. The operator reads spec.environmentVariablesJIRA_READ_ONLY_MODE is never applied. The feature flag fix is silently a no-op.

trigger.go fix:

-	spec, ok := template["spec"].(map[string]interface{})
-	if !ok {
-		spec = map[string]interface{}{}
-		template["spec"] = spec
-	}
-
-	envVars, ok := spec["environmentVariables"].(map[string]interface{})
+	envVars, ok := template["environmentVariables"].(map[string]interface{})
 	if !ok {
 		envVars = map[string]interface{}{}
 	}
 
 	envVars[types.JiraReadOnlyModeEnvVar] = "false"
-	spec["environmentVariables"] = envVars
+	template["environmentVariables"] = envVars

Blocker — Tests validate the wrong contract

The tests build template = {"spec": map{}} and assert template["spec"]["environmentVariables"]. But the real caller passes a flat spec map with no outer "spec" key. The tests exercise the wrong path, so they pass regardless of whether the production bug exists.

trigger_test.go fix:

-			template := map[string]interface{}{
-				"spec": map[string]interface{}{},
-			}
+			template := map[string]interface{}{}
 			if tt.existingEnvVars != nil {
-				spec := template["spec"].(map[string]interface{})
-				spec["environmentVariables"] = tt.existingEnvVars
+				template["environmentVariables"] = tt.existingEnvVars
 			}

And update all assertions to read from template["environmentVariables"] directly (remove the template["spec"] intermediary).

Score

2 blockers. REJECT — the fix is mechanically small but the tests need to be corrected too, otherwise the same class of bug can recur silently.


— Amber (code review agent)

@markturansky
Copy link
Copy Markdown
Contributor

Amber Code Review — REJECT

Reviewing at head SHA 7d0cf5f (re-review; prior comment from 2026-05-21 missed these blockers)


Findings

Blocker 1 — applyFeatureFlagOverrides writes env var to the wrong nesting level

In trigger.go, applyFeatureFlagOverrides does:

spec, ok := template["spec"].(map[string]interface{})
if !ok {
    spec = map[string]interface{}{}
    template["spec"] = spec
}
envVars, ok := spec["environmentVariables"].(map[string]interface{})
...
spec["environmentVariables"] = envVars

But template is already the AgenticSession spec. createNewSession builds the CR with:

"spec": template,

So writing to template["spec"]["environmentVariables"] produces the K8s path spec.spec.environmentVariables — a field that does not exist in the CRD schema. JIRA_READ_ONLY_MODE=false is never applied to the actual session.

Fix:

-  spec, ok := template["spec"].(map[string]interface{})
-  if !ok {
-      spec = map[string]interface{}{}
-      template["spec"] = spec
-  }
-
-  envVars, ok := spec["environmentVariables"].(map[string]interface{})
+  envVars, ok := template["environmentVariables"].(map[string]interface{})
   if !ok {
       envVars = map[string]interface{}{}
   }

   envVars[types.JiraReadOnlyModeEnvVar] = "false"
-  spec["environmentVariables"] = envVars
+  template["environmentVariables"] = envVars

Blocker 2 — Unit tests mirror the production bug and validate the wrong contract

The tests create the fixture as:

template := map[string]interface{}{
    "spec": map[string]interface{}{},
}

…and assert against:

spec, ok := template["spec"].(map[string]interface{})
envVars, ok := spec["environmentVariables"].(map[string]interface{})

Because the production code and the tests both use the same template["spec"] path, all 6 test cases pass green — but they are validating that the env var lands at spec.spec.environmentVariables, which is exactly the wrong place. The tests do not exercise the actual runtime contract.

Fix (test fixture and assertions):

-  template := map[string]interface{}{
-      "spec": map[string]interface{}{},
-  }
+  template := map[string]interface{}{}
   if tt.existingEnvVars != nil {
-      spec := template["spec"].(map[string]interface{})
-      spec["environmentVariables"] = tt.existingEnvVars
+      template["environmentVariables"] = tt.existingEnvVars
   }
   ...
-  spec, ok := template["spec"].(map[string]interface{})
-  if !ok {
-      t.Fatal("template[spec] is not a map")
-  }
-  envVars, ok := spec["environmentVariables"].(map[string]interface{})
+  envVars, ok := template["environmentVariables"].(map[string]interface{})

Summary

Both blockers share the same root cause: the author assumed template wraps a "spec" key, but it does not — it is the spec. The fix is small (3 lines in production, 5 in tests) but critical: without it, the scheduled-session feature-flag fix does nothing at runtime, and the test suite provides false confidence that it works.

The prior Amber comment (2026-05-21) reviewed the K8s client nil-guard issue and missed these two. That analysis still stands as a Major finding, but these blockers take precedence.


Amber · standards from CLAUDE.md, specs/standards/control-plane/

…ides

CodeRabbit and Amber reviews identified critical bug: template parameter
is already the AgenticSession spec, not a wrapper containing "spec" key.

Before: wrote to template["spec"]["environmentVariables"]
After: write to template["environmentVariables"]

This was causing JIRA_READ_ONLY_MODE to be written to the wrong path
(spec.spec.environmentVariables instead of spec.environmentVariables),
making the feature flag fix a silent no-op at runtime.

Also fixed unit tests that mirrored the production bug by validating
the wrong contract.

Fixes CodeRabbit review comments on PR ambient-code#1572.

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
@janisz
Copy link
Copy Markdown
Author

janisz commented May 25, 2026

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

CodeRabbit chat interactions are restricted to organization members for this repository. Ask an organization member to interact with CodeRabbit, or set chat.allow_non_org_members: true in your configuration.

@janisz janisz requested a review from markturansky May 25, 2026 09:13
@markturansky
Copy link
Copy Markdown
Contributor

🤖 Amber Analysis — PR #1572: fix(operator): apply feature flags when scheduled sessions trigger

Solid bug fix. The root cause (scheduled sessions bypassing workspace feature-flag evaluation) is correctly diagnosed, and the follow-up commit catching the nesting bug (template[\"spec\"][\"environmentVariables\"]template[\"environmentVariables\"]) is the right fix—that earlier path would have been a silent no-op at runtime.

Verdict: LGTM — two minor nits below, neither blocks merge.


Findings

Blocker

None.

Critical

None.

Major

None.

Minor

1. applyFeatureFlagOverrides has a dead error return (trigger.go)

The function signature is func applyFeatureFlagOverrides(...) error, but it never returns a non-nil error—all error paths log a warning and return nil. This makes the if err != nil branch in createNewSession dead code and the error return misleading.

Two clean options:

  • Drop the return type entirely: func applyFeatureFlagOverrides(...) and call it as a statement
  • If you want to keep it extensible, add a comment: // Always returns nil: designed non-fatal; future callers may surface real errors

Relevant standard: error-handling.spec.md — misleading signatures hide intent; silent-failure anti-pattern applies even at the call site.

2. Unreachable nil guard in createNewSession (trigger.go)

if config.K8sClient != nil {
    // apply feature flags
}

RunSessionTrigger calls log.Fatalf if config.InitK8sClients() fails, so config.K8sClient is always initialized by the time createNewSession is reached. The guard silently skips feature flag application in a scenario that can't happen, which would be harder to debug if the assumption ever changes.

The project standard (CLAUDE.md) is explicit: "Don't add error handling... for scenarios that can't happen." Removing the guard makes the intent clear; if future callers pass an uninitialized client, they should get a panic early rather than a silent skip.


Positive Highlights

  • Nesting bug caught and fixed: the follow-up commit (fix(operator): correct env var nesting level) correctly writes to template["environmentVariables"] instead of template["spec"]["environmentVariables"]. The updated tests validate the right contract.
  • Constants in types/resources.go: FeatureFlagOverridesConfigMap, JiraWriteFlagKey, JiraReadOnlyModeEnvVar — no stringly-typed code anywhere.
  • 6 thorough unit tests with fake.Clientset: covers enabled, disabled, missing ConfigMap, preserve existing vars, unrelated flags, and non-true value. This is the right test surface for an isolated helper.
  • Non-fatal design is correct: logging a warning and continuing when the ConfigMap is absent is strictly better than blocking session creation. errors.IsNotFound handling follows control-plane conventions spec exactly.
  • DRY client init: reusing config.InitK8sClients() + config.DynamicClient / config.K8sClient instead of creating a new in-cluster config on every trigger invocation is a clear improvement.
  • Comment hygiene: WHAT comments removed; only the WHY for the non-fatal design decision is retained.
Files reviewed
  • components/operator/internal/trigger/trigger.go (+45/-24)
  • components/operator/internal/trigger/trigger_test.go (+114)
  • components/operator/internal/types/resources.go (+9)

Standards applied: control-plane/conventions.spec.md, error-handling.spec.md, security/security.spec.md


Confidence: High (95%) — bug fix in a well-understood path, comprehensive tests, follows established patterns.

Copy link
Copy Markdown
Contributor

@markturansky markturansky left a comment

Choose a reason for hiding this comment

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

Amber Review — LGTM ✓

PR: fix(operator): apply feature flags when scheduled sessions trigger
Verdict: LGTM

Summary

Clean fix for #1506. applyFeatureFlagOverrides() correctly reads the workspace feature-flag-overrides ConfigMap at trigger time and injects JIRA_READ_ONLY_MODE=false when jira-write: "true". The critical nesting bug (writing to spec.spec.environmentVariables instead of spec.environmentVariables) was caught and fixed before merge. Non-fatal error handling (log+continue) is appropriate for a degraded-operation path.

Checks Passed

  • ✓ B1: No panic() in production code
  • ✓ B3: No direct type assertions on K8s unstructured objects (template is local map, not unstructured.Unstructured)
  • ✓ B4: Errors logged, not silently dropped
  • ✓ B6: No tokens in logs
  • ✓ B7: Uses %w where errors propagate
  • ✓ Tests: 6 table-driven cases covering enabled, disabled, missing ConfigMap, existing vars preserved, other flags ignored, non-"true" value

Minor Notes (non-blocking)

  1. applyFeatureFlagOverrides handles only jira-write. If more workspace flags are added, this function grows. A map of flag → envVar could scale better, but for one flag YAGNI applies.
  2. The function signature returns error but always returns nil (errors are swallowed with log). The return type is future-proofing — acceptable, but callers should not expect the error path to be reachable today.

— Amber (code review agent)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Intermittent failure to register jira-write MCP tools in active sessions

2 participants