Step 5 of #201, stacked on #214: Settings holds a default token per forge.
Settings: a "Default tokens" section with GitHub (token) and Gitea (server address, token). The Gitea address and token are saved, replaced and removed together. The saved token is never carried to another address; a token for an address that cannot be used is not saved (Save is withheld and the reason shown).
The rule: "the default token is a GitHub token" is replaced by "a default token belongs to one place and is never used for another". It is in the name of the vault entry (DefaultTokens.EntryFor): GitHub's is the one it always was, a Gitea server's is made from the server's address.
Who uses it: Quick Sync fills in the default token of the place on its card. An account on the default token gets the one of its own place (AccountTokenResolver). The wizard offers the choice for a place that has one. accounts add / accounts edit --use-default-token work for any place that has one.
A workspace is told which place's default token changed and takes only its own.
README, docs/CLI.md, ADR 0010 ("A default token per place"), threat model T16.
Why
Closes#212. The owner: GitHub and Gitea are to be equal options throughout, and Settings was all about GitHub.
The rules, pinned in DefaultTokenPerPlaceTests (and the rewritten tests that used to state the old rule):
a default token is found for the place it was saved for and for no other (another server, GitHub, an account whose place cannot be used);
the Gitea group saves an address and its token together, refuses a token without an address and an address that cannot be used, and never moves the saved token to another address;
removing or replacing a default token names the accounts that depend on it;
the wizard offers and uses the default token of the chosen place only;
Quick Sync takes a changed default token only for the place it is on.
How it was tested
DefaultTokenPerPlaceTests; nine tests that stated the old rule now state the new one; one new test in the UI suite for the Settings dialog.
Gates, started from PowerShell: numbers in the comment below.
Looked at on screen: the dialog as it opens, with a refused address, and with a server and token entered.
Not merged until
1.1.1 is tagged (#200) and the pull requests below this one are merged.
Checklist
Commits are signed and signed off (git commit -S -s)
Zero build warnings; format and lint are clean
Tests added or updated; the 100% coverage gate still passes
Docs updated for user-visible changes; threat model updated if the attack surface changed (README, docs/CLI.md, ADR 0010, T16)
No secrets, credentials, or personal data
New dependencies are justified below, or none were added (none)
Substantial AI assistance, if any, is noted here: written with Claude Code.
## What
Step 5 of #201, stacked on #214: Settings holds a default token per forge.
- **Settings:** a "Default tokens" section with **GitHub** (token) and **Gitea** (server address, token). The Gitea address and token are saved, replaced and removed together. The saved token is never carried to another address; a token for an address that cannot be used is not saved (Save is withheld and the reason shown).
- **The rule:** "the default token is a GitHub token" is replaced by "a default token belongs to one place and is never used for another". It is in the name of the vault entry (`DefaultTokens.EntryFor`): GitHub's is the one it always was, a Gitea server's is made from the server's address.
- **Who uses it:** Quick Sync fills in the default token of the place on its card. An account on the default token gets the one of its own place (`AccountTokenResolver`). The wizard offers the choice for a place that has one. `accounts add` / `accounts edit --use-default-token` work for any place that has one.
- A workspace is told which place's default token changed and takes only its own.
- README, `docs/CLI.md`, ADR 0010 ("A default token per place"), threat model T16.
## Why
Closes #212. The owner: GitHub and Gitea are to be equal options throughout, and Settings was all about GitHub.
The rules, pinned in `DefaultTokenPerPlaceTests` (and the rewritten tests that used to state the old rule):
- a default token is found for the place it was saved for and for no other (another server, GitHub, an account whose place cannot be used);
- the Gitea group saves an address and its token together, refuses a token without an address and an address that cannot be used, and never moves the saved token to another address;
- removing or replacing a default token names the accounts that depend on it;
- the wizard offers and uses the default token of the chosen place only;
- Quick Sync takes a changed default token only for the place it is on.
## How it was tested
- `DefaultTokenPerPlaceTests`; nine tests that stated the old rule now state the new one; one new test in the UI suite for the Settings dialog.
- Gates, started from PowerShell: numbers in the comment below.
- Looked at on screen: the dialog as it opens, with a refused address, and with a server and token entered.
## Not merged until
1.1.1 is tagged (#200) and the pull requests below this one are merged.
## Checklist
- [x] Commits are signed and signed off (`git commit -S -s`)
- [x] Zero build warnings; format and lint are clean
- [x] Tests added or updated; the 100% coverage gate still passes
- [x] Docs updated for user-visible changes; threat model updated if the attack surface changed (README, docs/CLI.md, ADR 0010, T16)
- [x] No secrets, credentials, or personal data
- [x] New dependencies are justified below, or none were added (none)
- [x] Substantial AI assistance, if any, is noted here: written with Claude Code.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Gates on d88cf08, started from PowerShell: build 0 warnings; engine and view-model tests 1,462; CLI tests 249; 100% line coverage on the three assemblies; format clean; UI suite 18/18 (one new test for the Settings dialog). An independent review follows.
Gates on `d88cf08`, started from PowerShell: build 0 warnings; engine and view-model tests 1,462; CLI tests 249; 100% line coverage on the three assemblies; format clean; UI suite 18/18 (one new test for the Settings dialog). An independent review follows.
Independent review of this pull request (and of the last commit of #210): merge after fixes. It found no way for a default token to reach another place with the shell as wired. The fixes are in 1fc76d1.
Fixed
A Gitea default token could stay in Credential Manager with nothing in Settings pointing at it (settings.json not written, or written back by an older gclo). It was still used for that server, but not shown and not removable. An address entered in Settings that already has a default token is now taken up as the default it is; the caption says so before Save.
"Open where you left off" is written whenever the window closes (it was skipped when the value equalled the one read at start).
The edit wizard and the "save this connection" wizard take the default token per place, like the wizard for a new account. A caller that knows one default token gives GitHub's, and it is offered and sent for GitHub only. The rule was in untested shell code; it is in the view model, with tests.
Moving the Gitea default to another server stores the new token first and removes the old one after.
Messages that tell two servers apart name the whole address, not the host.
An address typed over and typed back is the server again without leaving the box. A token typed behind the mask in Settings is stored without the mask's dots.
README, ADR 0010, threat model T13, and comments and test names that still said "the default token is GitHub's".
Tests that were missing: servers under a path on one host, a wizard for an account whose place cannot be used, a token typed with the address taken out, path recovery while the address is no place, GitHub having no address to edit. The test on a lookup in flight now fails fast.
Checked
Gates from PowerShell: engine and view models 1,470, CLI 249, line coverage 100% on the three assemblies, format clean, UI suite 18/18. The first run failed on VaultTests.CredentialManager_RoundTrips_AgainstTheRealStore only (the known flaky test against the real store, #190); the rerun passed.
Nine mutations of the new rules, each caught by a test (order of store and remove, taking a token up, writing the address, whole address in captions, saved token without a server, GitHub's default for any place, the mask, both guards on path recovery).
Not done here
The shell changes of items 2 and 6 (window close, mask in the Settings boxes) have no automated test; the typed-back address has a step in the UI suite.
Still owed for the whole feature: a clone and a listing against a live Gitea server.
Independent review of this pull request (and of the last commit of #210): **merge after fixes**. It found no way for a default token to reach another place with the shell as wired. The fixes are in `1fc76d1`.
**Fixed**
1. A Gitea default token could stay in Credential Manager with nothing in Settings pointing at it (`settings.json` not written, or written back by an older gclo). It was still used for that server, but not shown and not removable. An address entered in Settings that already has a default token is now taken up as the default it is; the caption says so before Save.
2. "Open where you left off" is written whenever the window closes (it was skipped when the value equalled the one read at start).
3. The edit wizard and the "save this connection" wizard take the default token per place, like the wizard for a new account. A caller that knows one default token gives GitHub's, and it is offered and sent for GitHub only. The rule was in untested shell code; it is in the view model, with tests.
4. Moving the Gitea default to another server stores the new token first and removes the old one after.
5. Messages that tell two servers apart name the whole address, not the host.
6. An address typed over and typed back is the server again without leaving the box. A token typed behind the mask in Settings is stored without the mask's dots.
7. README, ADR 0010, threat model T13, and comments and test names that still said "the default token is GitHub's".
8. Tests that were missing: servers under a path on one host, a wizard for an account whose place cannot be used, a token typed with the address taken out, path recovery while the address is no place, GitHub having no address to edit. The test on a lookup in flight now fails fast.
**Checked**
- Gates from PowerShell: engine and view models 1,470, CLI 249, line coverage 100% on the three assemblies, format clean, UI suite 18/18. The first run failed on `VaultTests.CredentialManager_RoundTrips_AgainstTheRealStore` only (the known flaky test against the real store, #190); the rerun passed.
- Nine mutations of the new rules, each caught by a test (order of store and remove, taking a token up, writing the address, whole address in captions, saved token without a server, GitHub's default for any place, the mask, both guards on path recovery).
**Not done here**
- The shell changes of items 2 and 6 (window close, mask in the Settings boxes) have no automated test; the typed-back address has a step in the UI suite.
- Still owed for the whole feature: a clone and a listing against a live Gitea server.
Second review, of the fix commit only (d88cf08..1fc76d1): merge after fixes. It confirmed the earlier findings closed (all its earlier surviving mutants are now killed) and found things the fixes themselves caused or left. Those are fixed in the commit just pushed.
Fixed
A window that closes put older settings back. The close-time save wrote the window's whole copy of the settings. With two windows open, the one that closed last dropped what the other had saved in the meantime, including the address of the default Gitea server. What the app remembers by itself is now written over the file as it is on disk at that moment, those values only; a file that cannot be read then is left alone and the log says so.
A lost token could not be taken up while another server was the default. It can now. Taking it up removes the other server's default token (one default Gitea server, as when a token is typed for another server); the caption says so and names the accounts that depend on it.
The token boxes in Settings are no longer rewritten while typed into. The review suspected this moved the caret and scrambled a typed token; I did not test that in the app, I removed the cause. The dots are taken out where the value is read and from what is stored.
The credential store is asked once per server while an address is typed, and a store that cannot be read just then does not break typing.
Two assertions that could not fail were replaced.
Checked
Gates from PowerShell: engine and view models 1,474, CLI 249, line coverage 100% on the three assemblies, format clean, UI suite 18/18.
Eleven mutations of this round's rules. Ten were caught at once; one survived (the Gitea group reading a partly deleted mask as a token for another address), got a test, and is caught now.
Left open
#216: the Settings dialog itself still shows and saves the window's own copy of the settings, so with two windows open it can put back values the other window saved. Not a regression; the fix is either a fresh read in the dialog or one window per data folder, which is the owner's decision.
MissingDescription and the activity log still name a server by host.
The shell handlers (window close, Settings boxes) have no automated test of their own; the rules they call are tested in the view models.
A clone and a listing against a live Gitea server are still owed for the feature as a whole.
Second review, of the fix commit only (`d88cf08..1fc76d1`): **merge after fixes**. It confirmed the earlier findings closed (all its earlier surviving mutants are now killed) and found things the fixes themselves caused or left. Those are fixed in the commit just pushed.
**Fixed**
1. **A window that closes put older settings back.** The close-time save wrote the window's whole copy of the settings. With two windows open, the one that closed last dropped what the other had saved in the meantime, including the address of the default Gitea server. What the app remembers by itself is now written over the file as it is on disk at that moment, those values only; a file that cannot be read then is left alone and the log says so.
2. **A lost token could not be taken up while another server was the default.** It can now. Taking it up removes the other server's default token (one default Gitea server, as when a token is typed for another server); the caption says so and names the accounts that depend on it.
3. **The token boxes in Settings are no longer rewritten while typed into.** The review suspected this moved the caret and scrambled a typed token; I did not test that in the app, I removed the cause. The dots are taken out where the value is read and from what is stored.
4. The credential store is asked once per server while an address is typed, and a store that cannot be read just then does not break typing.
5. Two assertions that could not fail were replaced.
**Checked**
- Gates from PowerShell: engine and view models 1,474, CLI 249, line coverage 100% on the three assemblies, format clean, UI suite 18/18.
- Eleven mutations of this round's rules. Ten were caught at once; one survived (the Gitea group reading a partly deleted mask as a token for another address), got a test, and is caught now.
**Left open**
- #216: the Settings dialog itself still shows and saves the window's own copy of the settings, so with two windows open it can put back values the other window saved. Not a regression; the fix is either a fresh read in the dialog or one window per data folder, which is the owner's decision.
- `MissingDescription` and the activity log still name a server by host.
- The shell handlers (window close, Settings boxes) have no automated test of their own; the rules they call are tested in the view models.
- A clone and a listing against a live Gitea server are still owed for the feature as a whole.
Third review, of the second fix commit only (1fc76d1..4691ad1): merge. One low finding in the new path, fixed in the commit just pushed: a token that is taken up in place of the saved default is looked for again when Save is pressed; if it has gone since its address was typed, nothing is changed and the saved default stays (before, the saved default was removed all the same). Test added, and a mutation of the check is caught by it. Gates from PowerShell: engine and view models 1,476, CLI 249, line coverage 100%, format clean, UI suite 18/18. The review's other finding (a failed write of settings.json after the token was stored is not made good at close) is the subject of #216 and is noted there. Review of this step ends here.
Third review, of the second fix commit only (1fc76d1..4691ad1): **merge**. One low finding in the new path, fixed in the commit just pushed: a token that is taken up in place of the saved default is looked for again when Save is pressed; if it has gone since its address was typed, nothing is changed and the saved default stays (before, the saved default was removed all the same). Test added, and a mutation of the check is caught by it. Gates from PowerShell: engine and view models 1,476, CLI 249, line coverage 100%, format clean, UI suite 18/18. The review's other finding (a failed write of settings.json after the token was stored is not made good at close) is the subject of #216 and is noted there. Review of this step ends here.
Step 5 of Gitea support (#201). Settings was all about GitHub; GitHub and
Gitea are equal options there too.
- Settings has a "Default tokens" section with two groups, named as
everywhere else: GitHub (token) and Gitea (the server's address and its
token). The Gitea address and token are saved, replaced and removed
together; the saved token is never carried to another address, and a token
for an address that cannot be used is not saved (Save is withheld, with the
reason).
- The rule "the default token is a GitHub token" is replaced by: a default
token belongs to one place and is never used for another. It is in the name
of the vault entry: GitHub's is the one it always was, a Gitea server's is
made from the server's address. Asking for the default token of a place
finds the one saved for exactly that place, or none.
- Quick Sync fills in the default token of the place on its card.
- An account that uses the default token gets the one of its own place. The
wizard offers the choice for a place that has one; accounts add and
accounts edit take --use-default-token for any place that has one.
- A workspace is told which place's default token changed, and takes only its
own.
- README, docs/CLI.md, ADR 0010, threat model T16.
Closes#212
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
Findings of the review of the default token per forge (#212).
- A Gitea default token could stay in Credential Manager with nothing in
Settings pointing at it (settings.json not written, or written back by an
older gclo): still used for that server, but not shown and not removable.
An address entered in Settings that already has a default token is now
taken up as the default it is, so it can be seen, replaced and removed.
- Moving the Gitea default to another server stores the new token first and
removes the old one after: a store that fails leaves the old default whole.
- The wizards for editing an account and for saving a Quick Sync connection
take the default token per place, as the wizard for a new account does. A
caller that knows one default token gives GitHub's, and it is offered and
sent for GitHub only. The rule was in the shell; it is in the view model
and has tests.
- "Open where you left off" is written whenever the window closes, not only
when it differs from what was read at the start.
- Messages that tell two servers apart name the whole address, not the host
(two servers can share a host).
- An address typed over and typed back is the server again without the box
being left (Load and Find stayed withheld). A token typed behind the mask
in Settings is stored without the mask's dots.
- A saved token without a server is refused when the Gitea group is made.
- Tests for: servers under a path on one host, a wizard for an account whose
place cannot be used, a token typed with the address taken out, path
recovery while the address is no place, GitHub having no address to edit.
A test on a lookup in flight fails fast instead of waiting for ever.
- README, ADR 0010, threat model T13, comments and test names that still
said "the default token is GitHub's".
Refs #212
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
Findings of the review of the previous fix commit (#212).
- What the app remembers by itself (the workspace that was open, Quick
Sync's connection) is written over settings.json as it is on disk at that
moment, those values only. With two windows open, the one that closed last
wrote its whole older copy, which dropped settings saved in the other in
the meantime, among them the address of the default Gitea server. A file
that cannot be read at that moment is left alone, and the log says so.
- A default token the vault still holds for a server can be taken up while
another server is the default. It was refused, so that token could not be
seen or removed without typing a new one. Taking it up removes the other
server's default token, as typing a token for another server does, and the
caption says so and names the accounts that depend on it.
- The Settings token boxes are no longer rewritten while they are typed into
(that could move the caret and scramble the token). The mask is taken out
where the value is read and from what is stored.
- The vault is asked once for each server while an address is typed, and a
credential store that cannot be read just then does not break typing.
- Two assertions that could not fail are replaced.
- ADR 0010.
Refs #212, #216
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
Last finding of the review of the default token per forge (#212).
Taking up a token the vault still holds for another server removes the
default that is saved. Whether the vault holds that token was asked when the
address was typed. Removed elsewhere before Save (another window, Windows'
Credential Manager), the saved default was still removed, which left no
default token at all under the new address.
The token is looked for again when Save is pressed. Without it nothing is
changed: the address stays, the saved token stays, and the log says so.
Refs #212
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
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.
What
Step 5 of #201, stacked on #214: Settings holds a default token per forge.
DefaultTokens.EntryFor): GitHub's is the one it always was, a Gitea server's is made from the server's address.AccountTokenResolver). The wizard offers the choice for a place that has one.accounts add/accounts edit --use-default-tokenwork for any place that has one.docs/CLI.md, ADR 0010 ("A default token per place"), threat model T16.Why
Closes #212. The owner: GitHub and Gitea are to be equal options throughout, and Settings was all about GitHub.
The rules, pinned in
DefaultTokenPerPlaceTests(and the rewritten tests that used to state the old rule):How it was tested
DefaultTokenPerPlaceTests; nine tests that stated the old rule now state the new one; one new test in the UI suite for the Settings dialog.Not merged until
1.1.1 is tagged (#200) and the pull requests below this one are merged.
Checklist
git commit -S -s)🤖 Generated with Claude Code
Gates on
d88cf08, started from PowerShell: build 0 warnings; engine and view-model tests 1,462; CLI tests 249; 100% line coverage on the three assemblies; format clean; UI suite 18/18 (one new test for the Settings dialog). An independent review follows.Independent review of this pull request (and of the last commit of #210): merge after fixes. It found no way for a default token to reach another place with the shell as wired. The fixes are in
1fc76d1.Fixed
settings.jsonnot written, or written back by an older gclo). It was still used for that server, but not shown and not removable. An address entered in Settings that already has a default token is now taken up as the default it is; the caption says so before Save.Checked
VaultTests.CredentialManager_RoundTrips_AgainstTheRealStoreonly (the known flaky test against the real store, #190); the rerun passed.Not done here
Second review, of the fix commit only (
d88cf08..1fc76d1): merge after fixes. It confirmed the earlier findings closed (all its earlier surviving mutants are now killed) and found things the fixes themselves caused or left. Those are fixed in the commit just pushed.Fixed
Checked
Left open
MissingDescriptionand the activity log still name a server by host.Third review, of the second fix commit only (1fc76d1..4691ad1): merge. One low finding in the new path, fixed in the commit just pushed: a token that is taken up in place of the saved default is looked for again when Save is pressed; if it has gone since its address was typed, nothing is changed and the saved default stays (before, the saved default was removed all the same). Test added, and a mutation of the check is caught by it. Gates from PowerShell: engine and view models 1,476, CLI 249, line coverage 100%, format clean, UI suite 18/18. The review's other finding (a failed write of settings.json after the token was stored is not made good at close) is the subject of #216 and is noted there. Review of this step ends here.