diff --git a/AGENTS.md b/AGENTS.md index bbf6ce8..b8c910d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -31,6 +31,22 @@ instruction from the user: 4. **Deletion is explicit.** Only `remove`, `rename` and `move` delete anything, each requires `--yes`, and each writes the full object JSON to `.iobroker-sync/trash/` _before_ deleting. A failed backup aborts the operation. + + The one thing they delete without backing it up is the pair of adapter markers + belonging to the script being deleted — `javascript..scriptEnabled.` and + `javascript..scriptProblem.` — and only after that script is gone. Those are + adapter-generated derived state (`common.enabled` and `common.engine` are already in + the trash copy), so there is nothing in them to lose. See `cleanUpScriptMarkers` in + `commands/remove.ts` for why they have to be swept at all, and + `ObjectsApi.deleteScriptMarker` for the ordering rule (value first, object second, + never the reverse). The sweep is best-effort: it warns, it never fails the command. + + **Both kinds or neither.** The adapter creates and deletes the two together, so code + that handles only `scriptEnabled` cleans up half a mess and reports success. That is + not hypothetical: the first version of this sweep shipped that way and left eight + orphaned `scriptProblem` states on a live instance while `doctor` called it clean. + `MARKER_KINDS` in `types.ts` is the single list; anything iterating markers iterates it. + 5. **Copy-then-delete must verify first.** ioBroker has no native rename/move, so both are implemented as copy-verify-delete. The verification compares the actual source text. Checking only that "something exists at the new id" is not verification — a diff --git a/README.md b/README.md index 80f1cd7..718d2b2 100644 --- a/README.md +++ b/README.md @@ -108,29 +108,29 @@ If `logs` shows nothing, that is usually the adapter's own log level rather than **Sync** -| Command | Description | -| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `init` | Write `.iobroker-sync.json`, verify the connection, create the script folder. Asks interactively when run without flags. `--types` also sets up TypeScript definitions. | -| `types` | Set up editor intellisense (`log`, `schedule`, ...). `--force`, `--offline`. | -| `login` / `logout` | Save or remove the password for this instance. Never stored in the project. | -| `trust` | Accept the instance's current TLS certificate. Only needed after it changes. `--yes` skips the prompt. | -| `doctor` | Check config, certificate, login, connection and a live round-trip, and say which one is wrong. Read-only, never prompts. Run this first when something looks broken. | -| `pull [pattern]` | Download scripts to disk. Never deletes or overwrites local files. | -| `push [pattern]` | Upload locally modified scripts. Never deletes remote objects. | -| `status` | Show what changed, locally and remotely. | -| `diff [pattern]` | Unified diff of local vs server. `--against ` compares with a backup instead. | -| `watch` | Push on save. `--pull` also applies remote changes. | -| `logs [pattern]` | Stream the server log. `--level`, `--limit`. Read-only. | -| `backup [pattern]` | Snapshot every script — source _and_ full object — to `.iobroker-sync/backup//`. Read-only against the server. | +| Command | Description | +| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `init` | Write `.iobroker-sync.json`, verify the connection, create the script folder. Asks interactively when run without flags. `--types` also sets up TypeScript definitions. | +| `types` | Set up editor intellisense (`log`, `schedule`, ...). `--force`, `--offline`. | +| `login` / `logout` | Save or remove the password for this instance. Never stored in the project. | +| `trust` | Accept the instance's current TLS certificate. Only needed after it changes. `--yes` skips the prompt. | +| `doctor` | Check config, certificate, login, connection, a live round-trip and leftover adapter markers, and say which one is wrong. Read-only, never prompts. Run this first when something looks broken. | +| `pull [pattern]` | Download scripts to disk. Never deletes or overwrites local files. | +| `push [pattern]` | Upload locally modified scripts. Never deletes remote objects. | +| `status` | Show what changed, locally and remotely. | +| `diff [pattern]` | Unified diff of local vs server. `--against ` compares with a backup instead. | +| `watch` | Push on save. `--pull` also applies remote changes. | +| `logs [pattern]` | Stream the server log. `--level`, `--limit`. Read-only. | +| `backup [pattern]` | Snapshot every script — source _and_ full object — to `.iobroker-sync/backup//`. Read-only against the server. | **Lifecycle** -| Command | Description | -| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | -| `list` | All scripts with instance and enabled state. | -| `start` / `stop` / `restart` `` | Toggle `common.enabled`. | -| `new ` | Create a new script (disabled) plus any missing folders. | -| `rename` / `move` / `remove` | Destructive. Require `--yes` and back up the object first. `remove` keeps the local file unless `--delete-local`. | +| Command | Description | +| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `list` | All scripts with instance and enabled state. | +| `start` / `stop` / `restart` `` | Toggle `common.enabled`. | +| `new ` | Create a new script (disabled) plus any missing folders. | +| `rename` / `move` / `remove` | Destructive. Require `--yes` and back up the object first. `remove` keeps the local file unless `--delete-local`. All three also clean up the `scriptEnabled`/`scriptProblem` states the old id leaves behind on every javascript instance. | `--dry-run`, `--verbose`, `--json` and `-C ` are global and work with every command. When in doubt, `--dry-run` shows what would happen and changes nothing. diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index 818e9f1..4288e85 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -3,9 +3,9 @@ Things that look like bugs and are not. See the [README](../README.md) for the overview. **Start with `iob-sync doctor`.** It checks the config, the certificate, the login, the -connection and a live round-trip, and names the one that is wrong — including the two -cases below, which are the ones that reliably send people down the wrong path. It is -read-only and never prompts, so it is safe to run at any time. +connection, a live round-trip and the adapter's leftover script markers, and names the one +that is wrong — including the cases below, which are the ones that reliably send people +down the wrong path. It is read-only and never prompts, so it is safe to run at any time. ## Commands time out, but the connection "works" @@ -86,6 +86,48 @@ a script or move it to another javascript instance, it cannot — that is delibe sync bug cannot stop a running script. Use `start` / `stop` for `enabled`; instance moves must be done in Admin. +## `doctor` warns about orphaned markers + +Nothing is broken, and no script is affected. + +The javascript adapter keeps two bookkeeping states beside every script, on **every** +javascript instance — not only the one that runs it: + +``` +javascript..scriptEnabled.