feat(client): workspace bootstrap — what a new computer pulls first

granthi-sync bootstrap <folder> reads a workspace.json and pulls the repos it
names, still bounded by what the forge grants: a manifest naming a repo this
account cannot see prints NOT GRANTED and continues, because that is a
permissions answer, not an error to route around.

It does NOT install applications. Dash, deck, genie and shiva each have their
own repo, deploy path and reviewers; a sync client installing them would
create a second unreviewed deploy path beside the real one. So apps are
REFERENCED -- the command prints each app's repo, model, setup doc and the
vault keys it needs, with the shre-cred line to supply them.

Per-repo mode overrides the default, so a documents folder can be declared
mirror while real projects stay on the safe snapshot default.

184 tests (was 178).
This commit is contained in:
claude
2026-08-23 13:45:05 -04:00
parent 4ba1256894
commit 24a7fa6cdb
3 changed files with 257 additions and 1 deletions
+30 -1
View File
@@ -412,7 +412,7 @@ deleted it again, `DELETE …/tokens/{id}` returning 204 under basic auth):
## Tests
* `python3 -m unittest discover -s tests` — 172 tests. The v1.2 additions
* `python3 -m unittest discover -s tests` — 184 tests. The v1.2 additions
cover: a snapshot capturing uncommitted work while HEAD, the index and the
working tree stay byte-identical; snapshots landing outside `refs/heads`;
an unchanged tree not being re-pushed; a diverged folder still being backed
@@ -512,6 +512,35 @@ holding a working forge token, and **the forge decides** whose it is
(`GET /api/v1/user`). No login is ever read from the request body, so a body
claiming another account changes nothing.
## Workspace bootstrap — what a new computer should pull first
granthi-sync bootstrap ~/granthi/acme --into ~/work
A workspace is a repo holding a `workspace.json`:
```json
{"name": "acme",
"repos": ["notes", {"name": "reports", "mode": "mirror"}],
"apps": [{"id": "dash", "repo": "nirpa/hermes-agent", "model": "aum/70b",
"setup": "docs/SETUP.md", "needs_keys": ["anthropic"]}]}
```
`bootstrap` pulls the repos it names — **still only the ones the forge grants
this account**; a manifest asking for a repo you cannot see prints
`NOT GRANTED` and moves on, because that is a permissions answer and not an
error to route around.
**It does not install applications, and that is the design, not a shortcut.**
Dash, deck, genie and shiva each have their own repo, deploy path and
reviewers. A sync client that installed them would create a second,
unreviewed deploy path beside the real one — the same copy-instead-of-refer
mistake the estate rulebook exists to end. So `apps` is *referenced*: the
command prints each app's repo, the model it uses, its setup doc, and which
vault keys must exist, with the `shre-cred request` line to supply them.
Per-repo `mode` in the manifest overrides the default, so a documents folder
can be declared `mirror` while everything else stays on the safe `snapshot`.
## Next phase — invites and per-repo access (designed, not built)
Today `/v1/link` creates an account and every folder becomes a private repo