-
Notifications
You must be signed in to change notification settings - Fork 2
workflow_variables_lost_on_env_ref_replacement
An agent working in confida-qa-testing, verifying a staging API bug, stopped
and said:
Variables aren't injected in this shell — reading them from the group config instead.
It then worked around the platform by hand:
export VAR_SITE_URL=$(jq -r '.groups[]|select(.name=="confida-staging").values.SITE_URL' variables/variables.json)The diagnosis was correct and the workaround was sound. $VAR_* genuinely was
not in the shell. The cost is invisible: every agent that hits this either
re-derives values from variables/variables.json, or hardcodes them — which is
exactly what the builder prompt tells it never to do.
The variables WERE loaded. From server_debug.log:
12:16:15 [VARIABLES] Synced 28 variable values as VAR_* env vars:
[VAR_SITE_URL VAR_LOGIN_EMAIL VAR_ADMIN_PASSWORD …]
12:19:29 🔗 Stored workspace env ref (keys: [MCP_SESSION_ID MCP_MCP MCP_CUSTOM …])
The second line has no VAR_* at all. Across the whole day's log:
| check | result |
|---|---|
[VARIABLES] Synced fired |
17 times, 3 of them with the full 28-variable confida set |
VAR_SITE_URL present in any Stored workspace env ref
|
0 times, ever |
SECRET_* present in those same env refs |
yes, consistently |
So variables were computed and written, and then disappeared, while secrets sitting in the same map survived.
BaseOrchestrator.SetWorkspaceEnvRef does not merge into the existing env map.
It replaces it, and then restores what must survive the swap:
bo.workspaceEnvRef = env // whole map swapped
for _, secret := range bo.secrets {
env["SECRET_"+secret.Name] = secret.Value // secrets restored
} // VAR_* were notSyncVariablesToWorkspaceEnv wrote VAR_* into whichever map was current at
the time. Any later SetWorkspaceEnvRef — and initialization performs several —
dropped them.
The tell was already in the file. The doc comment immediately above that function describes this exact failure, for secrets:
"Backfill them here so initialization order cannot leave the builder shell without
SECRET_*variables that are already attached to the workflow."
Someone hit this, fixed it for secrets, and variables were never added to the same backfill. One mechanism, two kinds of state, only one of them protected.
Severity depends on how much a workflow loads and how early.
controller_workshop.go:130 auto-loads the full variable set when a workflow has
exactly one group — confida-login has exactly one (confida-staging), so all 28
values were resolved at workshop start, giving the longest possible window for a
later env-ref store to wipe them.
Workflows that sync only a couple of framework variables late (the sessions
logging [VAR_USER_ID VAR_CREDENTIALS_SHEET_ID]) lose less, and nobody notices.
The bug was equally present in both.
Give the orchestrator the variables to restore, exactly as it already holds secrets:
-
BaseOrchestrator.workspaceVars, guarded by the existingworkspaceEnvMu. -
SetWorkspaceVariables(values)records them. It merges rather than replaces, so a later group-scoped load adds to what is known instead of dropping what an earlier one resolved. Values are copied, so a caller mutating its own map afterwards cannot change what gets backfilled. -
SetWorkspaceEnvRefbackfillsVAR_*in the same place it backfillsSECRET_*. -
SyncVariablesToWorkspaceEnvrecords on the orchestrator before touching the live map.
That last ordering change fixes a second case that was never reported: variables resolved before any env map exists were previously discarded outright. They are now applied to the first map that appears.
pkg/orchestrator/workspace_vars_backfill_test.go — four cases: survives a map
replacement, applies when recorded before any map exists, merges across repeated
loads, and secrets still backfill alongside.
Confirmed the tests catch the defect rather than merely describing it: removing
only the backfill loop makes two of them fail with
VAR_SITE_URL after first store = "".
- Requires a server restart. The running process keeps the old behavior, so a session started before the restart still has no variables.
- Two tests in
step_based_workflow(TestWorkshopModeIsMergedSuperset,TestWorkshopCLIPromptUsesProjectedWorkspaceToolReference) fail both with and without this change — verified by stashing. They arrived with unrelated upstream commits;run_goal_advisor_reviewis missing from the workshop prompt. Not addressed here. - The shape is this archive's recurring one: one fact, two sources, and nothing checking they agree. Here the two sources are the live env map and the orchestrator state that can rebuild it, and only half the state was registered for rebuilding.
Auto-synced from docs/ on main. Edit there, not here.