A workspace reads its account's token when it uses it: when it is shown, before a load, before a sync, and when Sync all reaches it (WorkspaceViewModel.RefreshAccountToken). A token that is gone by then stops the operation with a message naming the source.
The shell tells open workspaces when Settings changed the default token (OnDefaultTokenChanged). Quick Sync follows only while its box still holds what Settings put there.
Account wizard: the choice between the default token and a token of the account's own is also shown for an account that uses the default token while none is saved.
"No token" message: names the credential entry the way the vault in use names it (ITokenVault.DescribeEntry).
After the default token was replaced or removed in Settings, open workspaces kept using the old one until a restart; after a first save, Sync all still skipped the accounts that use it.
How it was tested
WorkspaceViewModelTests: a rotated default token is used by the next load and sync; a token saved after the workspace opened lets it load; a removed default token stops the sync (nothing is cloned with the old one) and the message names Settings; a missing own token names its entry; Quick Sync follows or keeps its token as described; the log says "saved token changed", never "Token entered", and never the value.
SyncAllCoordinatorTests: a token saved after the workspace opened runs; one removed is skipped without a request.
AccountWizardViewModelTests: the dead end at step 2 can be left by choosing a token of the account's own.
AccountTokenResolverTests: the entry name for the default and for a scoped data folder.
Local gates: locked zero-warning x64 build, 923 engine and 164 CLI tests, 100% line coverage on the three gated assemblies, dotnet format --verify-no-changes.
Notes for the reviewer
Shell changes: one binding in AccountWizardDialog.xaml (OffersTokenChoice) and the Settings handler in MainWindow.xaml.cs telling the workspaces. The UI suite has not been run on this branch yet; it will be before merge.
ITokenVault gained DescribeEntry. It is in gclo.ViewModels, which is not a published package.
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 (no change to the attack surface)
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, reviewed by an independent read-only agent before merge
## What
- **A workspace reads its account's token when it uses it**: when it is shown, before a load, before a sync, and when Sync all reaches it (`WorkspaceViewModel.RefreshAccountToken`). A token that is gone by then stops the operation with a message naming the source.
- **The shell tells open workspaces when Settings changed the default token** (`OnDefaultTokenChanged`). Quick Sync follows only while its box still holds what Settings put there.
- **Account wizard**: the choice between the default token and a token of the account's own is also shown for an account that uses the default token while none is saved.
- **"No token" message**: names the credential entry the way the vault in use names it (`ITokenVault.DescribeEntry`).
## Why
Closes #157
After the default token was replaced or removed in Settings, open workspaces kept using the old one until a restart; after a first save, Sync all still skipped the accounts that use it.
## How it was tested
- `WorkspaceViewModelTests`: a rotated default token is used by the next load and sync; a token saved after the workspace opened lets it load; a removed default token stops the sync (nothing is cloned with the old one) and the message names Settings; a missing own token names its entry; Quick Sync follows or keeps its token as described; the log says "saved token changed", never "Token entered", and never the value.
- `SyncAllCoordinatorTests`: a token saved after the workspace opened runs; one removed is skipped without a request.
- `AccountWizardViewModelTests`: the dead end at step 2 can be left by choosing a token of the account's own.
- `AccountTokenResolverTests`: the entry name for the default and for a scoped data folder.
- Local gates: locked zero-warning x64 build, 923 engine and 164 CLI tests, 100% line coverage on the three gated assemblies, `dotnet format --verify-no-changes`.
## Notes for the reviewer
- Shell changes: one binding in `AccountWizardDialog.xaml` (`OffersTokenChoice`) and the Settings handler in `MainWindow.xaml.cs` telling the workspaces. The UI suite has not been run on this branch yet; it will be before merge.
- `ITokenVault` gained `DescribeEntry`. It is in `gclo.ViewModels`, which is not a published package.
## 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 (no change to the attack surface)
- [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, reviewed by an independent read-only agent before merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
An account workspace took its token from the vault in its constructor
and kept it. After the default token was replaced or removed in Settings,
open workspaces went on using the old one until the app was restarted;
after a default token was saved for the first time, Sync all still
skipped the accounts that use it; Quick Sync, which Settings pre-fills,
kept the old value too.
- A workspace reads its account's token from its source again when it is
shown, before it loads, before it syncs, and when Sync all reaches it.
A token that is gone by then stops the operation with a message that
names the source, instead of running with the old one.
- The shell tells the open workspaces when Settings changed the default
token. Quick Sync follows only while its box still holds what Settings
put there; a token somebody typed stays.
- The account wizard shows the choice between the default token and a
token of the account's own also for an account that uses the default
token while none is saved, which was a dead end at step 2.
- The "no token" message names the credential entry the way the vault in
use names it (entry names are scoped to the data folder).
Closes#157
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
- Sync all skips any account whose token is gone, whichever source it
comes from, and reports a sync that could not start as failed, not as
succeeded.
- A sync reads its token before it touches a row: one that cannot start
for want of a token leaves failures, their paths and the retry as they
were. The WSL clone reads the token at use too.
- The organization survives its token going away (the same capture and
restore the successful lookup has).
- A lookup started by reading the token again says nothing: it does not
write the status line or close the error of the load that triggered it.
- The token's source is taken from the account as the store has it now.
- A credential store that does not answer keeps the token read earlier
and is logged; it no longer escapes into Sync all or the shell.
- A saved token of nothing but spaces is no token; Quick Sync with an
empty box fails in its own words.
Refs #157
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
Independent review (read-only agent): merge after fixes
The reviewer ran its own probe tests against the view models. Nothing leaks a token, and the core claim held for load, sync and Sync all. Findings and what was done, in the second commit (the branch was also rebased onto main):
#
Finding
Resolution
M1
Sync all showed a green check for an own-token account whose vault entry was gone (the sync never ran).
Sync all skips any account whose token is gone, and reports a sync that could not start as failed, not counted as run. Tests added.
M2
A sync refused for want of a token had already reset the rows: failures, their paths and Retry were lost.
The token is read first, before any row is touched. Test asserts the row state.
M3
Removing the token could blank the organization through the editable box (by reading).
The organization is captured and restored when the list is cleared, as it already was when a lookup succeeds. Test with a handler that behaves like the box.
L1
The organization lookup started by reading the token again wrote over the status and closed the error of the load that triggered it.
Such a lookup refreshes the list and says nothing. Tests added.
L2
Clone in WSL used the token read earlier.
It reads the token at use. Test added.
L3
The token was resolved against the account record from when the workspace opened.
The account is taken from the store as it is now. Test added.
L4
A credential store that does not answer escaped into Sync all and the shell.
The token read earlier stays in use, and the failure is logged. Test added.
L6
A saved token of only spaces, and Quick Sync with an emptied box, got the engine's raw message.
Both fail in their own words. Tests added.
L5, L7
The connect card's mask and placeholder are set once; no UI test for the Settings handler.
Left for the shell work and the UI coverage work.
Gates on the rebased branch, including the UI suite: 1000 engine, 212 CLI and 12 UI tests, 100% line coverage on the three gated assemblies, zero warnings, format clean.
## Independent review (read-only agent): merge after fixes
The reviewer ran its own probe tests against the view models. Nothing leaks a token, and the core claim held for load, sync and Sync all. Findings and what was done, in the second commit (the branch was also rebased onto `main`):
| # | Finding | Resolution |
| --- | --- | --- |
| M1 | Sync all showed a green check for an own-token account whose vault entry was gone (the sync never ran). | Sync all skips any account whose token is gone, and reports a sync that could not start as failed, not counted as run. Tests added. |
| M2 | A sync refused for want of a token had already reset the rows: failures, their paths and Retry were lost. | The token is read first, before any row is touched. Test asserts the row state. |
| M3 | Removing the token could blank the organization through the editable box (by reading). | The organization is captured and restored when the list is cleared, as it already was when a lookup succeeds. Test with a handler that behaves like the box. |
| L1 | The organization lookup started by reading the token again wrote over the status and closed the error of the load that triggered it. | Such a lookup refreshes the list and says nothing. Tests added. |
| L2 | Clone in WSL used the token read earlier. | It reads the token at use. Test added. |
| L3 | The token was resolved against the account record from when the workspace opened. | The account is taken from the store as it is now. Test added. |
| L4 | A credential store that does not answer escaped into Sync all and the shell. | The token read earlier stays in use, and the failure is logged. Test added. |
| L6 | A saved token of only spaces, and Quick Sync with an emptied box, got the engine's raw message. | Both fail in their own words. Tests added. |
| L5, L7 | The connect card's mask and placeholder are set once; no UI test for the Settings handler. | Left for the shell work and the UI coverage work. |
Gates on the rebased branch, including the UI suite: 1000 engine, 212 CLI and 12 UI tests, 100% line coverage on the three gated assemblies, zero warnings, format clean.
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
WorkspaceViewModel.RefreshAccountToken). A token that is gone by then stops the operation with a message naming the source.OnDefaultTokenChanged). Quick Sync follows only while its box still holds what Settings put there.ITokenVault.DescribeEntry).Why
Closes #157
After the default token was replaced or removed in Settings, open workspaces kept using the old one until a restart; after a first save, Sync all still skipped the accounts that use it.
How it was tested
WorkspaceViewModelTests: a rotated default token is used by the next load and sync; a token saved after the workspace opened lets it load; a removed default token stops the sync (nothing is cloned with the old one) and the message names Settings; a missing own token names its entry; Quick Sync follows or keeps its token as described; the log says "saved token changed", never "Token entered", and never the value.SyncAllCoordinatorTests: a token saved after the workspace opened runs; one removed is skipped without a request.AccountWizardViewModelTests: the dead end at step 2 can be left by choosing a token of the account's own.AccountTokenResolverTests: the entry name for the default and for a scoped data folder.dotnet format --verify-no-changes.Notes for the reviewer
AccountWizardDialog.xaml(OffersTokenChoice) and the Settings handler inMainWindow.xaml.cstelling the workspaces. The UI suite has not been run on this branch yet; it will be before merge.ITokenVaultgainedDescribeEntry. It is ingclo.ViewModels, which is not a published package.Checklist
git commit -S -s)🤖 Generated with Claude Code
84b702c4e3to02bd751357Independent review (read-only agent): merge after fixes
The reviewer ran its own probe tests against the view models. Nothing leaks a token, and the core claim held for load, sync and Sync all. Findings and what was done, in the second commit (the branch was also rebased onto
main):Gates on the rebased branch, including the UI suite: 1000 engine, 212 CLI and 12 UI tests, 100% line coverage on the three gated assemblies, zero warnings, format clean.