[PR #4580] [CLOSED] fix(uninstall): preserve the configuration as it was before uninstall edits it #6320

Closed
opened 2026-10-31 01:13:32 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/4580
Author: @Adolanium
Created: 10/16/2026
Status: ❌ Closed

Base: main ← Head: fix/uninstall-current-config-backup


📝 Commits (2)

  • 2dfc2d0 fix(uninstall): preserve the configuration as it was before uninstall edits it
  • 70bff72 test(uninstall): cover backups that fail verification after copying

📊 Changes

11 files changed (+384 additions, -82 deletions)

View changed files

📝 docs/installation.md (+6 -4)
📝 docs/user-visible-errors.csv (+2 -2)
📝 src/application/Setup.configuration.test.ts (+20 -1)
📝 src/application/SetupClientConfiguration.ts (+14 -22)
📝 src/application/SetupClients.ts (+20 -15)
📝 src/application/SetupRecoveryBackup.ts (+62 -11)
📝 src/application/SetupTypes.ts (+7 -6)
📝 src/application/Uninstall.ts (+52 -14)
📝 tests/boundary/filesystem/clientConfigurationTransactions.test.ts (+161 -2)
📝 tests/boundary/filesystem/clientLifecycle.test.ts (+10 -5)
📝 tests/boundary/filesystem/setupClientConfigCleanupOwnership.test.ts (+30 -0)

📄 Description

Summary

Fixes #4581.

rea uninstall did not preserve a client configuration as it was just before uninstall edited it. After an earlier setup, .rea.backup holds the bytes from before setup, and uninstall's failure messages told the user to restore that file, which drops every edit made since setup. Uninstall now preserves the current bytes through the same recovery-backup owner setup uses, and both failure messages name the backup that was actually used. Setup's readback-failure message had the same hardcoded .rea.backup wording and is fixed here too.

Problem and expected behavior

docs/installation.md says setup and uninstall keep the first .rea.backup, and that setup preserves a verified .rea.backup.<sha256>.recovery copy when that snapshot differs from the current configuration.

removeClient in src/application/Uninstall.ts copied the configuration to ${configPath}.rea.backup with COPYFILE_EXCL and treated EEXIST as success. After setup, that file already exists with the pre-setup bytes, so uninstall made no copy of the bytes it was about to edit. If the update failed and the restore also failed, the message said Restore its .rea.backup manually. Following it loses the REA registration and any user edits made after setup.

failedConfigurationMessage in src/application/SetupClients.ts returned Repair the configuration file or restore its .rea.backup for every readback failure. When setup had used a recovery copy, that also named the older snapshot. The same readback reason is also returned before any write, where no backup was made.

Change and scope

  • src/application/SetupRecoveryBackup.ts: preserveRecoveryBackup is replaced by preserveCurrentRecoveryBackup. It runs the select, exclusive copy, and verify steps that persistClientConfiguration used to run inline, and returns the verified backup path or a typed failure (unreadable, occupied, copy, unverified). Its options take the copy function, so uninstall keeps its injectable UninstallFileSystem.copy, and an optional resource scope. selectCurrentRecoveryBackup takes the same optional scope for its backup reads.
  • src/application/SetupClientConfiguration.ts: persistClientConfiguration calls the new function. rollbackOrReadbackFailure now carries backupPath on a readback result.
  • src/application/Uninstall.ts: a new backupClientConfiguration calls preserveCurrentRecoveryBackup with the original text uninstall already read. The update and restore failure messages name the returned path. An occupied recovery path blocks the edit and names that path. The backup reads use uninstall's ArtifactResourceScope, the same owner its configuration reads use. If a backup file handle fails to close, the scope keeps it for close(), and that client fails with the backup message instead of the error escaping runUninstall.
  • docs/user-visible-errors.csv: the setup readback and uninstall restore rows now show the new messages.
  • src/application/SetupTypes.ts, src/application/SetupClients.ts: readback gets its own result member with an optional backupPath. With a path, the message names it. Without one, it no longer mentions a backup or claims a write happened.

Uninstall still has no preview step, so the recovery path is not shown before approval as it is in setup. Uninstall's preflight inspectClient does not check the recovery path; an occupied path is reported when that client is reached.

Contribution scope: not applicable. This is a fix within the existing setup and uninstall boundary.

Contract and boundary impact

  • Semantic owner and earliest changed stage: SetupRecoveryBackup.ts owns backup selection and preservation for both setup and uninstall; the change is at the backup step before uninstall edits a client configuration.
  • Contribution scope fit and linked maintainer decision: not applicable; ordinary fix within existing boundaries.
  • CLI and MCP/tool-catalog contract: the uninstall item detail text and setup remediation text change. ClientConfigurationResult with reason: "readback" may now carry backupPath.
  • Provider, bridge, target-format, or platform compatibility: none.
  • Evidence, artifact, provenance, or reconstruction contract: none.
  • Target selection, authority, upstream behavior, and cleanup details (if applicable): uninstall may now create <config>.rea.backup.<sha256>.recovery next to a client configuration, the same file setup already creates. It never replaces an existing file there.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact: docs/installation.md now says uninstall also preserves the recovery copy, and that when an edit made a backup, uninstall update and restore failures and setup readback and rollback failures name the backup holding the bytes from just before the edit.

Expected effects (check known effects; use documentation-only on its own).
Unchecked items do not establish absence; leave uncertainties visible.

  • Read existing files or captured data.
  • Launch or attach to a process.
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

Evidence and regression coverage

  • Tests added or updated:
    • tests/boundary/filesystem/clientConfigurationTransactions.test.ts: a new uninstall backups after edits made since setup block. Each case runs real setup, edits the configured file, then uninstalls. It checks that a successful uninstall leaves the edited bytes in the recovery path and the pre-setup bytes in .rea.backup; that an update failure and an update-plus-restore failure name the recovery path in the message, and that path holds the edited bytes; and that an occupied recovery path blocks the edit and is named.
    • The same block also covers a backup copy that reports success but leaves the recovery file missing or holding other bytes. Uninstall must fail before writing. Both cases fail when the verify step after copying is removed.
    • tests/boundary/filesystem/setupClientConfigCleanupOwnership.test.ts: a new case holds the .rea.backup read handle open during uninstall. It checks that the client fails with the backup message, the configuration is unchanged, and the host's close() releases the same handle.
    • tests/boundary/filesystem/clientLifecycle.test.ts: the existing update and restore message cases now expect the .rea.backup path for a first uninstall.
    • src/application/Setup.configuration.test.ts: the readback message without a backup, and a new case where a readback result carries a recovery path.
  • Base reproduction or other evidence: executed. With the new and updated tests and src/ from upstream/main (8ab502439), all four new uninstall cases, the two updated uninstall message cases, the backup-handle cleanup case, and both setup readback cases failed. With the fix they pass.
  • User-visible CLI/MCP output (if applicable): uninstall restore failure now reads Configuration could not be updated or restored. Restore the backup at <config>.rea.backup.<sha256>.recovery manually, then rerun uninstall.
  • Remaining proof gaps: run on Windows only. Two symlink tests in clientLifecycle.test.ts fail on this machine both before and after the change, so the symlinked-configuration path was not exercised here. No end-to-end rea uninstall CLI run was made; the tests call systemUninstallHost(...).removeClient.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

  • npm run test:focused -- tests/boundary/filesystem/clientConfigurationTransactions.test.ts tests/boundary/filesystem/clientLifecycle.test.ts src/application/Setup.configuration.test.ts — exited 1 before running tests because the Windows native artifacts (native/windows/build/manifest.json) are missing in this checkout.
  • npx vitest run tests/boundary/filesystem/clientConfigurationTransactions.test.ts tests/boundary/filesystem/clientLifecycle.test.ts src/application/Setup.configuration.test.ts src/application/Setup.recovery.test.ts tests/boundary/filesystem/clientConfigurationBom.test.ts tests/boundary/filesystem/setupClientConfigCleanupOwnership.test.ts tests/boundary/filesystem/setupSkillCleanupOwnership.test.ts — 129 passed, 3 skipped, 2 failed. The 2 failures are the symlink tests named above, which also fail on upstream/main on this machine.
  • npx vitest run tests/boundary/filesystem/clientConfigurationTransactions.test.ts tests/boundary/filesystem/clientLifecycle.test.ts tests/boundary/filesystem/setupClientConfigCleanupOwnership.test.ts src/application/Setup.configuration.test.ts with the new tests on upstream/main source (8ab502439) — 11 failed: the 9 regression cases above and the 2 symlink tests.
  • npx vitest run on the other 14 client configuration boundary files plus src/cliLogging.test.ts and src/setupEnvironment.test.ts — 300 passed, 1 skipped, 42 failed, both with the fix and on 8ab502439. The failures are Windows path-separator and stdio connection failures that this change does not touch.
  • npx tsc --noEmit --pretty false — no errors.
  • After the review fixes in 70bff7240: npx vitest run tests/boundary/filesystem/clientConfigurationTransactions.test.ts — 18 passed, 2 skipped; with the verify step removed from preserveCurrentRecoveryBackup, the two new cases fail. npm run check:fast — passed. npm run docs:check — did not run because the Windows native artifacts are missing.
  • After rebasing onto upstream/main f26973a52 (conflicts with the new target_unavailable result in SetupClientConfiguration.ts and SetupClients.ts, resolved by keeping that check and adding its message case to the switch): npx tsc --noEmit — no errors; npm run check:fast — passed; npx vitest run on clientConfigurationTransactions, clientLifecycle, setupClientConfigCleanupOwnership and Setup.configuration — 97 passed, 3 skipped, 2 failed (the same two symlink tests, which also fail on unmodified f26973a52 here).
  • npx oxlint on the changed files — 0 errors. Existing size and complexity warnings remain; removeClient complexity went from 25 to 22.
  • npx oxfmt --check on the changed files — passed.

Compatibility, safety, and release

  • Breaking changes or migration steps: none. Callers that read reason: "readback" still get it; backupPath is optional.
  • Real Hopper/Ghidra, browser, or OS coverage: Windows 11 only. Not run on macOS or Linux.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: the recovery copy has the same contents and location as the one setup already writes. Failure messages now include the local backup path.

Author readiness

Human owner: @Adolanium

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/4580 **Author:** [@Adolanium](https://github.com/Adolanium) **Created:** 10/16/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `fix/uninstall-current-config-backup` --- ### 📝 Commits (2) - [`2dfc2d0`](https://github.com/morluto/rea/commit/2dfc2d0f7caa9ba583355e6d14a5c9905c87a5c8) fix(uninstall): preserve the configuration as it was before uninstall edits it - [`70bff72`](https://github.com/morluto/rea/commit/70bff724033809038c4665bb6e671d114d6a2f34) test(uninstall): cover backups that fail verification after copying ### 📊 Changes **11 files changed** (+384 additions, -82 deletions) <details> <summary>View changed files</summary> 📝 `docs/installation.md` (+6 -4) 📝 `docs/user-visible-errors.csv` (+2 -2) 📝 `src/application/Setup.configuration.test.ts` (+20 -1) 📝 `src/application/SetupClientConfiguration.ts` (+14 -22) 📝 `src/application/SetupClients.ts` (+20 -15) 📝 `src/application/SetupRecoveryBackup.ts` (+62 -11) 📝 `src/application/SetupTypes.ts` (+7 -6) 📝 `src/application/Uninstall.ts` (+52 -14) 📝 `tests/boundary/filesystem/clientConfigurationTransactions.test.ts` (+161 -2) 📝 `tests/boundary/filesystem/clientLifecycle.test.ts` (+10 -5) 📝 `tests/boundary/filesystem/setupClientConfigCleanupOwnership.test.ts` (+30 -0) </details> ### 📄 Description ## Summary Fixes #4581. `rea uninstall` did not preserve a client configuration as it was just before uninstall edited it. After an earlier setup, `.rea.backup` holds the bytes from before setup, and uninstall's failure messages told the user to restore that file, which drops every edit made since setup. Uninstall now preserves the current bytes through the same recovery-backup owner setup uses, and both failure messages name the backup that was actually used. Setup's readback-failure message had the same hardcoded `.rea.backup` wording and is fixed here too. ## Problem and expected behavior `docs/installation.md` says setup and uninstall keep the first `.rea.backup`, and that setup preserves a verified `.rea.backup.<sha256>.recovery` copy when that snapshot differs from the current configuration. `removeClient` in `src/application/Uninstall.ts` copied the configuration to `${configPath}.rea.backup` with `COPYFILE_EXCL` and treated `EEXIST` as success. After setup, that file already exists with the pre-setup bytes, so uninstall made no copy of the bytes it was about to edit. If the update failed and the restore also failed, the message said `Restore its .rea.backup manually`. Following it loses the REA registration and any user edits made after setup. `failedConfigurationMessage` in `src/application/SetupClients.ts` returned `Repair the configuration file or restore its .rea.backup` for every `readback` failure. When setup had used a recovery copy, that also named the older snapshot. The same `readback` reason is also returned before any write, where no backup was made. ## Change and scope - `src/application/SetupRecoveryBackup.ts`: `preserveRecoveryBackup` is replaced by `preserveCurrentRecoveryBackup`. It runs the select, exclusive copy, and verify steps that `persistClientConfiguration` used to run inline, and returns the verified backup path or a typed failure (`unreadable`, `occupied`, `copy`, `unverified`). Its options take the copy function, so uninstall keeps its injectable `UninstallFileSystem.copy`, and an optional resource scope. `selectCurrentRecoveryBackup` takes the same optional scope for its backup reads. - `src/application/SetupClientConfiguration.ts`: `persistClientConfiguration` calls the new function. `rollbackOrReadbackFailure` now carries `backupPath` on a `readback` result. - `src/application/Uninstall.ts`: a new `backupClientConfiguration` calls `preserveCurrentRecoveryBackup` with the `original` text uninstall already read. The update and restore failure messages name the returned path. An occupied recovery path blocks the edit and names that path. The backup reads use uninstall's `ArtifactResourceScope`, the same owner its configuration reads use. If a backup file handle fails to close, the scope keeps it for `close()`, and that client fails with the backup message instead of the error escaping `runUninstall`. - `docs/user-visible-errors.csv`: the setup `readback` and uninstall restore rows now show the new messages. - `src/application/SetupTypes.ts`, `src/application/SetupClients.ts`: `readback` gets its own result member with an optional `backupPath`. With a path, the message names it. Without one, it no longer mentions a backup or claims a write happened. Uninstall still has no preview step, so the recovery path is not shown before approval as it is in setup. Uninstall's preflight `inspectClient` does not check the recovery path; an occupied path is reported when that client is reached. Contribution scope: not applicable. This is a fix within the existing setup and uninstall boundary. ## Contract and boundary impact - Semantic owner and earliest changed stage: `SetupRecoveryBackup.ts` owns backup selection and preservation for both setup and uninstall; the change is at the backup step before uninstall edits a client configuration. - Contribution scope fit and linked maintainer decision: not applicable; ordinary fix within existing boundaries. - CLI and MCP/tool-catalog contract: the uninstall item `detail` text and setup `remediation` text change. `ClientConfigurationResult` with `reason: "readback"` may now carry `backupPath`. - Provider, bridge, target-format, or platform compatibility: none. - Evidence, artifact, provenance, or reconstruction contract: none. - Target selection, authority, upstream behavior, and cleanup details (if applicable): uninstall may now create `<config>.rea.backup.<sha256>.recovery` next to a client configuration, the same file setup already creates. It never replaces an existing file there. - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact: `docs/installation.md` now says uninstall also preserves the recovery copy, and that when an edit made a backup, uninstall update and restore failures and setup readback and rollback failures name the backup holding the bytes from just before the edit. Expected effects (check known effects; use documentation-only on its own). Unchecked items do not establish absence; leave uncertainties visible. - [x] Read existing files or captured data. - [ ] Launch or attach to a process. - [ ] Inject instrumentation or run scripts/code. - [x] Change process memory, registers, files, or configuration. - [ ] Contact network services. - [ ] Recover protected code or work around protections. - [ ] Documentation or usability only; no target interaction. - [ ] Not sure yet; investigation or maintainer guidance needed. ## Evidence and regression coverage - Tests added or updated: - `tests/boundary/filesystem/clientConfigurationTransactions.test.ts`: a new `uninstall backups after edits made since setup` block. Each case runs real setup, edits the configured file, then uninstalls. It checks that a successful uninstall leaves the edited bytes in the recovery path and the pre-setup bytes in `.rea.backup`; that an update failure and an update-plus-restore failure name the recovery path in the message, and that path holds the edited bytes; and that an occupied recovery path blocks the edit and is named. - The same block also covers a backup copy that reports success but leaves the recovery file missing or holding other bytes. Uninstall must fail before writing. Both cases fail when the verify step after copying is removed. - `tests/boundary/filesystem/setupClientConfigCleanupOwnership.test.ts`: a new case holds the `.rea.backup` read handle open during uninstall. It checks that the client fails with the backup message, the configuration is unchanged, and the host's `close()` releases the same handle. - `tests/boundary/filesystem/clientLifecycle.test.ts`: the existing update and restore message cases now expect the `.rea.backup` path for a first uninstall. - `src/application/Setup.configuration.test.ts`: the `readback` message without a backup, and a new case where a `readback` result carries a recovery path. - Base reproduction or other evidence: executed. With the new and updated tests and `src/` from upstream/main (`8ab502439`), all four new uninstall cases, the two updated uninstall message cases, the backup-handle cleanup case, and both setup `readback` cases failed. With the fix they pass. - User-visible CLI/MCP output (if applicable): uninstall restore failure now reads `Configuration could not be updated or restored. Restore the backup at <config>.rea.backup.<sha256>.recovery manually, then rerun uninstall.` - Remaining proof gaps: run on Windows only. Two symlink tests in `clientLifecycle.test.ts` fail on this machine both before and after the change, so the symlinked-configuration path was not exercised here. No end-to-end `rea uninstall` CLI run was made; the tests call `systemUninstallHost(...).removeClient`. For evidence-bearing changes: - [ ] Observed, derived, and inferred claims remain distinguishable. - [ ] Artifact identity, source provenance, and failed attempts remain preserved. - [ ] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed - `npm run test:focused -- tests/boundary/filesystem/clientConfigurationTransactions.test.ts tests/boundary/filesystem/clientLifecycle.test.ts src/application/Setup.configuration.test.ts` — exited 1 before running tests because the Windows native artifacts (`native/windows/build/manifest.json`) are missing in this checkout. - `npx vitest run tests/boundary/filesystem/clientConfigurationTransactions.test.ts tests/boundary/filesystem/clientLifecycle.test.ts src/application/Setup.configuration.test.ts src/application/Setup.recovery.test.ts tests/boundary/filesystem/clientConfigurationBom.test.ts tests/boundary/filesystem/setupClientConfigCleanupOwnership.test.ts tests/boundary/filesystem/setupSkillCleanupOwnership.test.ts` — 129 passed, 3 skipped, 2 failed. The 2 failures are the symlink tests named above, which also fail on upstream/main on this machine. - `npx vitest run tests/boundary/filesystem/clientConfigurationTransactions.test.ts tests/boundary/filesystem/clientLifecycle.test.ts tests/boundary/filesystem/setupClientConfigCleanupOwnership.test.ts src/application/Setup.configuration.test.ts` with the new tests on upstream/main source (`8ab502439`) — 11 failed: the 9 regression cases above and the 2 symlink tests. - `npx vitest run` on the other 14 client configuration boundary files plus `src/cliLogging.test.ts` and `src/setupEnvironment.test.ts` — 300 passed, 1 skipped, 42 failed, both with the fix and on `8ab502439`. The failures are Windows path-separator and stdio connection failures that this change does not touch. - `npx tsc --noEmit --pretty false` — no errors. - After the review fixes in `70bff7240`: `npx vitest run tests/boundary/filesystem/clientConfigurationTransactions.test.ts` — 18 passed, 2 skipped; with the verify step removed from `preserveCurrentRecoveryBackup`, the two new cases fail. `npm run check:fast` — passed. `npm run docs:check` — did not run because the Windows native artifacts are missing. - After rebasing onto upstream/main `f26973a52` (conflicts with the new `target_unavailable` result in `SetupClientConfiguration.ts` and `SetupClients.ts`, resolved by keeping that check and adding its message case to the `switch`): `npx tsc --noEmit` — no errors; `npm run check:fast` — passed; `npx vitest run` on `clientConfigurationTransactions`, `clientLifecycle`, `setupClientConfigCleanupOwnership` and `Setup.configuration` — 97 passed, 3 skipped, 2 failed (the same two symlink tests, which also fail on unmodified `f26973a52` here). - `npx oxlint` on the changed files — 0 errors. Existing size and complexity warnings remain; `removeClient` complexity went from 25 to 22. - `npx oxfmt --check` on the changed files — passed. ## Compatibility, safety, and release - Breaking changes or migration steps: none. Callers that read `reason: "readback"` still get it; `backupPath` is optional. - Real Hopper/Ghidra, browser, or OS coverage: Windows 11 only. Not run on macOS or Linux. - Package or release metadata impact: none. - Security, privacy, process, or containment review: the recovery copy has the same contents and location as the one setup already writes. Failure messages now include the local backup path. ## Author readiness <!-- The human owner completes these declarations before requesting review. The PR author is the owner by default. A different owner must explicitly accept ownership on this PR. AI agents must leave these declarations to a human to dictate to them or there could be catastrophic consequences for many users of REA. Draft PRs may leave them unchecked. These declarations record initial readiness when opening the PR for review; later pushes do not require a new human sign-off. Keep the description and validation evidence current as the work changes. See CONTRIBUTING.md#pull-request-ownership-and-readiness. --> Human owner: @Adolanium - [x] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work. - [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [x] I declare this PR ready for initial maintainer review. ## Before merge <!-- Draft PRs may leave pending items unchecked. Resolve material scope and authority questions before integration. --> - [ ] This PR addresses a concrete problem or an agreed enhancement. - [ ] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [ ] The PR has one focused outcome and the title follows `type(scope): outcome`. - [ ] Related issue is linked, or the reason for not linking one is stated above. - [ ] Tests cover changed observable behavior and meaningful failure paths. - [ ] Owning docs, contracts, and generated metadata are updated where needed. - [ ] User-visible CLI/MCP changes include representative output. - [ ] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 01:13:32 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#6320
No description provided.