Make the Assistant resolve a missing target before asking, not deflect

The system prompt framed ask-first as the only safe behavior for a
missing target ("Missing target information is not a safe default...
ask for the missing target"), so any "run X" / "check Y" request
without an explicit host deflected back to the operator — including on
single-host deployments with no real ambiguity. OpenCode-parity gap:
a competent operator looks first.

New policy: resolve-before-asking. Use read-only query/topology tools
to identify plausible targets; if exactly one plausible target matches,
run read-only diagnostics against it and name it in the answer; ask
only when several plausible targets remain or the action changes state.
Unchanged safety: placeholder targets (current_resource outside an
attached-resource turn) stay forbidden in all modes, never guess an
unresolved target, and write actions still need an explicit target.

Contract: resolve-before-asking entries in Extension Points and Current
State supersede the ask-first framing. Proof:
TestBuildSystemPrompt_CurrentResourceRequiresResourceHandoff pins the
new boundary strings. Live "just run X" behavior check is owed to the
interaction-quality corpus (next unit); dev-instance agent exec is
currently broken (known command-token issue), so it cannot be observed
end-to-end from here today.
This commit is contained in:
rcourtman 2026-06-09 21:12:45 +01:00
parent 36b9b2a53d
commit 945ed2198d
3 changed files with 44 additions and 5 deletions

View file

@ -1284,6 +1284,23 @@ deriving an older display status from `workflowStatusHistory`.
(`internal/ai/chat/service_execute_additional_test.go`) are the regression
proofs that previously-scoped prompt families all reach the model with the
same governed manifest and an unmodified user message.
The system prompt's missing-target policy is resolve-before-asking, not
ask-first: when a command or diagnostic request names no target, the
Assistant must first use read-only query/topology tools to identify
plausible targets, proceed against a sole plausible match for read-only
diagnostics while naming the target in the answer, and ask the operator
only when several plausible targets remain or the action changes state.
The earlier ask-first framing ("Missing target information is not a safe
default... ask for the missing target") made the Assistant deflect every
"run X" request back to the operator — including on single-host
deployments where no real ambiguity exists — instead of investigating
like a competent operator (the OpenCode-parity gap this supersedes).
Placeholder targets remain forbidden in all modes: the model must never
guess an unresolved target or substitute `current_resource` outside an
attached-resource turn, and write actions still require an explicit or
operator-confirmed target.
`TestBuildSystemPrompt_CurrentResourceRequiresResourceHandoff`
(`internal/ai/chat/service_tooling_test.go`) pins this boundary.
Deterministic count-only inventory prompts remain the single Pulse-owned
local answer shortcut, and it is an answer path, not tool selection: when
canonical topology state already carries the complete aggregate counts,
@ -2311,6 +2328,21 @@ deriving an older display status from `workflowStatusHistory`.
## Current State
The Assistant system prompt's missing-target policy is resolve-before-asking
(`buildSystemPromptWithToolGovernance`, `internal/ai/chat/service.go`): a
command or diagnostic request that names no target sends the model to
read-only query/topology tools first; a sole plausible match is used directly
for read-only diagnostics and named in the answer; the operator is asked only
when several plausible targets remain or the action changes state. This
supersedes the ask-first framing ("Missing target information is not a safe
default") that deflected every untargeted "run X" request back to the
operator even on single-host deployments. Placeholder targets
(`current_resource` outside an attached-resource turn) remain forbidden in
all modes, and write actions still require an explicit operator-confirmed
target. `TestBuildSystemPrompt_CurrentResourceRequiresResourceHandoff` pins
the boundary strings; the full Extension-Points entry sits beside the
model-owned tool-manifest rule.
The per-turn Assistant system prompt carries the current wall-clock time (the
Pulse server clock) so the Assistant answers "what time/date is it" directly
instead of deflecting ("I don't have access to a real-time clock") or demanding

View file

@ -3646,9 +3646,9 @@ func (s *Service) buildSystemPromptWithToolGovernance(toolGovernance string) str
- Not every VM or container supports control. Some API-backed platforms are read-only even when the resource type is "vm" or "system-container".
- Write tools are allowed only when the user explicitly asks you to perform an action.
- Status checks and monitoring are read-oriented; do not change state unless the user asked for a state change.
- If a structured clarification tool is offered and you are missing critical information (target, risky choice, preference), use it.
- If no structured clarification tool is offered, ask for missing information in normal assistant text.
- Missing target information is not a safe default. In autonomous mode, ask for the missing target in normal assistant text instead of attempting a tool call with current_resource or another placeholder.
- Resolve a missing target yourself before asking: use read-only query/topology tools to see which hosts, VMs, or containers exist. If exactly one plausible target matches the request (a single connected host, or one resource by that name), run read-only diagnostics against it and name the target in your answer instead of asking.
- Ask for the target only when several plausible targets remain after looking, or when the action changes state. If a structured clarification tool is offered, use it for missing critical information (target, risky choice, preference); otherwise ask in normal assistant text.
- Never guess a target you did not resolve. Do not attempt a tool call with current_resource or another placeholder as a stand-in for a missing target in autonomous mode the same rules apply: resolve with read-only tools first, then ask in normal assistant text if genuine ambiguity remains.
## HOW TO RESPOND
You are like a colleague doing pair programming on infrastructure tasks. Tool calls are your internal investigation the user sees your final synthesized response.

View file

@ -293,8 +293,15 @@ func TestBuildSystemPrompt_CurrentResourceRequiresResourceHandoff(t *testing.T)
"The placeholder current_resource is valid only when this turn includes Pulse resource context",
"either from a resource-context handoff or from Pulse backend resource-reference resolution",
"If no attached resource context is present, do not use target_host=\"current_resource\" or resource_id=\"current_resource\"",
"Missing target information is not a safe default.",
"In autonomous mode, ask for the missing target in normal assistant text instead of attempting a tool call with current_resource",
// Resolve-before-asking: the Assistant must try to identify the target
// with read-only tools and proceed against a sole plausible match for
// read-only diagnostics, instead of deflecting every "run X" request
// back to the user. Placeholder targets remain forbidden.
"Resolve a missing target yourself before asking",
"run read-only diagnostics against it and name the target in your answer",
"Ask for the target only when several plausible targets remain after looking, or when the action changes state",
"Never guess a target you did not resolve",
"Do not attempt a tool call with current_resource or another placeholder",
} {
if !strings.Contains(prompt, expected) {
t.Fatalf("expected current_resource boundary %q in system prompt, got %q", expected, prompt)