* fix(auth): keep a retired auth JSON from stranding a migrated store
Runtime failed closed with AUTH_PROFILE_MIGRATION_REQUIRED whenever a retired
credential file was present, even when the canonical SQLite store already held
the agent's profiles. One leftover auth.json therefore made a fully migrated
install unusable, and the gateway lifecycle preflight refused start/restart on
top of it, so every channel and provider stayed offline until Doctor ran.
A legacy file is now only fatal when the canonical store cannot serve
credentials. Doctor's importer never overwrites a usable stored credential, so
a file sitting beside a populated store is unarchived bytes, not pending
migration: runtime logs a one-time warning and keeps serving. An empty store
with a credential file still fails closed and never falls through to
environment auth. Startup degrades that owner to configured-unavailable
instead of refusing to boot, which lets the lifecycle preflight go away.
* refactor(secrets): retire the auth-profiles.json vocabulary
Auth profiles moved to SQLite, but operator-facing surfaces still named the
retired JSON file. The duplicate-agentDir error told operators to copy
auth-profiles.json to share credentials, which does nothing and lands the
second agent in a migration-required state; `openclaw migrate plan codex`
reported a target file that is never created; and the secrets picker labelled
candidates with a filename that no longer exists.
Renames the SecretTargetConfigFile discriminator to "auth-profile-store" and
corrects the operator-facing text, the migrate plan target, and the docs that
described the file as a live target. Genuine legacy-filename uses in doctor,
the security fixer, and migration fixtures are unchanged.
Also deletes resolveSecretPlanTargetByPath and ResolvedSecretPlanTarget from
the plugin SDK. They have no callers in core, plugins, or tests, and the
symbols are absent from the latest stable tag, so they carry no compatibility
obligation and are removed rather than deprecated. Their inline parameter type
was the only thing putting the retired filename on the public SDK surface.
* improve(wizard): warn about device-code phishing
The device-code prompt only warned against sharing the code, and only when an
expiry was known. Device-code phishing works the other way around: the attacker
starts the login and gets the victim to enter the attacker's code. Codes
delivered over a chat channel are the risky case and carry no expiry hint, so
the warning is now unconditional and covers received codes, matching the Codex
CLI prompt.
Also documents the Codex auth handoff: a subscription profile is installed as
in-memory external auth rather than persisted, and token refresh is inverted
so the refresh token stays in OpenClaw's store.
* fix(test): make transcript read-failure injection order-independent
server.sessions.compaction-read-errors.test.ts injected its failures with
mockRejectedValueOnce, which fails the NEXT call to loadTranscriptEvents
globally. Under --isolate=false a shard shares one worker, so any sibling
transcript read could consume the one-shot rejection before the compaction RPC
issued its own; compaction then ran against the real reader and returned ok,
failing three assertions. This shard was already red on main; a prior repair
fixed the mock's initialization order but left the call-order dependency.
Key the injection on the seeded sessionId instead, so unrelated readers cannot
consume it and the re-read case counts only its own session's reads.
Also updates two expectations invalidated by this branch: the duplicate-agentDir
remediation text, and the plugin SDK export ratchet, shrunk by the two retired
secret-plan exports.
* fix(gateway): bound startup history materialization
Apply SQLite byte limits before loading history payloads and share a yielded, fingerprinted Claude CLI snapshot across concurrent startup clients. Preserve marker, cursor, redaction, and external identity semantics, and clarify that shrink-only ratchet updates need no separate approval.
* test(browser): await all lazy command groups
* fix(gateway): bound history snapshot state
* fix(gateway): preserve oversized history responsiveness
Live verification of user-facing behavior is the default before landing,
with skips requiring a stated infeasibility in the PR; captured
screenshots/videos count as evidence only after the agent inspects them
in frame.
The Signal and iMessage channel plugins inferred approval prompts by
regex-sniffing rendered message text (header matching like 'Exec approval
required', /approve command parsing), violating the transport-only channel
doctrine: approval actions must stay typed until channel encoding.
The typed envelope already existed (channelData.execApproval with
approvalId/approvalKind/allowedDecisions) and every payload-level delivery
path consumed it; the regex paths were redundant re-derivation at the raw
send seam plus restart recovery for in-memory iMessage poll state.
- Signal: delete send-level prompt sniffing entirely; all approval sends
already flow through typed structured-payload or native-handler paths.
- iMessage: sendMessageIMessage takes a typed approvalPrompt binding
(id/kind/decisions) from the native approval handler instead of an
approvalKind flag plus text re-parsing.
- iMessage poller: persist pending poll targets in the plugin keyed store
so restart recovery no longer regex-scans chat history; typed recent-chat
discovery for handle-only DM targets stays. Split poll-target ownership
into approval-reaction-poll-targets.ts (max-lines).
- Plugin SDK: remove extractApprovalReactionPromptBinding — beta-only
surface, never in a stable release, so no deprecation window applies;
AGENTS.md now records that rule.
Accepted tradeoff: approval prompts delivered by a pre-upgrade process are
not rediscovered from chat text after restart (<=24h transient state;
persisted reaction bindings and event-driven tapbacks still work).
Three audit lanes over the remaining sections:
- Repair Doctrine/Code/Tests/Start: merge bullets that restate the same
obligation (read-broadly pair, LOC+closeout, verification pair, helper-rent
pair, narrow-API pair, over-engineering pair, test-value trio); relocate the
consolidate-duplicated-setup clause into the test-audit skill's authoring
gate; split the Codex hard gate into its own bullet.
- Architecture: cite CI enforcement instead of restating it (coercion-helper
guard owns isRecord carve-outs); defer additive-SQLite criteria to
docs/reference/database-schemas.md (verbatim coverage); merge the channel
transport-only trio, hot-path pair, and process-stable/freshness trio;
every approval gate untouched.
- Platform/Ops: delete the three deployed-agent bullets (generated-media wake,
message_tool_only, memory wiki) — deployed agents read runtime prompts and
docs, not this repo's AGENTS.md, and docs/tools/media-overview.md,
docs/gateway/config-channels.md, and the memory-wiki plugin surfaces cover
every clause (wiki-maintainer skill gains the verify-contact-data line);
move SwiftUI Observation + provider-schema bullets to Code; compress Mac
signing to its doc pointer.
- ClawSweeper: 13 wording compressions; every schema field name and gate kept.
Adversarial no-loss audit ran over the diff; its four findings (external-API
live-test weakened by a feasibility qualifier, dropped orchestration-only
clause, existing-vs-obsolete abstraction flip, oversold channel-doc pointer)
are all restored/fixed.
* docs(agents): fact-check, disambiguate, and further slim root AGENTS.md
Second cleanup pass over root AGENTS.md driven by three audit lanes
(fact-check vs repo, confusion/contradiction hunt, cut candidates):
- Fact fixes: delete the @buape/carbon pin rule (dependency no longer exists
in any manifest or pnpm-lock.yaml); fix the reply-format example to a real
file (extensions/telegram/src/bot-access.ts).
- Confusion fixes: define checkout classes once in Commands and merge the
three conflicting test/check bullets onto them; state that the mock-gateway
harness verdict satisfies channel-visible proof as an explicit exception to
mandatory UI screenshots; complete the Map scoped-guide list (src/tui, test)
and note deeper subtree guides; inline the dangling 'Start-section evidence
bar' reference; gate issue-closing and public-comment posting on explicit
authority; scope the NO_REPLY rule to ClawSweeper hook sessions; clarify
auto-provisioning vs Architecture's compat-fallback deletion rules.
- Cuts/moves: scripts/pr gotchas, throttle-lock recovery, non-main merge
procedure, media-upload mechanics, and merge-ref-race recognition into
$openclaw-pr-maintainer; QA private build flag into $openclaw-qa-testing;
release-branch CI dispatch refs and release-ci-summary limits into
$release-openclaw-ci; Vitest ENOTEMPTY race into $openclaw-testing;
WebVNC screenshot etiquette into $crabbox; delete bullets duplicated by
those skills (prompt snapshots, QA YAML) and the obsolete rebrand-doctor
bullet; compress the preflight, Codex gate, SQLite-additive, ClawSweeper,
and Execution Identity wording without dropping any invariant.
* docs(agents): exempt extended-stable canonical dispatch from the target_context_ref rule
* docs(agents): dedupe root AGENTS.md and move workflow detail into owning skills
Root AGENTS.md loads into every session; ~9.5KB of it duplicated content
already owned by skills (crabbox, openclaw-testing, release-openclaw-*,
openclaw-pr-maintainer, openclaw-changelog-update) or restated the same
invariant in multiple sections. Skills absorb the root-only operational
details first (Testbox/Crabbox mechanics, backport default target, fork-code
landing variant), then root keeps hard policy and routing only.
Also fixes skill drift found during the audit: positional testbox status id,
missing remote-unavailable local-fallback policy, delegated-flag reject list,
and aligns the early performance dispatch fail_on_regression flag with the
Full Release Validation profile gate (true stable / false beta).
* docs(agents): derive fail_on_regression from release profile in dispatch example
Red main, a red merge gate, and flaky-by-construction assertions were
treated as someone else's problem often enough to be worth a rule: fix
them in the landing PR rather than waiting them out or routing around
them. Keeps the two real exceptions explicit so the default cannot be
argued away.
Replace assertion-driven cleanup with real runtime narrowing and typed owner contracts across memory-core dreaming.
Reject the unsound one-file lint ratchet and document repository-wide rules against checker-gaming and baseline-driven enforcement.
Co-authored-by: Amp <amp@ampcode.com>
Adds hard policy: UI-visible changes need before/after screenshots or a
short video as PR evidence; gateway-behavior changes provable in the
Control UI need an isolated dev-gateway live run with a recorded video.
Documents the verified video upload flow (user-attachments endpoint,
mp4/webm both served) and the bare-URL embed rule for GitHub's player.
- Rename openclaw-test-audit skill to test-audit; add an authoring-gate mode
invoked whenever tests are written, not only during sweeps.
- Repair Doctrine: capture the failing repro before editing; regression tests
must fail on pre-fix code. Bug fixes default to net <=0 production LOC via
the refactor that absorbs the fix, not a bolted-on guard.
- ClawSweeper policy: production-vs-test LOC reviewMetrics entry is now
unconditional for code PRs, counted with judgment; net-positive bug-fix
deltas become risks findings with bestSolution naming the absorbing refactor.
Replace the artifact-store-first guidance with the direct
uploads.github.com/user-attachments endpoint (same CDN as drag-and-drop,
works with gh auth token); Crabbox artifact publishing stays as the
fallback for non-media artifacts or endpoint failure.
The no-round-trip clause from #111385 covered the unchanged-head case; once rank-up moves are applied the old comment attests stale code. Head unchanged: land without re-review, as before. Head changed after review: one exact-head re-review, land when no actionable finding and no remaining rank-up move.
* feat(harness): report copilot code-mode engagement on the attempt result
* test(copilot): prove code-mode engagement through the production tool bridge
* docs: describe the normalized codeModeEngaged value for native harnesses
* docs(agents): standardize canonical record guards
* refactor(model-catalog): use canonical record guard
* refactor(scripts): use canonical record guard
* refactor(ui): use canonical record guard
* refactor(codex): use canonical record guard
* refactor(agents): use canonical record guard
* refactor(sessions): use canonical record guard
* refactor(doctor): use canonical record guard
* refactor(infra): use canonical record guard