feat(task/controller): TASK_DIR per-vault (required, no default)#22
Conversation
Different vaults follow different folder conventions:
openclaw → tasks
personal → 24 Tasks
Previously the binary defaulted TaskDir to '24 Tasks' but the STS env
hard-coded it to 'tasks' for both controllers, so commands targeting
the personal vault materialized in the wrong directory.
Changes:
- main.go: TaskDir field now required:"true" (no default). Missing
TASK_DIR env now fails fast at startup with a clear error instead of
silently defaulting.
- k8s/agent-task-controller-sts.yaml: TASK_DIR env reads
{{ "TASK_DIR" | env }} (was hardcoded 'tasks').
- k8s/Makefile: VAULTS tuple gains a third '=<task-dir>' field. The
apply loop exports TASK_DIR per iteration via cut -d'=' -f3-, which
preserves the space in '24 Tasks'.
Deploy migration: existing files in obsidian-personal's 'tasks/' folder
need to be 'git mv'd to '24 Tasks/' once. Future ticks dedup via UUID5
regardless of path, so no double-write.
There was a problem hiding this comment.
Based on my review of the 3 changed files, here is my analysis:
Review Findings
Changes Overview
The PR makes TASK_DIR a required field per-vault instead of a global default. This enforces explicit configuration for each vault (openclaw=tasks, personal=24 Tasks).
Analysis by Concern
Concern 1: TaskDir required with no default — crash loop risk
The concern about missing TASK_DIR causing startup failure is valid but intended. The change explicitly moves from a default of "24 Tasks" to required:"true". The agent-task-controller-sts.yaml now uses {{ "TASK_DIR" | env }} which will produce an empty string if unset — combined with required:"true", the binary will fail fast at startup. This is the correct behavior: it forces operators to provide TASK_DIR explicitly rather than silently using a wrong default. Migration path: update k8s manifests to set TASK_DIR before deploying this version.
Concern 2: Helm templating {{ "TASK_DIR" | env }} with unset variable
The helm env function returns the value of the named environment variable at template rendering time. If TASK_DIR is unset, this renders as an empty string (not the literal {{ ... }}). Since main.go now requires this field, the container will fail to start if the variable is missing. This is correct — it's a hard fail rather than silent wrong behavior.
Concern 3: cut -d'=' -f3- parsing with spaces in vault names
✅ No issue found. The command uses -f3- (from 3rd field to end), so 'personal=dwkkgw=24 Tasks' correctly extracts 24 Tasks (preserving the space). The vault name itself (personal) cannot contain = so field splitting is clean.
Must Fix (Critical)
None — the behavior is as intended.
Should Fix (Important)
None.
Nice to Have (Optional)
- Documentation: The
agent-task-controller-sts.yamlcomment block could document thatTASK_DIRenv var must be set by the Helm render pipeline (e.g., via{{ .Values.taskDir }}with a required field), since an unset variable produces an empty string that will crash the container.
{
"verdict": "approve",
"summary": "The PR correctly makes TASK_DIR required per-vault with no default. The three-file change (Makefile, STS template, main.go) is internally consistent: the Makefile exports TASK_DIR from the VAULTS tuple, the STS template consumes it via helm env templating, and main.go enforces it as required. Missing TASK_DIR causes fast-fail startup rather than a silent wrong-behavior crash — which is the intended design.",
"comments": [
{
"file": "task/controller/k8s/agent-task-controller-sts.yaml",
"line": 68,
"severity": "nit",
"message": "Consider adding a comment documenting that TASK_DIR must be provided at helm render time (e.g., via Values.taskDir with required=true), since an unset variable renders as empty string and will cause startup failure when combined with main.go's required:true flag."
}
],
"concerns_addressed": [
"correctness: TaskDir is now required with no default — the binary fails fast at startup if TASK_DIR is missing, enforcing explicit per-vault configuration rather than silently using a wrong default. This is the intended behavior.",
"correctness: The helm templating {{ \"TASK_DIR\" | env }} renders as empty string if unset. Combined with required:true in main.go, this causes a clear startup failure rather than silent misbehavior. Correct.",
"correctness: cut -d'=' -f3- correctly preserves '24 Tasks' (space included) by taking fields 3 onward. No issue with vault names containing spaces."
]
}
Vaults follow different folder conventions:
tasks24 TasksPreviously the binary defaulted
TaskDirto"24 Tasks"but the STS env hard-coded it to'tasks'for both controllers, so commands routed toagent-task-controller-personalmaterialized in the wrong directory (Personal vault grew atasks/folder instead of using the canonical24 Tasks/).Changes
task/controller/main.go:TaskDiris nowrequired:"true"with no default. MissingTASK_DIRenv now fails fast at startup with a clear validation error instead of silently defaulting to a vault-incorrect path.task/controller/k8s/agent-task-controller-sts.yaml:TASK_DIRenv reads{{ "TASK_DIR" | env }}(was hard-coded'tasks').task/controller/k8s/Makefile: VAULTS tuple gains a third=<task-dir>field. The apply loop exportsTASK_DIRper iteration viacut -d'=' -f3-, preserving the space in"24 Tasks".Migration
After this lands:
BRANCH=dev make apply+BRANCH=prod make applyredeploy with new env wiringgit mv45 files inbborbe/obsidian-personalfromtasks/→24 Tasks/(one-time)24 Tasks/; UUID5 prevents duplicates regardless of pathTest plan
/coding:pr-reviewloop convergedArgument: TaskDir '24 Tasks'