The existing FULL DELEGATION manual-QA rule said 'use it yourself' but
left the choice of tool implicit. Make it explicit and non-optional, so
the agent cannot satisfy the gate by reading the source instead of
running the artifact.
Surface → tool mapping:
- TUI / CLI work → interactive_bash (tmux). Launch in real terminal,
send keystrokes, run happy path, try bad input, hit --help.
- Web / browser / UI work → playwright skill. Drive a real browser,
click elements, fill forms, watch console, screenshot if helpful.
- HTTP API / service work → curl or integration script against the
running service.
- Library / SDK work → minimal driver script that imports + executes.
- Other surfaces → ask how a real user would discover it works, then
do that.
Frame the gate as a contract violation when bypassed: reporting
'implementation complete' without using the matching tool is the same
failure pattern as deleting a failing test for a green build.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Existing <verification> required tests pass + lsp clean + build green, but
that is insufficient for end-to-end delegation. Tests cover known cases;
they do not cover whether the user-visible feature actually works.
Add a NON-NEGOTIABLE rule: when the user hands off end-to-end ("ulw",
"implement and finish", "do the whole thing", "make it work", "ship it"),
verification escalates to:
1. BUILD the actual artifact
2. USE IT YOURSELF as a real user would
3. VERIFY end-to-end behavior matches the spec
4. TASK NOT DONE until usage confirms it works
Reporting "implementation complete" without having USED the artifact is
explicitly framed as a contract violation. Defects discovered during this
QA pass are the agent's to fix in the same turn.
This complements the existing 'lsp_diagnostics catches type errors, not
logic bugs' line by giving full-delegation cases a sharper, named gate.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Document the canonical ordering mechanism (Array.prototype.toSorted/sort shim in src/shared/agent-sort-shim.ts) and forbid ZWSP, U+2060, U+00AD, ANSI escape, and ASCII space prefixes. Reference sst/opencode#19127 as the upstream fix that will obsolete the shim.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
The sort shim from the previous commit enforces canonical core ordering at runtime, so ZWSP prefixes are no longer needed. Removing them eliminates the Bun.stringWidth vs terminal-width drift that broke the TUI status bar (#3259).
Drop AGENT_LIST_SORT_PREFIXES and getAgentRuntimeName from agent-display-names; switch all call sites to getAgentDisplayName. getAgentListDisplayName stays as a thin alias for external importers.
Keep stripInvisibleAgentCharacters and the ZWSP regex paths so legacy session state and configs from v3.14.0-v3.16.0 still resolve.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
OpenCode 1.4.x ignores the agent `order` field (sst/opencode#19127), so its
`Agent.list()` sorts purely by `agent.name` via Remeda `sortBy` which uses
native string `<`/`>` comparison. Without intervention, the four core agents
fall into alphabetical order (Atlas -> Hephaestus -> Prometheus -> Sisyphus),
which is not the canonical sisyphus -> hephaestus -> prometheus -> atlas order
the project ships.
Prior attempts to bias the sort key with invisible characters (ZWSP,
U+2060 WORD JOINER, U+00AD SOFT HYPHEN, ANSI escape) all caused
`Bun.stringWidth()` vs terminal-width drift, producing visible gaps and
column truncation in the TUI status bar (#3259, #3238).
Solution: a narrowly-scoped shim of `Array.prototype.toSorted` and
`Array.prototype.sort` that activates only when the array contains two or
more agent objects whose `.name` matches a canonical core display name.
The activation predicate guards against mixed-type arrays so unrelated
`.sort()` / `.toSorted()` calls (string arrays, number arrays, mixed
objects) execute native behavior unchanged. Install is idempotent.
Cubic P1 mitigations from PR #3267:
- `isAgentArray` rejects any array with non-object or null elements,
eliminating the throw-on-mixed-array failure mode.
- Strict activation predicate (>= 2 ranked elements) keeps the global
prototype patch from affecting unrelated sort calls.
Remove this shim once OpenCode honors the agent `order` field
(sst/opencode#19127).
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Discovered via post-implementation review (Oracle goal verification): the
prior commit's prefix swap (ZWSP -> ASCII spaces) inherited a pre-existing
architectural bug from the ZWSP era. `getAgentListDisplayName()` was an
alias for `getAgentRuntimeName()`, which meant every callsite that used
the "list display" name as an OBJECT KEY (config.agent keys, lookup keys,
HTTP-header-bound paths) ended up carrying the sort prefix.
This worked silently with ZWSP because zero-width characters are visually
invisible. With ASCII space prefixes, the same bug becomes user-visible
and violates the explicit RFC 7230 constraint documented in AGENTS.md:
"ZWSP MUST NOT appear in object keys (used as HTTP header values)."
Fix: separate the two concepts that were conflated.
- `getAgentListDisplayName(key)` now returns the CLEAN display name
(alias of `getAgentDisplayName`). Used for object keys, config keys,
and any path where the name will be sent over HTTP.
- `getAgentRuntimeName(key)` keeps its prefixed return value. Used ONLY
for the `.name` field that OpenCode reads for `localeCompare` sort.
`agent-key-remapper.ts` was already correct: it uses `getAgentRuntimeName`
for the `.name` field. The bug was that `getAgentListDisplayName` (used
as the object key) also returned the prefix.
Test updates:
- agent-display-names.test.ts splits the assertions: getAgentListDisplayName
asserts clean names, new getAgentRuntimeName describe asserts prefixes
- All other tests using getAgentListDisplayName as an expected object key
continue to pass because they always wanted clean names
Verification:
- bun test: 5769 pass / 10 pre-existing failures (unchanged)
- bun run typecheck: clean
- Manual: agent-key-remapper output keys verified RFC 7230 safe (no
leading whitespace, no ZWSP); name fields preserve descending-space
prefix for canonical core agent ordering