An initial :skill command submitted with tau --prompt-stdin is rejected as
unknown even though the same project-local skill is discovered and becomes
available to the session during startup. The equivalent command works in the
interactive UI after extension readiness.
The observed event order is consistent with an initialization-order defect in
the --prompt-stdin path: the initial agent's skill context contains only
built-in skills, while later session discovery events contain the requested
filesystem skill. This report does not establish whether the correct fix is to
defer initial agent-context construction, refresh that context, or synchronize
initial-prompt preprocessing with session discovery another way.
tau: tau 0.1.0 (e341951, 2026-08-09 23:02)
commit: e34195176e2f658dd9d84cba7d7bb7ea4ba7f197
OS: NixOS 26.11pre1047536.e72e4f299401 (Zokor)
kernel: Linux 7.1.6
architecture: x86_64
shell: GNU bash 5.3.15(1)-release
observed working directory: /tmp/tau-skill-repro
HOME: /home/master
XDG_CONFIG_HOME: unset
TAU_* environment variables: none
The local checkout and its remote master both resolved to the commit above.
Tau loaded /home/master/.config/tau/harness.yaml. The primary reproduction
below explicitly selects the built-in engineer role. The only local override
to that role is:
agents:
role_groups:
engineer:
roles:
engineer:
effort: highThere is no required_skills setting on this role and no extensions override
in the user configuration. Filesystem skill discovery is supplied by Tau's
default built-in core-shell extension; the session reported it ready. The
failure happens during prompt preprocessing, before any provider request.
I also reproduced the failure with a custom driver role that declares
required_skills: [drive], but the built-in-role reproduction proves that
required_skills is not necessary.
Create a project-local skill with no symlinks or external repositories:
repro_dir=$(mktemp -d)
mkdir -p "$repro_dir/.agents/skills/tau-repro-skill"
cat > "$repro_dir/.agents/skills/tau-repro-skill/SKILL.md" <<'SKILL'
---
name: tau-repro-skill
description: Minimal skill for reproducing Tau initial prompt discovery.
---
# tau-repro-skill
Reply with the skill name and first Markdown heading only.
SKILL
cd "$repro_dir"
printf '%s\n' ':skill tau-repro-skill Smoke test only.' \
| tau --role engineer --ephemeral --prompt-stdinObserved result:
session_id: tau-skill-repro-nk8s5r
role: engineer
error: initial prompt failed (preprocessing): :skill: unknown skill `tau-repro-skill`
The failure was repeatable. The earlier external-skill reproduction also still failed when the first stdin write was delayed by two seconds:
(sleep 2; printf '%s\n' ':skill drive Smoke test only.') \
| tau --role engineer --ephemeral --prompt-stdinThat delay is only one attempted variation; it is not an exhaustive test of non-interactive startup mechanisms.
The initial :skill command should wait for discovery initialization and load
the project-local skill, matching docs/skills.md at this commit:
A new agent's initial
:skillcommand waits for its discovery initialization to finalize before this expansion.
I repeated the minimal reproduction without --ephemeral. The session ID was:
tau-skill-repro-8cgb16
I retrieved and reduced the relevant events with this exact command:
jq -c '
select(.recorded_at_micros >= 1786419380583528
and .recorded_at_micros <= 1786419380595782)
| select(.event_name == "agent.initialization_context_set"
or .event_name == "agent.prompt_queued"
or .event_name == "extension.session_discovery_snapshot_declared"
or .event_name == "harness.session_skills_available"
or .event_name == "harness.notice"
or .event_name == "agent.prompt_failed")
| if .event_name == "agent.initialization_context_set" then
{recorded_at_micros, event_name,
effective_skill_count: (.event.payload.effective_skills | length),
matching_skill_names: [.event.payload.effective_skills[]
| select(.name == "tau-repro-skill") | .name]}
elif (.event_name == "extension.session_discovery_snapshot_declared"
or .event_name == "harness.session_skills_available") then
{recorded_at_micros, event_name,
skill_count: (.event.payload.skills | length),
matching_skills: [.event.payload.skills[]
| select(.name == "tau-repro-skill")
| {name, file_path, source, add_to_prompt,
user_invocable, disable_model_invocation}]}
else
{recorded_at_micros, event_name, payload: .event.payload}
end
' ~/.local/state/tau/sessions/tau-skill-repro-8cgb16/events.jsonlIts verbatim output was:
{"recorded_at_micros":1786419380583528,"event_name":"agent.initialization_context_set","effective_skill_count":25,"matching_skill_names":[]}
{"recorded_at_micros":1786419380585471,"event_name":"agent.prompt_queued","payload":{"agent_id":"M9sEht","text":":skill tau-repro-skill Smoke test only.\n","message_class":"user"}}
{"recorded_at_micros":1786419380592301,"event_name":"extension.session_discovery_snapshot_declared","skill_count":41,"matching_skills":[{"name":"tau-repro-skill","file_path":"/tmp/tau-skill-repro/.agents/skills/tau-repro-skill/SKILL.md","source":null,"add_to_prompt":true,"user_invocable":true,"disable_model_invocation":false}]}
{"recorded_at_micros":1786419380592482,"event_name":"harness.session_skills_available","skill_count":66,"matching_skills":[{"name":"tau-repro-skill","file_path":null,"source":{"kind":"file","path":"/tmp/tau-skill-repro/.agents/skills/tau-repro-skill/SKILL.md"},"add_to_prompt":true,"user_invocable":true,"disable_model_invocation":false}]}
{"recorded_at_micros":1786419380592733,"event_name":"harness.session_skills_available","skill_count":66,"matching_skills":[{"name":"tau-repro-skill","file_path":null,"source":{"kind":"file","path":"/tmp/tau-skill-repro/.agents/skills/tau-repro-skill/SKILL.md"},"add_to_prompt":true,"user_invocable":true,"disable_model_invocation":false}]}
{"recorded_at_micros":1786419380595763,"event_name":"harness.notice","payload":{"kind":"harness.notice","message":":skill: unknown skill `tau-repro-skill`","level":"info"}}
{"recorded_at_micros":1786419380595782,"event_name":"agent.prompt_failed","payload":{"request_id":"ui-create-764044-1","agent_id":"M9sEht","ctx_id":"ui-prompt-764044-2","stage":"preprocessing","message":":skill: unknown skill `tau-repro-skill`"}}No discovery diagnostic was emitted for the project skill. A second discovery snapshot containing the same valid candidate followed the failure; it is omitted above as redundant.
These events establish that the initial agent context did not contain the project skill, while filesystem discovery subsequently found it and the session published it as available. The availability event was recorded before the preprocessing failure event, but the trace does not expose the precise instant at which preprocessing performed its skill lookup. The evidence therefore narrows the defect to startup synchronization between session discovery, initial agent skill context, and initial-prompt preprocessing without proving the exact internal mechanism.
Using the same minimal project, role, configuration, working directory, and environment, I started:
tau --role engineer --ephemeralI waited until Tau displayed ready for all five started extensions:
core-shell, provider-builtin, std-notifications, std-utils, and
std-websearch. I then submitted:
:skill tau-repro-skill Smoke test only.
Before connecting to the provider, Tau directly rendered the preprocessed prompt expansion:
initialized zR3yPT
skills:
tau-repro-skill 1L, 59B
tau-self-knowledge 1L, 235B
64 other session skills available
<skill name="tau-repro-skill" location="/tmp/tau-skill-repro/.agents/skills/tau-repro-skill/SKILL.md">
References are relative to /tmp/tau-skill-repro/.agents/skills/tau-repro-skill.
# tau-repro-skill
Reply with the skill name and first Markdown heading only.
</skill>
Smoke test only.
Connecting to provider…
The model subsequently returned tau-repro-skill and # tau-repro-skill, but
the harness-rendered expansion above is the direct control evidence. This shows
that discovery and user invocation work after interactive startup. It narrows
the failure to initial non-interactive startup without identifying its precise
internal cause.
One hypothesis is that --prompt-stdin constructs the initial agent's skill
context before the default discovery extension has published its session
snapshot, then preprocesses the initial prompt against that earlier context.
Relevant boundaries include completion of extension discovery, construction or
updating of the initial agent context, and the point at which initial-prompt
preprocessing is released.
The interactive control proves successful invocation after readiness, but does not by itself prove that the interactive path constructs the agent at a different startup point.
The confirmed workaround is to use interactive Tau, wait for extension readiness, and then submit the first skill invocation.
I did not find a non-interactive workaround in the cases tested. In particular, delaying the first stdin write by two seconds did not change the external-skill reproduction. I have not exhaustively tested other non-interactive startup or preloading mechanisms.