You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Base this work on feature/package-as-jobs, not main.
The Jobs migration (#223) is integrating on that branch. main does not yet execute
packages as Jobs, so a PR opened against main will be missing the code this depends
on. feature/package-as-jobs merges to main once #305 lands.
That branch squash-merges, so if your branch is stacked on another PR in this epic,
replay only your own commits when restacking: git rebase --onto origin/feature/package-as-jobs <last-inherited-commit>
Part of #223. Found by manual validation on 2026-08-13 (945b0061), cases 3, 11 and 13 of the Jobs
validation plan.
What happens
The operator deletes package-stage Jobs on several paths and explains none of them. deleteJobForeground
(operator/internal/controller/job_controller.go:752) logs nothing at any level and records no event:
Only the error path is observable. Every successful deletion is silent, including:
the orphaned-node sweep — case 13: the Job for a deleted node was simply gone 5s later;
the stale-spec / spec-edit clear of a terminal Job — case 3 (editing a failing package to unpark
it) and case 4 (raising stageTimeout);
config invalidation of an in-flight Job — case 11.
Why it matters
The asymmetry is the problem, not the silence in isolation. Job creation and stage transitions are
well covered — roughly 296 events over a 70-minute run (NodeWright/State, NodeWright/Apply, Node/State, Node/Apply), plus Job/SuccessfulCreate from the Job controller. So a reader trusts
the event stream, and then a Job disappears from it with no explanation.
At 3am a vanished Job is indistinguishable from:
a TTL collection (ttlSecondsAfterFinished firing),
the orphan sweep,
a stale-spec clear,
someone running kubectl delete job.
Those have very different meanings and the same observable signature. The Jobs validation plan's own
step for case 13 — "check the operator log for the sweep line rather than inferring from absence" —
cannot be satisfied today; the case can only be confirmed by watching the object vanish.
For scale: across the whole run the operator log contained 10 distinct messages, all
controller-runtime startup noise plus one reconcile error.
Suggested fix
One logger.Info in deleteJobForeground with the job name, node, and a reason supplied by the
caller — e.g. orphaned-node, stale-spec, config-update, timed-out-cleared. A caller-supplied
reason is the important half: the function itself cannot distinguish the cases, and the reason is
exactly what the reader is missing.
Diagnosability only — no lifecycle impact, nothing wedges, node state is correct on every path. Same
family as #443 and #449: these cost the ability to explain what happened, not correctness.
Important
Base this work on
feature/package-as-jobs, notmain.The Jobs migration (#223) is integrating on that branch.
maindoes not yet executepackages as Jobs, so a PR opened against
mainwill be missing the code this dependson.
feature/package-as-jobsmerges tomainonce #305 lands.That branch squash-merges, so if your branch is stacked on another PR in this epic,
replay only your own commits when restacking:
git rebase --onto origin/feature/package-as-jobs <last-inherited-commit>Part of #223. Found by manual validation on 2026-08-13 (
945b0061), cases 3, 11 and 13 of the Jobsvalidation plan.
What happens
The operator deletes package-stage Jobs on several paths and explains none of them.
deleteJobForeground(
operator/internal/controller/job_controller.go:752) logs nothing at any level and records no event:Only the error path is observable. Every successful deletion is silent, including:
it) and case 4 (raising
stageTimeout);Why it matters
The asymmetry is the problem, not the silence in isolation. Job creation and stage transitions are
well covered — roughly 296 events over a 70-minute run (
NodeWright/State,NodeWright/Apply,Node/State,Node/Apply), plusJob/SuccessfulCreatefrom the Job controller. So a reader truststhe event stream, and then a Job disappears from it with no explanation.
At 3am a vanished Job is indistinguishable from:
ttlSecondsAfterFinishedfiring),kubectl delete job.Those have very different meanings and the same observable signature. The Jobs validation plan's own
step for case 13 — "check the operator log for the sweep line rather than inferring from absence" —
cannot be satisfied today; the case can only be confirmed by watching the object vanish.
For scale: across the whole run the operator log contained 10 distinct messages, all
controller-runtime startup noise plus one reconcile error.
Suggested fix
One
logger.InfoindeleteJobForegroundwith the job name, node, and a reason supplied by thecaller — e.g.
orphaned-node,stale-spec,config-update,timed-out-cleared. A caller-suppliedreason is the important half: the function itself cannot distinguish the cases, and the reason is
exactly what the reader is missing.
Diagnosability only — no lifecycle impact, nothing wedges, node state is correct on every path. Same
family as #443 and #449: these cost the ability to explain what happened, not correctness.