* docs: close remaining one-way link findings in cli, plugins, tools, providers
Adds the back-links and anchors that PR #143157 did not cover, and gives two
"see below" tables real headings to link to.
- Related back-links: cli/tui -> resume, cli/doctor -> status,
configuration-reference -> configure, voice-call -> voicecall CLI,
onepassword -> secrets CLI, acp-agents-setup -> acpx reference,
llama-cpp -> llama-cpp reference, cli/policy -> policy reference,
ollama -> LM Studio and Memory LanceDB, image/video generation -> OpenRouter,
google-meet -> ElevenLabs, media-understanding -> Mistral,
tts service links -> Fish Audio, tools/secrets -> ask_user.
- manifest/config-and-secrets: H3 headings for the dangerousFlags and
secretInputs detail tables; the two "See below" cells now link to them.
- sdk-overview/capabilities: new "Worker providers" heading; the manifest
worker-provider contract link now lands on it instead of 36 lines above.
- providers/openrouter: the model-list Note pointed at /concepts/model-providers,
which carries no OpenRouter catalog; it now points at OpenRouter's own catalog
and keeps a separate pointer to OpenClaw model selection.
- glossary.zh-CN: 7 sources for the new list-item link labels.
* docs(google): link the Gemini CLI runtime tab to the CLI backends page
r3-2107. `google-gemini-cli` is the CLI backend id the bundled Google plugin
registers, so the tab that configures it should point at the page documenting
its argv, JSONL dialect, and session behavior. The Related card alone left the
tab itself unlinked.
---------
Co-authored-by: Vincent Koc <vincent@openclaw.org>
`docs/cli/policy.md` was 58,576 characters with the whole rule reference
nested under `## Quick start`, so the first `openclaw policy check` command
appeared 497 lines after the section that introduces it.
Children, each a contiguous slice of the original in source order:
- `cli/policy/authoring` - enable the plugin and write `policy.jsonc`
- `cli/policy/rules` - every rule namespace, field by field
- `cli/policy/scopes` - scoped overlays for named agents and channels
- `cli/policy/running-checks` - `policy check`, `policy compare`, plugin config
- `cli/policy/attestation` - evidence, attestation hashes, `policy watch`
- `cli/policy/findings` - check ids, repair behavior, exit codes
`docs/cli/policy.md` stays as a short index. The parent is load-bearing:
`docs/cli/index.md`, `docs/plugins/reference/policy.md` and the
`PLUGIN_DOC_ALIASES` entry in `scripts/generate-plugin-inventory-doc.mts`
all point at `/cli/policy`. Removing it breaks 9 internal links, so it is
kept rather than redirected.
Anchor strategy: per-anchor routes are not expressible (a path is not a
fragment, and `redirectSource()` rejects any source containing `[?#]`), so
all 24 moved anchors stay alive on the parent as authored `<a id="..." />`
stubs that link to their new home. The 2 ids the index still publishes
itself (`openclaw-policy`, `related`) are not stubbed, so no authored ID
duplicates a canonical one. All 26 pre-split ids were enumerated with
`parseDocsDocument` and each was asserted to resolve after the split;
`parseDocsDocument` reports no collisions on the parent or any child.
Losslessness, asserted mechanically rather than by eye:
- the six children, with heading levels restored and the added lede
removed, concatenate to a byte-identical copy of the original body
(sha256 48eccef69a4dfbf6b6abb0ca5f1e5f04ee1caeb09457be057b94d38555d1c420)
- fences 20 -> 20, matched fence-by-fence on info string, body sha256 and
line count; 0 missing, 0 extra
- table rows 157 -> 157
- headings 26 -> 27 (`## Detailed topics` is the only addition)
- words 4,681 -> 4,979 and links 3 -> 33; both additive only (6 card
entries, 24 anchor stubs, 6 one-line child ledes)
- no rule, precedence order or example block was reordered, reworded or
regrouped; the rule namespaces keep the exact order the page presents
One declared exception to "do not rewrite prose while splitting": the
cross-reference "the rule tables below" was orphaned by the split, since
the tables now live on a different page, and became a real link. That is
the entire prose diff - the reconstruction above differs from the original
by exactly one line.
CODEOWNERS: `docs/cli/policy.md` is NOT owned by @openclaw/openclaw-secops.
CODEOWNERS names `docs/cli/approvals.md`, `docs/cli/sandbox.md`,
`docs/cli/security.md` and `docs/cli/secrets.md` individually, and has no
`/docs/cli/` directory rule, so no pattern matches this page under
last-match-wins. The new `docs/cli/policy/` directory is unowned for the
same reason. This PR does not change CODEOWNERS: extending secops
ownership to a security-adjacent page is a policy decision for that team,
not a side effect of a docs split.
Closes audit findings: r3-0110, r3-0111, r3-0113, r3-0114
* fix(policy): align tool requirements with core vocabulary
Use core group expansion and deny matching through a narrow SDK seam; preserve restrictive coverage semantics. Regression proof: 26 expected failures before repair; 332 conformance/doctor tests pass after, alongside 52 core tests and the remaining policy suites.
* refactor(policy): reuse existing SDK policy entrypoint
Keep core vocabulary canonical without growing the SDK budget; retire two unused deprecated projections. Proof: SDK export/surface checks and 33 policy/doctor boundary tests pass; autoreview clean.
* docs(plugin-sdk): clarify tool expansion order
Document first-seen order across entries and catalog order within each group. Proof: formatting and independent review pass.
* docs(plugin-sdk): place exec migration in runtime guide
Keep the typed entrypoint guide limited to typed public package exports. Proof: 19 SDK package contract tests, formatting, and autoreview pass.
* docs(plugin-sdk): scope tool policy migration guidance
Retain the approved export removals while disclosing that the private runtime facade has no packaged TypeScript replacement.
Proof: node scripts/check-changed.mjs -- docs/plugins/sdk-runtime.md.
* refactor(policy): group shared tool helpers in one SDK object
Keep Policy conformance on core vocabulary and deny matching through one typed toolPolicy object on the existing public harness entrypoint. The two unused deprecated mode projections remain retired after the consumer audit.
Shrink the exact SDK export cap from 4447 to 4446 and the callable cap from 2631 to 2629, matching canonical source counts. No new entrypoint or budget increase. Proof: 406 focused and SDK contract tests pass; staged Codex review is clean through P2. Proof commands: pnpm plugin-sdk:check-exports; pnpm plugin-sdk:surface:check; pnpm build. Changed checks reached only the permitted nested-worktree extension declaration boundary. Runtime proof is unchanged by the documentation-only rebase.
* 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.
* refactor(infra): move exec approvals into the shared SQLite state DB
Delete the file-runtime exec-approvals store (exec-approvals.json + .lock
sidecar machinery) on both runtimes and make the reserved
exec_approvals_config singleton row canonical. Doctor owns the one-time
import with claim/verify/receipt discipline; runtime fails closed with a
doctor instruction while un-migrated legacy state exists. The wire CAS
contract, socket semantics, and gateway auth-token derivations are
unchanged. Kills the #113929 lock-contention bug class structurally and
nets around -2.9k lines.
* fix(infra): green CI gates and retire file-era exec approvals tests
Break the migration-type import cycle with a leaf contract, regenerate the
plugin-SDK API and native i18n baselines for the intentional surface change,
drop unused exports, and replace the macOS file-era approvals test suite with
SQLite-backed behavior coverage per the obsolete-internals test policy.
* chore: green max-lines ratchet, native i18n baseline, and unused-export scan
The policy/data-handling-redaction-disabled check could never emit a finding:
scanPolicyDataHandling records the sensitiveLoggingRedaction evidence with a
hardcoded value true, and the finding builder only fired on value !== true.
Redaction is unconditional in src/logging/redact.ts, which hardcodes tools mode
and reads only redactPatterns, so no config can turn it off.
Delete the check, its finding builder, check id, and the validateOnly fix class
that existed solely for it. Keep the public
dataHandling.sensitiveLogging.requireRedaction policy key: it is a policy.jsonc
contract, it still drives openclaw policy compare baseline strictness, and its
shape stays validated.
Make the key's satisfied status explicit instead of silent: the policy rule
declares satisfiedByInvariant pointing at the evidence source policy state
records (oc://openclaw.invariant/logging/redaction), which openclaw policy check
emits in dataHandling evidence and the attestation. A metadata test asserts every
rule names either its checks or its invariant, never both and never neither, and
that policy state actually emits the declared source.
Also drop the stale logging.redactSensitive entry from the policy config coverage
manifest; that config key is retired.
Sensitive log redaction became unconditional and `logging.redactSensitive`
was retired from the config schema, but the policy doctor still classified
`dataHandling.sensitiveLogging.requireRedaction` as an automatic repair that
would have written the retired key back into config.
Remove the automatic repair (check-id registration, patch branch, and the
`enableSensitiveLoggingRedaction` writer), drop the `logging.redactSensitive`
config target, and reclassify the check as `validateOnly`. The check keeps
evaluating the policy declaration against the redaction invariant; it just no
longer claims a fixable config target. Refresh the policy docs, the finding
fix hint, and a stale comment in `src/logging/redact.ts`.
Add exec approvals artifact evidence to Policy.
- add the execApprovals policy namespace and check IDs for required artifact presence, default/per-agent security posture, autoAllowSkills, and allowlist drift
- read the active exec-approvals.json artifact only when execApprovals policy rules are configured, honoring OPENCLAW_STATE_DIR before the default ~/.openclaw path
- emit redacted posture evidence and stable oc:// references without socket tokens, command text, resolved paths, timestamps, or approval-session details
- document the public policy surface and add focused scanner, doctor, conformance, and CLI coverage
Validation:
- GitHub Actions for head b82eefe492 are green, including Real behavior proof.
- ClawSweeper re-review completed for the same head with proof: sufficient and status: ready for maintainer look.
- Maintainer artifact-boundary acceptance is recorded in the PR discussion and body.
Co-authored-by: Gio Della-Libera <235387111+giodl73-repo@users.noreply.github.com>