refactor(task): drop taskParamser interface; use client.TaskBuilder directly#192
Merged
refactor(task): drop taskParamser interface; use client.TaskBuilder directly#192
Conversation
…irectly Removes the controller's local taskParamser interface (toRequestParams, taskType) and the 14 *Params shells that implemented it. The executor now constrains T to sidecar.TaskBuilder (the seictl-defined interface), calling builder.ToTaskRequest() to produce the wire payload and overriding TaskRequest.Id with the deterministic plan-task UUID. 13 of 14 sidecar tasks use the seictl client.*Task wrappers directly — single source of truth for shape, validation, and wire format. The planner constructs them at the call site; the deserialize registry unmarshals into them; the executor delegates to ToTaskRequest(). The 14th, config-apply, has a structural mismatch: seictl wraps the inputs in seiconfig.ConfigIntent rather than flat fields. Solution is a tiny unexported configApplyTask in the controller's task package that anonymously embeds seiconfig.ConfigIntent (whose flat camelCase json tags match today's PlannedTask.Params.Raw shape) and provides three TaskBuilder methods that delegate to client.ConfigApplyTask. Storage shape stable; wire format and validation still single-source. Bumps seictl to v0.0.45 (which includes SetGenesisPeersTask). Other 13 task types (snapshot-restore, configure-state-sync, await-condition, config-validate, configure-genesis, discover-peers, mark-ready, snapshot-upload, generate-identity, generate-gentx, upload-genesis-artifacts, assemble-and-upload-genesis, set-genesis-peers): planner constructs client.*Task directly; PlannedTask.Params.Raw shape shifts from controller-side flat camelCase to seictl-side Go-PascalCase. Cosmetic-only change for operators reading raw plan bytes; runtime behavior is unchanged because Params.Raw is internal to the controller and the wire payload is built fresh from ToTaskRequest() at submit time. Mid-rollover handled by Go's case-insensitive json unmarshal — old camelCase bytes deserialize cleanly into PascalCase Go fields for the flat-shape tasks. ConfigApply preserves shape via the embed. Net -284 lines; 2 files deleted (bootstrap.go, snapshot_upload.go). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment-tidiness pass on the paramser refactor:
- Drop internal/task/genesis.go entirely. It contained only re-exports of
sidecar.GenesisNodeParam and sidecar.GenesisAccountEntry, with a
stale "saves the planner from a second seictl import" justification —
group.go now imports sidecar directly anyway. Re-exports added a layer
of indirection without saving any imports.
- Trim configApplyTask godoc — same load-bearing facts, fewer words.
- Trim Execute()'s Id-override comment to the WHY only (decoupling from
TaskMeta internals); drop the WHAT-narration about UUIDs.
- Drop the registry block's WHAT-narration comment ("typed wrappers come
from seictl/sidecar/client") — the sidecar.X type names already say
this. The configApplyTask exception is documented at its type.
- Trim paramsForTaskType doc to point at configApplyTask for the why.
Net -13 hand-written comment lines, no behavior change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
--new-from-patch flagged the literal "node-0" on line 285 because the patch touched it. The shared testNodeName const already exists at group_accounts_test.go:21; using it here drops one of the 5 package-wide occurrences without touching pre-existing tech-debt sites. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1392640 to
6f598f4
Compare
3 tasks
bdchatham
added a commit
that referenced
this pull request
May 6, 2026
…#193) Pulls in #192 — drops the controller's local taskParamser interface and 14 *Params shells; executor now uses client.TaskBuilder (the seictl interface) directly. ConfigApply uses an unexported configApplyTask that anonymously embeds seiconfig.ConfigIntent for storage-shape stability. Bumps seictl to v0.0.45. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 tasks
bdchatham
added a commit
that referenced
this pull request
May 6, 2026
Picks up seictl#153 — drops the unused TaskMeta + applyMeta from client.*Task wrappers. Side benefit: PlannedTask.Params.Raw bytes shed the "ID":null suffix that came from json-marshaling the empty embed. No controller code changes — TaskMeta had zero references in this repo (the executor sets TaskRequest.Id directly post-ToTaskRequest since #192). Mid-rollover safe: pre-existing CRD bytes containing "ID":null deserialize cleanly into TaskMeta-less structs (Go's json.Unmarshal silently drops unknown keys). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bdchatham
added a commit
that referenced
this pull request
May 6, 2026
Adds e.params.Validate() to sidecarExecution[T].Execute() before the
ToTaskRequest()/SubmitTask path. Catches malformed task params at
executor-time as a structured terminal failure instead of as an opaque
sidecar HTTP 400.
For most task types Validate() returns nil (empty struct or trivial
validation), so this is a no-op in practice. The two strict paths are:
- sidecar.AssembleAndUploadGenesisTask.Validate() — required-field
+ bech32 checks. Already invoked at planner-time at group.go:45,
so this catch is defense-in-depth.
- configApplyTask.Validate() — delegates to seictl
ConfigApplyTask.Validate(), which calls seiconfig.ValidateIntent.
The planner doesn't currently call this; the executor now does.
Drive-by: drop a stale comment about overriding Id "rather than on
the wrapper's embedded TaskMeta" — TaskMeta no longer exists (removed
in seictl#153, consumed in #195). The comment was rot from #192.
Test fixture fix: TestExecuteGroupPlan_CompletesSuccessfully built
an AssembleAndUploadGenesisTask with only Nodes (missing required
AccountBalance and Namespace). It passed before only because Validate
wasn't invoked; now we populate the required fields, which is the
correct test intent for a happy-path submission test.
Closes the platform-engineer's deferred opportunity flagged in the
cross-review of #192.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2 tasks
bdchatham
added a commit
that referenced
this pull request
May 6, 2026
* refactor(task): invoke Validate() in executor before submit
Adds e.params.Validate() to sidecarExecution[T].Execute() before the
ToTaskRequest()/SubmitTask path. Catches malformed task params at
executor-time as a structured terminal failure instead of as an opaque
sidecar HTTP 400.
For most task types Validate() returns nil (empty struct or trivial
validation), so this is a no-op in practice. The two strict paths are:
- sidecar.AssembleAndUploadGenesisTask.Validate() — required-field
+ bech32 checks. Already invoked at planner-time at group.go:45,
so this catch is defense-in-depth.
- configApplyTask.Validate() — delegates to seictl
ConfigApplyTask.Validate(), which calls seiconfig.ValidateIntent.
The planner doesn't currently call this; the executor now does.
One test fixture fix: TestExecuteGroupPlan_CompletesSuccessfully built
an AssembleAndUploadGenesisTask with only Nodes (missing required
AccountBalance and Namespace). It passed before because Validate
wasn't invoked; now we populate the required fields, which is the
correct test intent for a happy-path submission test.
Closes the platform-engineer's deferred opportunity flagged in the
cross-review of #192.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(task): invoke Validate() in executor before submit
Adds e.params.Validate() to sidecarExecution[T].Execute() before the
ToTaskRequest()/SubmitTask path. Catches malformed task params at
executor-time as a structured terminal failure instead of as an opaque
sidecar HTTP 400.
For most task types Validate() returns nil (empty struct or trivial
validation), so this is a no-op in practice. The two strict paths are:
- sidecar.AssembleAndUploadGenesisTask.Validate() — required-field
+ bech32 checks. Already invoked at planner-time at group.go:45,
so this catch is defense-in-depth.
- configApplyTask.Validate() — delegates to seictl
ConfigApplyTask.Validate(), which calls seiconfig.ValidateIntent.
The planner doesn't currently call this; the executor now does.
Drive-by: drop a stale comment about overriding Id "rather than on
the wrapper's embedded TaskMeta" — TaskMeta no longer exists (removed
in seictl#153, consumed in #195). The comment was rot from #192.
Test fixture fix: TestExecuteGroupPlan_CompletesSuccessfully built
an AssembleAndUploadGenesisTask with only Nodes (missing required
AccountBalance and Namespace). It passed before only because Validate
wasn't invoked; now we populate the required fields, which is the
correct test intent for a happy-path submission test.
Closes the platform-engineer's deferred opportunity flagged in the
cross-review of #192.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 tasks
bdchatham
added a commit
that referenced
this pull request
May 6, 2026
Picks up seictl#156 — adds json tags to client.PeerSource. Effect on this repo: PlannedTask.Params.Raw bytes for discover-peers tasks shift from PascalCase (Type/Region/Tags/...) back to camelCase (type/region/tags/...) and drop the omitempty fields, matching the shape operators saw pre-#192. No code changes — controller never references PeerSource fields by struct-tag-sensitive json paths. Mid-rollover safe: pre-existing PascalCase bytes deserialize cleanly into the now-tagged struct via Go's case-insensitive json unmarshal. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Resolves #161.
Summary
Removes the controller-local
taskParamserinterface (toRequestParams(),taskType()) and the 14*Paramsshells that implemented it. The executor now constrainsTtosidecar.TaskBuilder(the seictl-defined interface), so wire format, validation, and task-type strings all flow from a single source of truth in seictl.20 files, -284 net lines, 2 files deleted.
What changed
Executor (
internal/task/sidecar.go)sidecarExecution[T]constraint changes fromT anytoT sidecar.TaskBuildere.params.ToTaskRequest(), then overridereq.Idwith the deterministic plan-task UUID. Override happens at theTaskRequestboundary, not on the wrapper'sTaskMeta— keeps the executor decoupled from wrapper internals (Demeter)taskParamserinterface + 14-entry assertion list deletedRegistry (
internal/task/task.go)sidecarTask[*Params]tosidecarTask[sidecar.*Task]sidecarTask[configApplyTask]— the unexported local type belowsidecarTask,deserializeSidecar) change toT sidecar.TaskBuilderThe one local exception:
configApplyTaskSeictl's
client.ConfigApplyTaskwraps inputs inseiconfig.ConfigIntentrather than flat fields. Direct swap would changeParams.Rawfrom{"mode": "X", ...}to{"Intent": {"mode": "X", ...}}— operationally fine but breaks structural compat for in-flight plans on rollover.Solution: 14-line unexported type in
internal/task/config.gothat anonymously embedsseiconfig.ConfigIntent(whose existingjson:"mode"/json:"overrides,omitempty"/etc. tags match today's controller shape exactly) and delegates the three TaskBuilder methods to a constructedclient.ConfigApplyTask. Storage shape stable; validation and wire format still single-sourced upstream.Planner (
internal/planner/*.go)*task.ConfigApplyParamsnow construct*seiconfig.ConfigIntent(cleaner — same data, fewer copies)sidecar.*TaskdirectlyparamsForTaskTypefactory updated;snapshotRestoreParams/discoverPeersParams/configureStateSyncParamsrenamed to*Taskand return seictl wrappersTests (~10 sites)
Mechanical type-name swaps where tests unmarshal
Params.Rawinto typed structs. No behavior assertions changed.Dependencies
go.mod: seictlv0.0.44 → v0.0.45(picks upSetGenesisPeersTaskfrom seictl#150)Storage shape impact (operator-visible only)
PlannedTask.Params.Rawshape shifts from controller-side flat camelCase to seictl-side Go-PascalCase for 13 of 14 task types. Cosmetic change for operators runningkubectl get snd -o yaml; no programmatic consumer cares — the field is internal to the controller and the wire payload to seictl is built fresh fromToTaskRequest()at submit time. Cross-review confirmed zeroParams.Rawconsumers in either repo outside the controller's own reconcile loop.ConfigApply preserves shape via the embedding pattern.
Mid-rollover safety
In-flight plans persisted under v0.0.44 have
Params.Rawbytes in the old camelCase shape. New controller deserializes them via Go's case-insensitive json unmarshal into the seictlclient.*Tasktypes — works cleanly for all 13 simple tasks (no acronym mismatches). ConfigApply round-trips byte-identically because the embedding pattern preserves the flat shape.Verification
GOWORK=off go build ./...— cleanGOWORK=off go test ./...— all packages passGOWORK=off go vet ./...— cleangolangci-lint run— 0 issuesgrep -rn 'taskParamser\|toRequestParams'— zero residual refsReferences
SetGenesisPeersTasktyped wrapper (the last missing piece)🤖 Generated with Claude Code