fix(cubeops): default AgentHub creates to a registered template - #1334
fix(cubeops): default AgentHub creates to a registered template#1334ENCHIGO wants to merge 4 commits into
Conversation
CreateInstance fell back to the literal "wecom-ds-openclaw" whenever the
request carried neither a snapshot nor a templateId. Nothing in this
repository provisions a template under that identifier: it is not a
tpl-/snap- id, so CubeMaster resolves it through GetTemplateByAlias, and it
only resolves on installs where an operator claimed that alias by hand. On a
self-hosted install every such create therefore fails with
failed to create sandbox: cubemaster error 130404: failed to resolve
template identifier "wecom-ds-openclaw": template not found
naming an identifier the caller never supplied. The Dashboard sends exactly
this request whenever its template selector has nothing to select, which is
the state an install is left in when a template-market registration does not
complete.
Resolve the default from the registered AgentHub templates instead: the
recommended one if there is any — the Dashboard sets recommended on every
market registration, and t_agenthub_template has carried the column all
along — otherwise the most recently registered. The literal stays as a last
resort so installs that do carry the alias are unaffected, and when it is
the identifier that failed to resolve, the caller gets a 400 naming the
missing registration instead of CubeMaster's not-found.
Fixes TencentCloud#1327
Assisted-by: Claude Code:claude-opus-5
Signed-off-by: fengjiaqi <enchiigo@gmail.com>
Review: fix(cubeops): default AgentHub creates to a registered template (PR #1334)AI-generated review — this is an automated assessment, not a human approval. VerdictThe fix is sound and well-targeted. The root-cause analysis is accurate (verified against the base tree: Verified strengths
Notes (low severity)
Nits
Not addressed (acknowledged in the PR description)The |
Two findings from the auto-review on TencentCloud#1334, both fair: - A failed ListAgentTemplates was indistinguishable from an empty registry, so a transient read failure could turn a CubeMaster not-found into "no agent template is registered" — a claim the code had no basis for. The flag now means "the registry was read and held nothing"; when the read itself fails, CubeMaster's own error surfaces unchanged. - The recommended-template preference only scanned the first page, so a recommended template registered before 50 newer ones was skipped in silence, against the documented contract. Pages are now walked until one comes back short, in pages of MaxListLimit, so any realistic registry is still a single query. Tests for both: a recommended template found on a later page wins, and a listing failure stays a 502 rather than becoming the registration hint. Assisted-by: Claude Code:claude-opus-5 Signed-off-by: fengjiaqi <enchiigo@gmail.com>
|
Pushed 9b83692 addressing both inline findings — replies in the threads, summary here:
On the third item (a registered template that CubeMaster cannot resolve — e.g. its backing snapshot was deleted — still surfacing as a 502 naming a Two tests added for the fixes: |
…ance
The auto-review caught a false premise in the previous round: the claim that
the Dashboard marks every market-registered template recommended. It sends
`recommended: true`, but RegisterMarketTemplate parses the field and never
writes it (handler/agenthub.go:522-560 — the INSERT omits the column, schema
default 0), and UpsertTemplateSQL hardcodes it false on the publish path. The
flag is set only by PATCH /agenthub/templates/{id}, which the Dashboard's
per-template toggle calls (web/src/pages/AgentHub.tsx:482).
So the preference is not dead — an operator can mark a template and expects it
to be honoured — but it is set out of band and can sit behind any number of
newer registrations. Walking the registry to find it, as the previous round
did, was both unbounded on the create path and the wrong shape for the
question.
Resolve it in the database instead: store.GetRecommendedAgentTemplate returns
the most recent non-deleted template with the flag set, and the fallback asks
for exactly one row. Two bounded queries, no page window, no loop.
Also drops the claim about market registration from the doc comments — the new
store method documents where the flag actually comes from.
Covered by TestStore_GetRecommendedAgentTemplate against real MySQL: nothing
recommended after registration, the marked older row winning over a newer
unmarked one, and no resurrection after soft delete.
Assisted-by: Claude Code:claude-opus-5
Signed-off-by: fengjiaqi <enchiigo@gmail.com>
|
Pushed 7c40da0. Both new findings addressed, and one of them was a false premise of mine, so flagging it at top level rather than only in the thread: Correction. I justified the recommended-first preference by saying the Dashboard marks every market-registered template recommended. That is wrong. It sends That makes the preference an explicit operator choice rather than a side effect of registration — real, but set out of band, so a marked template can sit behind any number of newer ones. Both findings therefore collapse into the same fix: resolve it in the database. Verified against real MySQL 8.0 rather than only the fake store — Not changed: whether |
…ss code isCMNotFound only matched *CMError, which readResponse produces for a business ret_code in a 200 body. A not-found reported with an HTTP status took the other branch: readResponse returned a bare fmt.Errorf, errors.As(&CMError) failed, and the actionable 400 this PR adds was never produced — the caller got the 502 naming defaultAgentTemplateID that the PR exists to remove. The TencentCloud#1327 repro uses the business code, so the reported case worked and the gap was invisible. - cubemaster: HTTPError carries the status and body for HTTP >= 400 instead of a bare fmt.Errorf. The message is byte-identical, so logs and greps are unaffected, and callers can now classify either shape. - service: isCMNotFound accepts both shapes. - Test pins both, plus a wrapped HTTPError and the non-404 cases. wrapCMError still maps HTTPError to 502 on purpose: widening the service-wide status mapping affects every CubeOps endpoint and belongs in its own patch, not smuggled into this one. Signed-off-by: fengjiaqi <enchiigo@gmail.com>
|
Pushed 85466e6 for the two findings from the last round. One is fixed, one I am deliberately not fixing here — reasoning in the threads, summary: 1. Fixed one level down rather than by string-matching the message:
2. But persisting
|
| return true | ||
| } | ||
| var httpErr *cubemaster.HTTPError | ||
| return errors.As(err, &httpErr) && httpErr.IsNotFound() |
There was a problem hiding this comment.
The CMError branch keys on ret codes (130404/404), but the HTTPError branch keys only on the HTTP status being exactly 404. A not-found that arrives as a non-404 HTTP status carrying ret_code: 130404 in the body (e.g. HTTP 400 + the 130404 envelope) would slip through here and the actionable 400 in CreateInstance would silently stop firing — the exact "silently stop working" regression this helper exists to prevent. Worth parsing the body's ret_code in HTTPError.IsNotFound() (or in isCMNotFound for the HTTPError case) so both shapes are recognised by content, not by status. Low severity since the #1327 repro (200 body, 130404) and a plain HTTP 404 are both covered.
| // could not resolve the fallback either. Report the missing | ||
| // registration rather than a not-found for an identifier the caller | ||
| // never supplied. | ||
| if noTemplateRegistered && isCMNotFound(err) { |
There was a problem hiding this comment.
The rewrite fires on any CubeMaster not-found once the registry was read empty — not specifically a template-resolution failure. In the empty-registry request the only template-ish input is the fallback id, but CreateSandbox also carries instance_type, network_type: "tap", and distribution_scope; a 130404 raised for one of those (e.g. a misconfigured network) would be reported as "no agent template is registered", masking the real cause. Since the error message in the repro already names the fallback identifier, a tighter predicate such as strings.Contains(err.Error(), defaultAgentTemplateID) would scope the rewrite to the case this PR is about while still being strictly better than the old 502. Not blocking — the current broad check is a reasonable default if you'd rather always err toward the actionable hint.
Summary
AgentHubService.CreateInstancefell back to the hardcoded template identifier"wecom-ds-openclaw"whenever the request carried neither a snapshot nor atemplateId. Nothing in this repository provisions a template under that identifier — it is not atpl-/snap-id, so CubeMaster resolves it throughGetTemplateByAlias, and it only resolves on installs where an operator claimed that alias by hand. On a self-hosted install every such create fails withnaming an identifier the caller never supplied. The Dashboard sends exactly this request whenever its template selector has nothing to select.
Fixes #1327 — full analysis, repro and environment there.
What changed
CreateInstanceresolves its default from the registered AgentHub templates: the one an operator marked recommended if there is one, otherwise the most recently registered. Both are single bounded queries —store.GetRecommendedAgentTemplate(new) andListAgentTemplates(1, 0).defaultAgentTemplateIDconstant and stays as a last resort, so installs that do carry the alias are unaffected.400naming the missing registration instead of the502not-found. The rewrite is scoped to that one case: a not-found for a template the caller did name still surfaces as before, and so does one after a registry read that failed rather than came back empty.GetRecommendedAgentTemplate/ListAgentTemplatesadded to theAgentStoreinterface —*store.Storealready implemented the latter.Behaviour
templateIdgivensnapshotIdgivenwecom-ds-openclaw→ 130404cubemaster error 130404: … "wecom-ds-openclaw" …no agent template is registered: register one from the template market (POST /agenthub/templates/market), or pass templateId explicitlyWhere
recommendedcomes fromCorrection to an earlier revision of this description, which claimed the Dashboard marks every market-registered template recommended. It does send
recommended: true, but the server does not persist it:RegisterMarketTemplate(internal/handler/agenthub.go:522-560) parses the field and its INSERT omits the column, andUpsertTemplateSQL(internal/store/dialect.go:139-157) hardcodes it false on the publish path — so registration always landsrecommended = 0.The flag is set only by
PATCH /agenthub/templates/{templateID}, which the Dashboard's per-template toggle calls (web/src/pages/AgentHub.tsx:482). That makes the preference an explicit operator choice rather than a side effect of registration, and it is why this PR resolves it with its own query: a marked template can sit behind any number of newer registrations.Whether registration should persist the flag the Dashboard sends looks like a separate question — persisting it as-is would mark every market template recommended — so this PR leaves that path alone. Happy to file it separately if you would like it changed.
Testing
go test ./...passes inCubeOps/, including the dockertest store suite against MySQL 8.0.TestStore_GetRecommendedAgentTemplate(real MySQL) — nothing recommended after registration; a marked older row wins over a newer unmarked one; a soft-deleted recommendation does not come backTestCreateInstance_DefaultTemplateSelection— recommended wins; most-recent when none is marked; the built-in identifier when nothing is registeredTestCreateInstance_RecommendedIsNotWindowLimited— the registry is not listed at all once a recommended template is foundTestCreateInstance_ExplicitTemplateIDWins— an explicit id is used as-is and the registry is never consultedTestCreateInstance_NoTemplateRegisteredReportsMissingRegistration— the actionable 400, and that the built-in identifier is not leaked to the callerTestCreateInstance_TemplateReadFailureIsNotReportedAsUnregistered— either read failing keeps the 502, so a transient DB failure is never reported as an empty registryTestCreateInstance_ExplicitTemplateNotFoundStaysBadGateway— guards the narrowness of the rewriteThe
130404in the issue was hit on a v0.6.0 single-node install; this branch is covered by the tests above and has not been deployed to that install.Left alone deliberately
docs/guide/digital-assistant.md:17(and thezhmirror) andCubeAPI/scripts/test-cube-api.sh:26also referencewecom-ds-openclawas though it exists. Those look like the same leak but they are documentation/tooling wording rather than behaviour, so they are not touched here — happy to follow up in a separate PR if you want them reworded.Assisted-by: Claude Code:claude-opus-5