30 Commits
Author SHA1 Message Date
Peter Steinberger 3e88e97f9f chore(deps): update fs-safe to 0.21.1 (#159882)
* chore(deps): update fs-safe to 0.21.1

* test: satisfy archive collision fixture lint

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-09-27 16:11:51 -07:00
Peter Steinberger 87f2e6371b fix(plugins): stop deleting sources after cleanup is revoked (#159563)
* fix(plugins): keep cleanup under source ownership

* test(plugins): model managed config activation during cleanup
2026-09-27 10:42:55 +00:00
Patrick Erichsen 2d5c68187a fix(plugins): allow reloads to wait for long-running work (#158688)
* fix(plugins): allow reloads to wait for long-running work

* test(plugins): await reload drain readiness without polling

* test(sessions): await resource retirement before reopening cleanup fixtures
2026-09-26 08:08:14 +00:00
Peter Steinberger 2a1402cd3e fix(startup): keep legacy state repair in Doctor (#151025)
* fix(startup): keep legacy state repair in doctor

Separate current-state startup readiness from explicit Doctor migrations.
Preserve current config recovery, quarantine, device identity checks,
lease fencing and updater completion ownership.

Remove automatic startup migration/checkpoint and node-host import paths.
Keep shipped read-only context and roster projections unchanged.

* test(doctor): align proof callers with repair ownership

Keep survivor fixture setup in caller order and select the shared Doctor
flow separately from the channel-specific proof. Include the complete
isolated diagnostics dependency closure.

Prove ordinary startup preserves legacy directories before explicit
Doctor repair, and await SQLite worker closure before test cleanup.

* test(startup): verify index repair through gateway maintenance

* test(doctor): align cutover fixtures with state owners

Reacquire the database after Doctor retires its generation, retain real session history for startup refusal, and keep pending reads at their actual owners. Distinguish the reused readonly reader from independent snapshot-token children and verify every child settles.

* fix(doctor): preserve image activation and published migration receipts

Run the shared noninteractive Doctor owner before default and Compose Gateway
activation so retained Docker volumes keep their published upgrade path while
ordinary Gateway startup stays readiness-only. Preserve root selectors and
settle interrupted repair before executing the original command.

Retain the path-wide tombstones emitted by the published restart-sentinel
importer, including consumed notices, and validate completed source decisions
before retiring recreated inputs. Add the root-image activation lane and causal
receipt coverage without introducing a schema, option, or second importer.

* test(docker): preserve release state pairs in upgrade proof

Use the existing synthetic v2026.9.2 corpus instead of combining a July shared database with a later agent schema. Preserve its deletion journal, registry, both transcript payloads, paired backups, and unsafe-state controls. Register the two shell-launched helpers with Knip.

* test(docker): normalize persisted schema snapshot rows

Compare node:sqlite schema rows using the same plain-object representation as the saved JSON preimage. Keep strict schema, row, and value-type assertions intact.

* fix(models): retain discovered models after refresh failures

Record successful legacy catalog results at the producer boundary so unavailable refreshes retain the accepted inventory. Preserve explicit outcomes, advisory SDK fallback behavior, and first-discovery starter policy.

* fix(models): preserve skipped catalog outcome semantics

Mark bundled static, configured, and advisory catalog projections with
explicit empty outcomes so legacy success inference cannot promote them
to observed account inventory. Preserve live outcomes and helper types.

Keep exact auth provenance histories and move existing fixture/policy
code into focused owners where required by the line-cap ratchet.

Validation: 447 producer and sibling cases, 56 shared self-hosted cases,
95 auth/policy cases, causal missing-outcome failures, maintained checks,
and independent review.

* test(plugin-sdk): keep discovery loader types acyclic

Move the shared loader type into a leaf consumed by both discovery
contract helpers. Preserve its public provider-test-contracts export
without a child-to-parent type import cycle.

Validation: maintained Madge check reports zero cycles; core, all core
test graphs, extension test types, lint, formatting and independent
review pass. Runtime behavior and previous catalog proof are unchanged.

* fix(plugin-sdk): mark generated static catalogs explicitly

Keep the generated non-live, non-strict catalog adapter from claiming
successful acquisition for manifest or configured rows. Preserve null,
errors, strict and custom callbacks, static catalogs, and public types.

Validation: three existing controls fail before the correction; all49
owner and sibling cases pass afterward, with types, lint, line caps and
fresh independent review clean.

* test(gateway): cover restart import during state retirement

Exercise canonical database close while a legacy notice read is paused. Verify admission rejection, retained canonical and source bytes, no migration receipt, and joined maintenance custody without changing the accepted sidecar-stop drain contract.

* fix(test): drain sharing fixtures before removing state

* test: bind retirement regression to its own worker

* docs(docker): clarify automatic Doctor activation

* test(doctor): keep readiness fixture runtime private

* test: stabilize shared skill watcher fixture roots

(cherry picked from commit 15606be10e)

* perf(tooling): share indexed scope parsing for artifact scans

(cherry picked from commit 6e6eef9f5b)

* fix(team-reports): use source owners in scheduler tests

The source barrel retired in #157819, but the scheduler tests still imported
it, breaking the extension test typecheck on main. Import the Discord and
GitHub owners directly, matching the production caller.

Validated the original TS2307/TS7006 failure, the corrected extension type
graph, all 36 scheduler tests, changed checks, and independent P2 review.

(cherry picked from commit 7c4866c73b)

* test(install): isolate global npm configuration in version fixtures

Use a controlled absent global config inside the fixture home so the
release helper does not query the deliberately narrow npm stub. Keep
predecessor selection, fresh-install behavior, and expected exits intact.

(cherry picked from commit 0a9eefc76c)

* test(docker): verify the complete image activation entrypoint
2026-09-25 13:43:40 -07:00
Peter Steinberger 1ae730f675 fix(plugins): repair archive plugin host links after upgrades (#158002)
Include registered archive installs in the existing host-link repair owner so Doctor and post-update convergence can recover stale or dangling SDK links without the original archive. Preserve canonical install-root, path ownership, and lifecycle authority checks.
2026-09-25 01:56:26 -07:00
Peter Steinberger 88fff4ed5a perf(plugins): admit reloads while retained work drains (#157796)
Reserve busy plugin replacements before gating new agent work, then let
admitted runs finish on their original instance before teardown. Report
queued retained work and completed publication through existing diagnostics.

Preserve the 60-second pre-stop budget, timeout recovery, and shared-resource
cleanup before replacement registration. Cover overlapping runs, installed
version publication, and retained cleanup without introducing configuration.
2026-09-25 02:35:18 +00:00
stevenlee-oai 6a8547cfcc fix: preserve plugin restrictions during managed installation (#152020)
* fix: preserve plugin policy during managed installation

* test: fix plugin install policy CI coverage

* test: route plugin install policy through isolated CLI suite
2026-09-24 15:15:41 -07:00
Peter Steinberger c8eab36020 refactor: run plugins and tooling without TypeScript 6 (#156829)
* refactor: remove TypeScript 6 from plugin runtime and tooling

Keep source plugin loading on Jiti and native module transforms, move development analysis and declaration builds to TypeScript 7, and preserve captured loader and input ownership contracts. Scaffolds compile to the same entry path without shipping a compiler in production.

* refactor: use native checker for restart preflight detection

* fix: keep test-directory docs out of native helper scans

* fix: preserve native compiler tooling across CI runtimes

Keep compiler-backed tests on the supported Node transport, preserve declared Windows aliases and partial-source analysis, and bind relocated SDK reports to their installed compiler. Retain strict package input admission and the existing fixture assertions.

* test: compare messaging guard diagnostics without sorting

* fix: bound native tooling memory and preserve bootstrap loading

* fix: bound native declaration builds with tsdown scheduling

* fix: retire compiler-only helpers from installed packages

* test: migrate routing import scan to native parser

* test(ui): wait for recovered model catalog receipt
2026-09-23 22:34:20 -07:00
Peter Steinberger d9d8f0829d fix(plugins): show the selected entry after reload (#156902)
* fix(plugins): show the selected entry after reload

Expose the loader-owned selectedEntries map in runtime receipts. Add bounded CLI and tool guidance to rebuild compiled output after source edits while preserving success and restart outcomes.

* fix(protocol): sync generated plugin reload receipt

Regenerate the protocol outputs for optional selectedEntries. The Swift model gains the field, default-nil initializer argument, and wire coding key; Kotlin outputs remain unchanged.
2026-09-24 02:16:04 +00:00
Peter Steinberger 525970204a fix(plugins): report reloads that still require a Gateway restart (#156827)
* fix(plugins): report reloads that still require a Gateway restart

Project captured-source facts into plugin runtime receipts so compiled bundled
registrations cannot imply that edited code was swapped. Preserve the restart
requirement through Gateway responses, CLI batches, and compact tool guidance,
while retaining live generation and captured source digests.

Keep external workspace code on the existing generation replacement lifecycle.
Extract the lazy reload command and receipt construction without changing drain
or recovery ownership. Cover unchanged bundled code with a restart-required
regression and verify real CLI/tool reloads on isolated Gateways.

Fixes #156699. Thanks @Loganwoooof for the digest-based proof.

* fix(plugins): avoid restart warnings for unchanged code

Record the original artifact fingerprint with shared module identity. Report a restart for explicitly reloaded code only when a retained module has changed or unverifiable files, while preserving successful channel ownership hand-offs and recovery facts.
2026-09-24 00:36:56 +00:00
Peter Steinberger de0d340597 fix(plugins): verify ClawHub archive files by extracted names (#156322)
## What Problem This Solves

Legacy ClawHub ZIP archives can be rejected when the separate preflight parser and the extractor interpret their filenames differently.

## User Impact

ClawHub verifies the extractor's canonical filenames and SHA-256 hashes before installation. A native-backed CP437 archive that previously failed now installs when its files match the advertised metadata. Harmless backslash aliases may normalize to those exact paths, and root-only records that produce no output are ignored. Unsafe paths, collisions, missing or changed files, extra files, named unsupported records, and archive limits remain enforced.

## Why This Change Was Made

The released extractor's existing entry callback supplies the complete canonical inventory, including named records that extraction cannot materialize. Verification hashes the observed regular files in the fresh, unstripped extraction workspace and retains unsupported records without a digest so they cannot disappear from integrity checks.

This removes the second ZIP read/parser and the later directory walk, reducing production code by 48 lines without adding a library API or changing configuration or stored formats. Server-provided paths and generated `_meta.json` validation remain strict. The install documentation records the canonical-name behavior.

## Evidence

- The real native-backed installer CP437 case failed before this change and passes afterward. Synthetic archive proof also covers canonical aliases, inert root records, named unsupported entries, complete inventory/hash matching, unsafe paths, archive limits, cleanup, and installation authority.
- Focused proof passed 146 tests in 33.20 seconds wall time. The changed ClawHub suite measured 107 tests in 23.77 seconds wall time; the final CI repair rerun passed the same 107 tests in 38.04 seconds of wrapper time.
- All 14 affected checks passed in 181.69 seconds. The assertion-safety baseline shrinks exactly from 7 to 5; no boundary waiver or policy exception was added.
- CI identified a facade export retained only by a negative test spy after its production caller was deleted. Both are now removed; the real installer assertions for single extraction, lost authority, absent persistent installation, and cleanup remain. The exact full production and all-export dependency scans now pass, along with affected type, format, and lint checks.
- Fresh independent scoped reviews found no actionable findings. Lead review checked the released extractor's planning/publication guarantee and the final implementation.
- Proof uses synthetic archives and the actual installer on macOS. No live ClawHub service or Windows execution is claimed.
2026-09-23 07:42:46 -07:00
Peter Steinberger 347133a9b4 fix(plugins): report incomplete installs before capability consent (#154011)
* fix(plugins): report incomplete installs before capability consent

* fix(plugins): repair install-health test and type boundaries

* fix(doctor): preserve recorded plugin recovery selectors
2026-09-21 05:30:24 +00:00
Peter Steinberger 0351056b60 fix(gateway): keep the previous plugin generation serving when a replacement stalls on admitted work (#151273)
A plugin generation replacement stopped the previous plugins' services and
channels before it learned that a plugin's admitted work would never settle;
the stop failure then disabled recovery and left the old generation
half-stopped with a stale model runtime, so unrelated Gateway requests
returned UNAVAILABLE indefinitely (team.openclaw.ai, 2026-09-17 22:10 UTC).

Wait up to 60 s for the replaced plugins' admitted calls before anything
irreversible, fail once through the existing pre-stop rollback when they do
not settle, admit channel account monitors as retained consumers like plugin
services so the call-only wait cannot block on them, and republish the
prepared model runtime after every uncommitted failure.
2026-09-18 12:52:09 -07:00
Peter Steinberger 5a1f923aa8 fix: updates fail for plugins selected from local load paths (#151851)
Fix updates and repair aborting during plugin finalization when plugins.load.paths selects a local copy over an npm installation. The plugin ownership resolver classifies that source as operator-managed for maintenance, preserving both the selected payload and its shadowed install record while recording an actionable plugin-operator-managed warning in the outcome and update history.

Channel startup recognizes typed plugin-trust refusals and records the existing terminal state instead of repeatedly retrying. Doctor and update status expose the source and supported installation remedy. Package ownership and plugin trust requirements remain unchanged.

Published OpenClaw 2026.9.4 driving the candidate completed plugin finalization with a persisted warning and unchanged local/npm payloads. The subsequent service refresh failed in a separate dependency-resolution path; complete update success and fixed live repair/channel startup remain unclaimed. Exact-head CI run 35360153214 is green, and all three rebased commits are patch-identical to the scoped-clean review of record.

Fixes #151794. Related: #151795.
Thanks @WhitenessTiger for the reproduction and ownership/trust analysis.
2026-09-18 09:44:17 -07:00
Peter Steinberger c928f7abbf fix(cli): avoid duplicate catalog version prefixes (#145975)
Reuse node version labeling for marketplace list, plugin search, and skills search so already-prefixed versions and opaque build names keep their labels. Numeric versions still gain v; JSON values and install references stay unchanged.

Move marketplace listing and skills search into their own command modules, removing the superseded inline implementations while preserving lazy loading, parent JSON options, and skills text sanitization. Extend command-boundary regressions and retain node status/describe coverage.

Final proof: 185 focused tests, the complete selected gate, fresh P0-P2 review, and six actual CLI calls passed. All three JSON outputs were byte-identical to baseline.

Refs #145975.
2026-09-17 18:26:18 -07:00
Peter Steinberger f7fe1564fc fix(plugins): exclude scaffold tests from published packages (#129393)
* fix(plugins): exclude scaffold tests from published packages

* fix(plugins): preserve feature compilation and verify scaffold packages
2026-09-16 18:27:47 -07:00
Peter Steinberger 355181c0af feat(plugins): manage multiple plugins in one CLI command (#149910)
* feat(plugins): manage multiple plugins in one CLI command

* test(plugins): split multi-ID uninstall coverage

* fix(plugins): wait for rejected lifecycle admission in CLI batches

* fix(gateway): capture candidate plugin records before watcher checks
2026-09-16 04:10:52 -07:00
Peter Steinberger 8f7c272397 perf(plugins): batch cloud worker plugin activation (#149824) 2026-09-16 00:11:19 -07:00
Peter Steinberger 98b7cf7e58 fix(update): preserve linked plugins during post-core sync (#147009)
Preserve explicitly linked path-source plugins during post-core stable and dev synchronization, including their selected source, install record, configuration, and payload. Keep path records out of package ownership reconciliation through the existing update-source predicate, while retaining strict checks for managed package updates. Record retained-link warnings in post-core status and logs.

The repair uses the candidate-side post-core path invoked by the released updater. Regression evidence covers real discovery and post-core convergence, with failing-before cases and 266 passing focused tests. Full published-driver upgrade and native service restart qualification remain outside this landing's proof.

Fixes #146958.
Related: #146959, #145252, #142681.
Reported by @obviyus (#146958).
2026-09-13 06:32:47 -07:00
Peter Steinberger a9d273dc5f fix(plugins): preserve update failures and prevent duplicate settlement (#146483)
Preserve the original plugin update failure when rollback also fails, keeping rollback errors as additional diagnostics. Consume completed managed install transaction handles and share successful or concurrent settlement, while retaining retryability after failed rollback in the existing recovery owner.

Regression evidence covers primary-error propagation through update finalization and the CLI, handle consumption across managed install sources, and duplicate/concurrent settlement. The authoring lane recorded failing-before/passing-after regressions and 356 passing focused tests; exact-head CI passed. Published-driver × candidate installation smoke remains a disclosed validation gap accepted for this scoped landing.

Fixes #146435
Refs #146434
Reported by @aniruddhaadak80 (#146435, #146434).
2026-09-12 15:17:37 -07:00
Peter Steinberger cf9cbef81c feat: refresh plugin tools in managed Codex conversations (#145917)
* feat: refresh plugin tools in managed Codex conversations

* test: wire native plugin refresh coverage into CI
2026-09-12 04:39:59 -07:00
Peter Steinberger e625b673e7 fix(plugins): recover reloads and release retired resources (#145816)
* fix(plugins): recover reloads and release retired resources

Restore native Bun dependency resolution, runtime-only refresh, channel
status and cold-account handling. Return contention before Gateway plugin
or Claw-package mutations start, retaining receipts for later failures.

Keep slow service startup cleanup with its lifecycle owner, restore
LanceDB service state on rollback, and retain CLI cleanup warnings.
Remove obsolete generation retirement and duplicate package resolution.

Related: #145484, #145649

* test(gateway): verify startup capability reconnects with real sockets

* fix(agents): release queued preparation before gateway retirement

* fix(plugins): preserve cleanup ownership and machine-readable reloads

* refactor(gateway): group reload recovery proof by lifecycle owner

* fix(plugins): preserve require context on older Bun runtimes

* fix(agents): return explicit queued cleanup completion
2026-09-12 03:27:47 -07:00
Peter Steinberger 6e259355b8 feat: apply supported plugin installs without restarting the Gateway (#145710)
* feat: apply supported plugin installs without restarting the Gateway

* fix: preserve read-only plugin reload and inspection fixtures

* fix: preserve plugin recovery and policy metadata
2026-09-12 01:56:54 -07:00
Peter Steinberger b5134de70a feat: manage and reload plugins without Gateway restarts (#145484)
* feat: manage and reload plugins without Gateway restarts

* fix(plugins): preserve Bun loading and align lifecycle fixtures
2026-09-11 23:12:58 -07:00
Peter Steinberger fe056539fd fix(plugins): keep rebuilt bundled plugins on git installs instead of npm refreshes with a mismatched SDK (#145356)
Git/dev installations keep the plugin rebuilt from the running source checkout instead of refreshing a same-version npm build with a different Plugin SDK. Doctor records why the registry artifact was not admitted.

Manifest-registry selection owns bundle precedence and dormant install detection. Named plugin updates, --all, and stable/beta release-cohort convergence carry that decision through package-mutation preflight while retaining strict checks for active packages. Existing npm records remain available, explicit plugin paths retain priority, and package-host updates retain their registry behavior.

Fixes #145266

Thanks @DonnieFi for reporting the artifact skew and providing the reproduction evidence.
2026-09-11 21:50:11 -07:00
Finn763andShakker b6073553b7 fix: recover managed plugin release pins during updates (#133884)
Recover eligible managed plugin release pins and keep individual plugin retry notices separate from successful OpenClaw updates.

Refs #133810, #135776.
Reviewed-by: @shakkernerd
Co-authored-by: Shakker <165377636+shakkernerd@users.noreply.github.com>
2026-09-12 02:46:16 +01:00
Vincent Koc 4bc64e7957 docs(cli): state present bare-spec install behaviour without the launch cutover (#143867)
`docs/cli/plugins/install.md` still described bare plugin specs as installing
from npm "during the launch cutover". There is no cutover: `grep -rni cutover src/`
returns nothing, no flag, date or feature gate exists, and no release note announces
an end. The phrase entered in cf21bcf9bf, which deleted the ClawHub-first
resolution path outright rather than scheduling a migration. Present behaviour is
`isOpenClawTrustedPluginInstallSpec` in `src/plugins/install-provenance.ts`.

Drops the time-relative clause and leaves the behaviour statement, matching the
phrasing already used in docs-audit/fix-accuracy-plugins for the six pages under
`docs/plugins/`.
2026-09-10 16:35:36 +08:00
Vincent Koc f575b7f4ef docs(cli,tools): fix information-architecture findings in CLI and tools pages (#143775)
Structure-only changes: no prose is added or reworded beyond the new
heading titles themselves. Every published anchor id is preserved.

- cli/doctor/checks.md: split the 42-bullet `## Notes` list into nine
  H3 groups so individual checks are addressable.
- cli/doctor/sqlite-maintenance.md: sentence-case the one Title Case H3
  (slug unchanged).
- cli/mcp/registry.md: move `Saved MCP server definitions` directly after
  the intro that introduces its command list; `Codex tool approvals` now
  follows it.
- cli/plugins/install.md: promote the nine collapsed accordions to H3
  sections (they carry scope gates, `--force`/`--pin` semantics and
  install-policy rules), lift the source/locator prose above them, and
  head the trailing local-path run.
- cli/plugins.md: add the body H1 used by the other CLI command pages.
- cli/update/how-updates-run.md: add per-topic H4s inside
  `Restart handoff`, promote `Plugin sync details` out of
  `Git checkout flow`, and head the package-manager install text that was
  sitting inside the Git checkout section.
- cli/onboard.md: give the flag list its own `## Flags` heading and demote
  `Additional non-interactive flags` under `Non-interactive setup`.
- cli/approvals.md: move the `openclaw exec-policy` section after
  `Common options` so the page opens on the command it is titled for.
- cli/infer.md: move the "turn infer into a skill" how-to off the top of
  the reference page.
- cli/index.md: file `devices` with pairing/channels to match the sidebar,
  and name the `automations` alias on the cron row.
- tools/tts/field-reference.md: drop the stray H3 nested inside the Inworld
  accordion (its id is kept as an authored anchor) and lift the SecretRef
  paragraph out from between two accordions.
- docs.json: drop the duplicate `tools/tts` nav entry, put browser
  agent-tools before troubleshooting and existing-session beside profiles,
  file `cli/wiki` with memory/models and `cli/openclaw` with onboard/setup.
2026-09-10 15:41:30 +09:00
Paul FrederiksenandVincent Koc 895bdee99e fix(plugins): report newer releases for pinned ClawHub installs (#137892)
* fix(plugins): report newer releases for pinned ClawHub installs

* fix(plugins): keep pinned formatter internal

* fix(plugins): normalize leading-v ClawHub update probes

* fix(plugins): preserve literal ClawHub install selectors

* style(plugins): format ClawHub diagnostic cases

---------

Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
2026-09-10 00:52:38 +09:00
Vincent Koc 20e0ed521c docs(cli): split the plugins command reference by reader job (#142980)
docs/cli/plugins.md was 55,872 characters and 11 H2 sections mixing a CLI
synopsis, an authoring how-to, install-policy explanation, lifecycle
reference, diagnostics, and marketplace feed trust. It is now a short index
over six children, one per reader job:

- cli/plugins/authoring             Author, Feature scaffold, Provider scaffold
- cli/plugins/install               Install, Marketplace shorthand
- cli/plugins/list                  List, Plugin index
- cli/plugins/uninstall-and-update  Uninstall, Update
- cli/plugins/inspect-and-diagnose  Inspect, Doctor, Registry
- cli/plugins/marketplace           Marketplace

The index keeps the intro, the CardGroup, the full `## Commands` synopsis with
its trace/Nix/bundled notes, and `## Related`.

Anchor strategy: per-anchor redirects are impossible, because redirectSource()
in scripts/lib/docs-redirects.mjs throws on any source containing [?#]. Every
original anchor therefore stays alive on the parent index as an authored
<a id="..." /> stub pointing at its new home. Ids were computed with
parseDocsDocument, not a slug approximation, so the nine Accordion titles, the
two Tab titles, and the three ParamField ids are covered alongside the
headings. 34 pre-split ids: 32 stubbed, 2 (commands, related) still published
by the index itself, so no duplicate authored/canonical ID is raised. All 34
verified to resolve on /cli/plugins, and all 32 stub targets verified to
resolve on their child page. Collisions are empty on the index and on each of
the six children.

Losslessness, asserted mechanically against the pre-split file:
- index prefix + the six children (frontmatter and lede removed) + index
  suffix reassemble byte-identically, sha256
  c462b672a341344fca6dc46d623f6136f337844080b97bd469faffacb12265f8
- fences 17 -> 17, every one identical on info string and body sha256
- table rows 6 -> 14 (+8 = the new index page table)
- links: 27/27 original targets retained
- words 7,266 -> 8,023 (+757 = index page table, anchor map, child ledes)
- index 55,872 -> 8,662 chars; children 3,935-22,363 chars

No prose was rewritten and no prose exception was needed. The page had zero
intra-page anchor links and zero self-route links, and the one directional
reference the orphan check finds ("the trusted plugin id replacement above")
keeps its target on the same child page.

Closes audit findings: r3-0126, r3-1163 (partially: the accordions keep their
authored anchors and are no longer buried under a 2,065-word H2)
2026-09-09 18:23:39 +09:00