fix(state): restore preserved backups into replacement sandboxes - #7814
Conversation
Cross-sandbox restore required the source sandbox to still be running, so a backup could not be recovered into a replacement once the original was gone. Resolve the clone image from the source registry entry instead, and fail only when no image is recorded. Restoring a SQLite state file also reported success from the transport exit status alone. Open a write transaction against the replaced database and fail the restore when it cannot be written. Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughCross-sandbox snapshot restores now validate source image and inference-route metadata before destination changes. SQLite state restores now remove stale sidecars and verify writable transactions, with tests and documentation covering both behaviors. ChangesSandbox restore recovery
Estimated code review effort: 3 (Moderate) | ~25 minutes Suggested labels: Sequence Diagram(s)Offline source replacement flowsequenceDiagram
participant SnapshotRestore
participant SourceRegistry
participant ReplacementSandbox
SnapshotRestore->>SourceRegistry: resolve source image and inference route
SourceRegistry-->>SnapshotRestore: return complete or incomplete metadata
SnapshotRestore->>ReplacementSandbox: create destination from recorded image
SQLite restore validation flowsequenceDiagram
participant RestoreCommand
participant RestoreScript
participant SQLiteDatabase
RestoreCommand->>RestoreScript: restore backup and swap database
RestoreScript->>SQLiteDatabase: remove stale WAL and SHM sidecars
RestoreCommand->>SQLiteDatabase: begin immediate transaction and roll back
SQLiteDatabase-->>RestoreCommand: return writable or failure status
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
|
🌿 Preview your docs: https://nvidia-preview-pr-7814.docs.buildwithfern.com/nemoclaw |
Code Coverage OverviewLanguages: TypeScript TypeScript / code-coverage/pluginThe overall coverage in commit 20e5a55 in the TypeScript / code-coverage/cliThe overall coverage in commit 20e5a55 in the Show a code coverage summary of the most impacted files.
Updated |
PR Review Advisor — No blocking findings reportedAdvisor assessment: No blocking advisor findings reported Model lanes
Nemotron output stays in workflow artifacts and does not change the assessment above. E2E guidanceAdvisory only. E2E / PR Gate selects and runs jobs independently. Recommended E2E: 2 optional E2E recommendations
1 warning · 0 suggestionsWarningsWarnings do not block.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/lib/state/state-file-sqlite-restore-behavior.test.ts`:
- Around line 77-87: Update the test “reports failure when the restored database
cannot be written” so staging and swap can complete while an exclusive SQLite
lock blocks only the final write check, rather than making the runtime directory
unwritable. Ensure the lock/setup is released and any permission restoration
runs in a finally block so cleanup occurs even when assertions or restore
execution fail.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 482a0ca7-f506-4b8a-bcc4-8601440f564f
📒 Files selected for processing (5)
docs/manage-sandboxes/backup-restore.mdxsrc/lib/actions/sandbox/snapshot-restore-offline-source.test.tssrc/lib/actions/sandbox/snapshot.tssrc/lib/state/state-file-restore.tssrc/lib/state/state-file-sqlite-restore-behavior.test.ts
Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/reference/commands.mdx`:
- Around line 2900-2911: Update the snapshot restore prerequisites section to
explicitly cover absent sources, stating the required condition for restoring
when the source no longer exists, or clearly define “stopped” to include absent
Docker/VM sources. Keep the existing Kubernetes gateway requirement and
destination validation behavior unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: e4a8ac9a-7896-4a4c-bfc7-242467136ac3
📒 Files selected for processing (2)
docs/manage-sandboxes/backup-restore.mdxdocs/reference/commands.mdx
🚧 Files skipped from review as they are similar to previous changes (1)
- docs/manage-sandboxes/backup-restore.mdx
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
|
Maintainer follow-up (no changes requested): exact head The PR is still based on |
…estore Signed-off-by: Tinson Lai <tinsonl@nvidia.com> # Conflicts: # docs/manage-sandboxes/backup-restore.mdx
cjagwani
left a comment
There was a problem hiding this comment.
Exact-head review after the contributor refresh onto current main. Deterministic gate passes with all 46 current checks green, clean mergeability, all 15 commits Verified, and no unresolved major findings. Focused exact-head tests pass 5 of 5. Security review PASS: secrets and credentials; input validation; authentication and authorization; dependencies and supply chain; error handling and exposure; cryptography; configuration and infrastructure; security tests; and system security. Source and destination mutations remain locked, registry image and complete inference route are re-read with gateway compatibility before deletion, Kubernetes still requires a live gateway image probe, and SQLite replacement remains staged and atomic with a fail-closed write transaction check. The accepted snapshot-restore bug scope is unchanged.
|
Current-base handoff after main advanced to da1b103: exact head 20e5a55 remains approved and its reviewed product patch is unchanged, but maintainer edits are disabled and the branch now lacks current-base CI/gate evidence. Please refresh it; I will re-gate the next head after the one-hour quiet window. This is a plain status comment, not Changes Requested. |
|
Correction to my prior handoff: conflict-free base refreshes are explicitly waived. Please do not merge main solely for base currency; preserving exact-head CI/E2E and documentation receipts is preferred unless GitHub reports a real conflict or reviewed behavior requires a change. The existing approval/review evidence remains in force, and I will continue monitoring mergeability. This is a plain coordination comment, not Changes Requested. |
…ren (#7839) <!-- markdownlint-disable MD041 --> ## Summary <!-- 1-3 plain sentences: what changes and why. Describe before-and-after behavior when it applies. Follow the NemoClaw Writing Guide: https://github.com/NVIDIA/NemoClaw/blob/main/WRITING.md. Do not add unrelated prose cleanup. --> On Node.js v26 the JWT bundle concurrency test failed even though all twelve helper subprocesses exited 0 and published one identical bundle. Each helper runs under `node --import tsx`, and `tsx` calls `module.register()`, which Node 26 deprecated as DEP0205, so every child wrote a warning line to stderr and the test's exact-empty stderr assertion rejected it. The helpers now start with `--disable-warning=DEP0205`, so the test passes on Node 26 while any other child stderr still fails it. ## Related Issue <!-- Fixes #NNN or Closes #NNN. Remove this section if none. --> Fixes #7837 ## Changes <!-- List concrete changes. If this adds an abstraction, configuration, fallback, migration, or compatibility path, name its current requirement and consumer, explain why a direct change is insufficient, and identify the test that protects it. --> - `src/lib/onboard/docker-driver-gateway-jwt-bundle.test.ts`: pass `--disable-warning=DEP0205` to each concurrent JWT bundle helper subprocess, named through a `TSX_LOADER_DEPRECATION` constant. - Suppression is scoped to the single deprecation code the TypeScript loader emits, not the broader `--no-warnings` used by other helper spawns in this repository, so a NemoClaw runtime warning from the child would still fail the test. - The only changed file is a test; no runtime onboarding, credential, or gateway code is touched. ## Type of Change - [x] Code change (feature, bug fix, or refactor) - [ ] Code change with doc updates - [ ] Doc only (prose changes, no code sample modifications) - [ ] Doc only (includes code sample changes) ## Quality Gates <!-- Check one tests line and one docs line. Check other lines when applicable. Add every requested justification or approval reference. --> - [x] Tests added or updated for changed behavior - [ ] Existing tests cover changed behavior — justification: - [ ] Tests not applicable — justification: - [ ] Docs updated for user-facing behavior changes - [x] Docs not applicable — justification: the change adjusts test subprocess flags only and alters no user-facing behavior. - [ ] Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging) - [ ] Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: - [ ] Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue: `static-checks` fails on this branch for a reason that predates it — `src/lib/core/shell-quote.ts: fan-in is 26; lower or remove its 27 limit` in `ci/source-architecture-budget.json`. The same stale limit fails on `main` and is already corrected in #7814; this branch does not touch import fan-in. ## Documentation Writer Review <!-- Required for code and documentation changes after the changes and applicable validation are complete. Keep one review checkbox and one instance of each visible or hidden field. For Evidence, list changed documentation paths. For documentation-only changes, also state that the writing rules and documentation style were reviewed. For other results, explain why no documentation change is needed or why the review is blocked. For Agent, use a consistent product and surface name, such as Codex Desktop, Codex CLI, Claude Code, or Cursor. After committing all review changes, put `git rev-parse --short HEAD` and `git rev-parse --short HEAD:AGENTS.md` in the hidden metadata below. Rerun the review and refresh that metadata after any new commit. This receipt is advisory during the data-collection pilot. --> - [ ] Documentation writer subagent reviewed the completed changes - Result: `no-docs-needed` - Evidence: no documentation paths changed; the diff is one test file and no user-facing behavior changes. - Agent: Claude Code <!-- docs-review-head-sha: a278731 --> <!-- docs-review-agents-blob-sha: 0082a58 --> ## DGX Station Hardware Evidence <!-- Required only when scripts/prepare-dgx-station-host.sh changes. Maintainers must review the linked evidence before approving or merging. This is human-reviewed evidence, not authenticated hardware provenance. Exceptional bypasses use existing repository governance and must be documented on the PR. --> - [ ] Tested on DGX Station - Tested commit: - Station profile/scenario: - Result: - Supporting evidence: ## Verification <!-- Check each applicable item only when supported by the requested evidence. Run targeted tests once per relevant change set and rerun after later edits or hook autofixes that can affect the tested behavior. Do not rerun hook-covered checks. --> - [x] PR description includes a `Signed-off-by:` line and every commit appears as `Verified` in GitHub - [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or `npm run check:diff` passed when hooks were skipped or unavailable - [x] Targeted behavior tests pass for the current change set, or tests are marked not applicable above — command/result or justification: `npx vitest run --project cli src/lib/onboard/docker-driver-gateway-jwt-bundle.test.ts` on Node v26.5.0 — 12 passed (12); the same command failed before the change with twelve `[DEP0205]` stderr lines. Temporarily writing an unrelated line to a helper's stderr still failed the test, confirming the assertion keeps its guard. `npm run typecheck:cli` and `npx biome check` on the changed file both clean. - [ ] Applicable broad gate passed — `npm test` for broad runtime/test-harness changes; `npm run check` for repo-wide validation/coverage changes — command/result: - [x] Quality Gates section completed with required justifications or waivers - [x] No secrets, API keys, or credentials committed - [ ] `npm run docs` builds without warnings (doc changes only) - [ ] Doc pages follow the [style guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md) (doc changes only) - [ ] New doc pages include SPDX header and frontmatter (new pages only) --- <!-- DCO sign-off is required in this PR description, and every commit must appear as Verified in GitHub. Run: git config user.name && git config user.email --> Signed-off-by: Tinson Lai <tinsonl@nvidia.com> Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
<!-- markdownlint-disable MD041 --> ## Summary Adds the canonical July 30 release entry for `v0.0.99` before the release tag is captured. The entry covers all 37 merged PRs since `v0.0.98` and bounds experimental or dormant work without presenting it as supported behavior. ## Changes - Adds `docs/changelog/2026-07-30.mdx` with the exact `## v0.0.99` heading, parser-safe MDX SPDX comment, summary, detailed release bullets, and published documentation routes. - Records user-visible recovery, snapshot, shared-route, Hermes, readiness, inference, image, documentation, and release E2E changes. - States that the managed-image selection and startup-profile contracts remain dormant and do not activate buildless onboarding. Source summary: - [#7972](#7972) -> `docs/changelog/2026-07-30.mdx`: Records restored managed OpenClaw configuration modes during recovery. - [#7834](#7834) -> `docs/changelog/2026-07-30.mdx`: Records clone-bound pairing verification after snapshot restore. - [#7975](#7975) -> `docs/changelog/2026-07-30.mdx`: Records managed startup recovery coverage. - [#7960](#7960) -> `docs/changelog/2026-07-30.mdx`: Records dormant startup-profile coordination without activating a supported surface. - [#7856](#7856) -> `docs/changelog/2026-07-30.mdx`: Records persistence of the credential-free OpenClaw startup command. - [#7959](#7959) -> `docs/changelog/2026-07-30.mdx`: Records dormant startup-profile construction without changing onboarding. - [#7946](#7946) -> `docs/changelog/2026-07-30.mdx`: Records the internal startup-profile schema and transport contract. - [#7951](#7951) -> `docs/changelog/2026-07-30.mdx`: Records platform-pull cleanup before managed-image validation. - [#7949](#7949) -> `docs/changelog/2026-07-30.mdx`: Records rejection of retained Hermes `uv` build cache metadata. - [#7597](#7597) -> `docs/changelog/2026-07-30.mdx`: Records separate command and agent first-turn latency evidence. - [#7931](#7931) -> `docs/changelog/2026-07-30.mdx`: Records focused E2E replacement evidence for retired selectors. - [#7950](#7950) -> `docs/changelog/2026-07-30.mdx`: Records exclusion of build-only BuildKit telemetry from the Deep Agents Code probe. - [#7665](#7665) -> `docs/changelog/2026-07-30.mdx`: Records consolidated priority 2 E2E coverage. - [#7911](#7911) -> `docs/changelog/2026-07-30.mdx`: Records the corrected NVIDIA DORI installation pin. - [#7934](#7934) -> `docs/changelog/2026-07-30.mdx`: Records the staging image-family wait before Brev Launchable deployment. - [#7772](#7772) -> `docs/changelog/2026-07-30.mdx`: Records dormant managed-image selection contracts without activating buildless onboarding. - [#7941](#7941) -> `docs/changelog/2026-07-30.mdx`: Records corrected agent-specific provider and policy guidance. - [#7819](#7819) -> `docs/changelog/2026-07-30.mdx`: Records removal of empty Deep Agents Code provider-switch sections. - [#7932](#7932) -> `docs/changelog/2026-07-30.mdx`: Records independent credential-generation E2E execution. - [#7840](#7840) -> `docs/changelog/2026-07-30.mdx`: Records shared-route preservation and pre-delete peer validation during upgrades. - [#7874](#7874) -> `docs/changelog/2026-07-30.mdx`: Records the split between pre-tag release entries and post-tag Announcements. - [#7876](#7876) -> `docs/changelog/2026-07-30.mdx`: Records the writable Hermes runtime root within lockdown. - [#7756](#7756) -> `docs/changelog/2026-07-30.mdx`: Records validated multi-platform managed-image publication. - [#7914](#7914) -> `docs/changelog/2026-07-30.mdx`: Records accepted `uv` version metadata in Hermes image validation. - [#7686](#7686) -> `docs/changelog/2026-07-30.mdx`: Records the explicitly experimental Microsoft Entra runtime identity reference. - [#7869](#7869) -> `docs/changelog/2026-07-30.mdx`: Records classified gateway relaunch quarantine and rebuild guidance. - [#7814](#7814) -> `docs/changelog/2026-07-30.mdx`: Records state restore into replacement sandboxes and SQLite write verification. - [#7839](#7839) -> `docs/changelog/2026-07-30.mdx`: Records quieter onboarding test execution without a user-facing behavior claim. - [#7854](#7854) -> `docs/changelog/2026-07-30.mdx`: Records generalized agent-selection guidance. - [#7845](#7845) -> `docs/changelog/2026-07-30.mdx`: Records isolated CDI test evidence without a user-facing behavior claim. - [#7843](#7843) -> `docs/changelog/2026-07-30.mdx`: Records the corrected Omni sub-agent model ID. - [#7908](#7908) -> `docs/changelog/2026-07-30.mdx`: Records reviewed Hermes and Deep Agents Code dependency pins. - [#7887](#7887) -> `docs/changelog/2026-07-30.mdx`: Records rejection of a symlinked DGX Station release marker. - [#7747](#7747) -> `docs/changelog/2026-07-30.mdx`: Records the internal compute-driver separation without a user-facing behavior claim. - [#7660](#7660) -> `docs/changelog/2026-07-30.mdx`: Records atomic publication of rebuild recovery manifests. - [#7661](#7661) -> `docs/changelog/2026-07-30.mdx`: Records bounded local inference health-response retention. - [#7654](#7654) -> `docs/changelog/2026-07-30.mdx`: Records state preservation across supervisor relaunch recovery. ## Type of Change - [ ] Code change (feature, bug fix, or refactor) - [ ] Code change with doc updates - [x] Doc only (prose changes, no code sample modifications) - [ ] Doc only (includes code sample changes) ## Quality Gates - [ ] Tests added or updated for changed behavior - [x] Existing tests cover changed behavior — justification: `test/changelog-docs.test.ts` validates the dated changelog contract, SPDX comment, version heading, and published routes. - [ ] Tests not applicable — justification: - [x] Docs updated for user-facing behavior changes - [ ] Docs not applicable — justification: - [ ] Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging) - [ ] Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: - [ ] Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue: ## Documentation Writer Review - [x] Documentation writer subagent reviewed the completed changes - Result: `docs-updated` - Evidence: `docs/changelog/2026-07-30.mdx`; the documentation-only diff passed review against `WRITING.md`, the controlled word list, and `docs/CONTRIBUTING.md`. The review covered terminology, structure, active voice, release meaning, product-scope boundaries, and link and code presentation. Changelog tests passed 6/6, and the docs build reported 0 errors with 2 pre-existing warnings. - Agent: Codex CLI <!-- docs-review-head-sha: 200940f --> <!-- docs-review-agents-blob-sha: c052d60 --> ## DGX Station Hardware Evidence - [ ] Tested on DGX Station - Tested commit: - Station profile/scenario: - Result: - Supporting evidence: ## Verification - [x] PR description includes a `Signed-off-by:` line and every commit appears as `Verified` in GitHub - [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or `npm run validate:pr` passed after refreshing `origin/main` when hooks were skipped or unavailable - [x] Targeted behavior tests pass for the current change set, or tests are marked not applicable above — command/result or justification: `npx vitest run test/changelog-docs.test.ts` passed 6/6 tests. - [ ] Applicable broad gate passed — `npm test` for broad runtime/test-harness changes; `npm run check` for repo-wide validation/coverage changes — command/result: Not applicable to this documentation-only release entry. - [x] Quality Gates section completed with required justifications or waivers - [x] No secrets, API keys, or credentials committed - [ ] `npm run docs` builds without warnings (doc changes only) — result: Build passed with 0 errors and 2 pre-existing warnings. - [x] Doc pages follow the [style guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md) (doc changes only) - [ ] New doc pages include SPDX header and frontmatter (new pages only) --- Signed-off-by: San Dang <sdang@nvidia.com> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Added release notes for v0.0.99 covering snapshot restoration, sandbox recovery, gateway route upgrades, and Hermes security updates. * Documented experimental Microsoft Entra runtime identity support and enhanced readiness checks. * Added details on managed image validation, trusted CI image promotion, and end-to-end release evidence. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
Summary
A preserved backup could not be recovered into a replacement sandbox once the original sandbox was gone, because cross-sandbox restore required the source to still be running. For Docker- and VM-driver sandboxes, restore now resolves the clone image from the source registry entry, so a stopped source with a complete durable inference route stays restorable. Restoring a SQLite state file also reported success from the transport exit status alone; it now opens a write transaction against the replaced database and fails when the database cannot be written.
Related Issue
Resolves #7807
Resolves #7800
Fixes #7799
Changes
src/lib/actions/sandbox/snapshot.ts: for Docker- and VM-driver sandboxes, cross-sandbox restore no longer aborts when the source is absent from the gateway listing. It uses the recorded image and requires a complete durable inference route; a missing image stops withnemoclaw onboardguidance. Kubernetes sources still require their pod image to be resolvable through the gateway.src/lib/state/state-file-restore.ts: thesqlite_backuprestore opensBEGIN IMMEDIATEand rolls back against the replaced database, under the same umask as the restore so its own sidecars stay group-writable. It drops both the stale sidecars and the ones the check creates, and extends the cleanup trap to the staged sidecars.src/lib/actions/sandbox/snapshot-restore-offline-source.test.ts: a stopped source with a registered image restores into an auto-created replacement; a stopped source without an image stops before creating or deleting anything.src/lib/state/state-file-sqlite-restore-behavior.test.ts: runs the generated restore command in a real shell against real SQLite and asserts the restored database accepts a write, that stale sidecars are gone, and that an unwritable target fails the restore.docs/manage-sandboxes/backup-restore.mdx: documents restoring into a replacement from a stopped source, and the write check.The reported non-writable WAL and SHM files in #7799 were already fixed by #7344, which is tagged in v0.0.95 and v0.0.96; the report is against v0.0.93. This PR adds the remaining part of that report, the write check the restore never performed.
Type of Change
Quality Gates
3eb94e62cagainst base9b1fbd897: PASS; no security findings; product scope remains bounded to the accepted snapshot-restore bugs.Documentation Writer Review
docs-updateddocs/manage-sandboxes/backup-restore.mdxanddocs/reference/commands.mdx; exact-head review confirmed driver scope, image and inference-route prerequisites, in-place requirements, Kubernetes behavior, SQLite write verification, and pre-delete guarantees. Focused tests (2 files, 4 tests), CLI typecheck,npm run docs(0 errors; existing Fern warnings), andnpm run check:diff(compatibility alias invokingnpm run validate:pr) passed.Verification
Signed-off-by:line and every commit appears asVerifiedin GitHubpre-commit,commit-msg, andpre-pushhooks passed, ornpm run validate:prpassed after refreshingorigin/mainwhen hooks were skipped or unavailablenpx vitest run --project cli src/lib/actions/sandbox/snapshot-restore-offline-source.test.ts src/lib/state/state-file-sqlite-restore-behavior.test.ts— 2 files, 4 tests passed;npm run typecheck:clipassed on3eb94e62c.npm testfor broad runtime/test-harness changes;npm run checkfor repo-wide validation/coverage changes — command/result:npm run docsbuilds without warnings (doc changes only)Signed-off-by: Tinson Lai tinsonl@nvidia.com
Summary by CodeRabbit
Bug Fixes
sqlite_backup-based restores now verify the restored database is writable and fail safely when it can’t be opened for writes.Documentation
Tests