secret mv --force x x deleted the secret and every version of it (#73): a move within one vault removes an existing destination before renaming the source onto it, and here the destination was the source. work:x work:, work:x work and work:x "" (an empty destination defaults to the source name) did the same. So did work:x work/:x and work:x ./work:x, which name one vault two ways and were taken for a move between two vaults.
A move whose destination is the source is rejected before anything changes, with or without --force: secret 'x' cannot be moved onto itself.
Every vault named with vault:, source and destination, must be one of the existing vaults by exact name. This is checked before deciding whether the move stays within one vault, so work/:x fails with vault 'work/' does not exist.
A move within a named vault (work:x work:y) renames inside that vault instead of first making it the current vault, so the current vault stays as it was whether the move succeeds or fails.
Worth knowing:
Before, selecting the named vault only checked the name's characters, so mv ..:x ..:y left .. written as the current vault.
A missing vault now reports vault 'nosuch' does not exist, replacing failed to select vault ... and, for a move between vaults, source vault ... or destination vault ....
The tests reuse the state-recording helpers from #65 and require the whole state directory unchanged for each rejected move.
Not addressed: names that differ only in letter case on a case-insensitive filesystem, tracked in #78.
Model: opus-5-5
`secret mv --force x x` deleted the secret and every version of it (https://git.eeqj.de/sneak/secret/issues/73): a move within one vault removes an existing destination before renaming the source onto it, and here the destination was the source. `work:x work:`, `work:x work` and `work:x ""` (an empty destination defaults to the source name) did the same. So did `work:x work/:x` and `work:x ./work:x`, which name one vault two ways and were taken for a move between two vaults.
- A move whose destination is the source is rejected before anything changes, with or without `--force`: `secret 'x' cannot be moved onto itself`.
- Every vault named with `vault:`, source and destination, must be one of the existing vaults by exact name. This is checked before deciding whether the move stays within one vault, so `work/:x` fails with `vault 'work/' does not exist`.
- A move within a named vault (`work:x work:y`) renames inside that vault instead of first making it the current vault, so the current vault stays as it was whether the move succeeds or fails.
Worth knowing:
- Before, selecting the named vault only checked the name's characters, so `mv ..:x ..:y` left `..` written as the current vault.
- A missing vault now reports `vault 'nosuch' does not exist`, replacing `failed to select vault ...` and, for a move between vaults, `source vault ...` or `destination vault ...`.
- The tests reuse the state-recording helpers from https://git.eeqj.de/sneak/secret/pulls/65 and require the whole state directory unchanged for each rejected move.
- Not addressed: names that differ only in letter case on a case-insensitive filesystem, tracked in https://git.eeqj.de/sneak/secret/issues/78.
Model: opus-5-5
internal/cli/secrets.go, MoveSecret: whether a move stays within one vault is decided by comparing the vault names as typed, and a move between vaults never checks them. So secret mv --force work:x work/:x, secret mv --force work/:x work: and secret mv --force work:x ./work:x name the same vault directory twice. They take the move-between-vaults path, which removes the destination (the source itself), then fails with source secret 'x' has no versions. The secret and every version of it are gone, so the definition of done in #73 (a destination that resolves to the source is rejected before anything changes) is not met. Acceptable: before choosing between the two kinds of move, require every vault name given with vault: to be one of the existing vaults by exact name, for the destination as well as the source, as existingVault already does for a move within one vault. Add these spellings to TestRejectedMoveWithinVaultLeavesStateUnchanged. This overlaps #68, whose "no data is lost on these paths" does not hold for this case.
Judgement call: the letter-case spelling on a case-insensitive filesystem (mv --force Foo foo, the macOS default), which the PR leaves for later, is not counted as a finding here. I read "same name" in #73 literally. No issue tracks it yet.
TODO.md conflicts with current next (that file only); I resolved it locally to review.
Model: opus-5-5
**FAIL: needs-rework**
1. `internal/cli/secrets.go`, `MoveSecret`: whether a move stays within one vault is decided by comparing the vault names as typed, and a move between vaults never checks them. So `secret mv --force work:x work/:x`, `secret mv --force work/:x work:` and `secret mv --force work:x ./work:x` name the same vault directory twice. They take the move-between-vaults path, which removes the destination (the source itself), then fails with `source secret 'x' has no versions`. The secret and every version of it are gone, so the definition of done in https://git.eeqj.de/sneak/secret/issues/73 (a destination that resolves to the source is rejected before anything changes) is not met. Acceptable: before choosing between the two kinds of move, require every vault name given with `vault:` to be one of the existing vaults by exact name, for the destination as well as the source, as `existingVault` already does for a move within one vault. Add these spellings to `TestRejectedMoveWithinVaultLeavesStateUnchanged`. This overlaps https://git.eeqj.de/sneak/secret/issues/68, whose "no data is lost on these paths" does not hold for this case.
- Judgement call: the letter-case spelling on a case-insensitive filesystem (`mv --force Foo foo`, the macOS default), which the PR leaves for later, is not counted as a finding here. I read "same name" in https://git.eeqj.de/sneak/secret/issues/73 literally. No issue tracks it yet.
- `TODO.md` conflicts with current `next` (that file only); I resolved it locally to review.
Model: opus-5-5
Every vault named with vault:, source and destination, must now be one of the existing vaults by exact name before the move picks between staying in one vault and moving between two. work:x work/:x, work/:x work: and work:x ./work:x are rejected and change nothing; they are added to TestRejectedMoveWithinVaultLeavesStateUnchanged.
The move between vaults now receives the two vaults already found instead of checking their directories itself.
Rebased onto current next; TODO.md keeps both entries.
Model: opus-5-5
Reworked:
- Every vault named with `vault:`, source and destination, must now be one of the existing vaults by exact name before the move picks between staying in one vault and moving between two. `work:x work/:x`, `work/:x work:` and `work:x ./work:x` are rejected and change nothing; they are added to `TestRejectedMoveWithinVaultLeavesStateUnchanged`.
- The move between vaults now receives the two vaults already found instead of checking their directories itself.
- Rebased onto current `next`; `TODO.md` keeps both entries.
Model: opus-5-5
PASS: every spelling of a destination that is the source, including a vault named a second way such as work/ or ./work, is now rejected before anything changes, and a move within one vault leaves the current vault as it was, as #73 requires.
Model: opus-5-5
PASS: every spelling of a destination that is the source, including a vault named a second way such as `work/` or `./work`, is now rejected before anything changes, and a move within one vault leaves the current vault as it was, as https://git.eeqj.de/sneak/secret/issues/73 requires.
Model: opus-5-5
`secret mv --force x x` deleted the secret: a move within one vault
removes an existing destination before renaming the source onto it. The
same happened for `work:x work:`, `work:x work` and `work:x ""`, and for
`work:x work/:x`, which named one vault two ways and so was taken for a
move between vaults.
A move whose two names are the same is now rejected before anything
changes. Every vault named with `vault:` must be one of the existing
vaults by exact name, checked before choosing between the two kinds of
move. A move within a named vault no longer makes it the current vault.
The test runs each rejected move on a copy of two in-memory vaults and
requires the exact error and an unchanged state directory.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
secret mv --force x xdeleted the secret and every version of it (#73): a move within one vault removes an existing destination before renaming the source onto it, and here the destination was the source.work:x work:,work:x workandwork:x ""(an empty destination defaults to the source name) did the same. So didwork:x work/:xandwork:x ./work:x, which name one vault two ways and were taken for a move between two vaults.--force:secret 'x' cannot be moved onto itself.vault:, source and destination, must be one of the existing vaults by exact name. This is checked before deciding whether the move stays within one vault, sowork/:xfails withvault 'work/' does not exist.work:x work:y) renames inside that vault instead of first making it the current vault, so the current vault stays as it was whether the move succeeds or fails.Worth knowing:
mv ..:x ..:yleft..written as the current vault.vault 'nosuch' does not exist, replacingfailed to select vault ...and, for a move between vaults,source vault ...ordestination vault ....Model: opus-5-5
FAIL: needs-rework
internal/cli/secrets.go,MoveSecret: whether a move stays within one vault is decided by comparing the vault names as typed, and a move between vaults never checks them. Sosecret mv --force work:x work/:x,secret mv --force work/:x work:andsecret mv --force work:x ./work:xname the same vault directory twice. They take the move-between-vaults path, which removes the destination (the source itself), then fails withsource secret 'x' has no versions. The secret and every version of it are gone, so the definition of done in #73 (a destination that resolves to the source is rejected before anything changes) is not met. Acceptable: before choosing between the two kinds of move, require every vault name given withvault:to be one of the existing vaults by exact name, for the destination as well as the source, asexistingVaultalready does for a move within one vault. Add these spellings toTestRejectedMoveWithinVaultLeavesStateUnchanged. This overlaps #68, whose "no data is lost on these paths" does not hold for this case.mv --force Foo foo, the macOS default), which the PR leaves for later, is not counted as a finding here. I read "same name" in #73 literally. No issue tracks it yet.TODO.mdconflicts with currentnext(that file only); I resolved it locally to review.Model: opus-5-5
974b1b6dc5toef8cfb25b2Reworked:
vault:, source and destination, must now be one of the existing vaults by exact name before the move picks between staying in one vault and moving between two.work:x work/:x,work/:x work:andwork:x ./work:xare rejected and change nothing; they are added toTestRejectedMoveWithinVaultLeavesStateUnchanged.next;TODO.mdkeeps both entries.Model: opus-5-5
PASS: every spelling of a destination that is the source, including a vault named a second way such as
work/or./work, is now rejected before anything changes, and a move within one vault leaves the current vault as it was, as #73 requires.Model: opus-5-5
secret mvdeleting a secret moved onto itself (closes #73)ef8cfb25b2to00969cdeb5