aboutsummaryrefslogtreecommitdiffstats
path: root/docs/superpowers/specs/2026-10-19-sb-builder-phase1-design.md
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-19 19:08:09 +0200
committerDanilo M. <danix@danix.xyz>2026-10-19 19:08:09 +0200
commitd9bb5ebc281f56dad278d890a871964efc4ecd75 (patch)
treedc5b968094b97ac09889149e2a84ef720b03c3fe /docs/superpowers/specs/2026-10-19-sb-builder-phase1-design.md
parentc4437a2c3bd0e9bfa6bd1adbf9cfe14898b26e0b (diff)
downloadsb-builder-d9bb5ebc281f56dad278d890a871964efc4ecd75.tar.gz
sb-builder-d9bb5ebc281f56dad278d890a871964efc4ecd75.zip
Add phase 1 implementation plan, refine spec
The plan embeds code that was run in a scratch copy before writing it down (38 tests, helper stats against the real VM data). Drafting it settled a few details now reflected in the spec: version also returns the overlays, an upstream_branch helper key, a shared fmt module and a launcher, follow resuming on the next jobs poll instead of its own backoff, and sbpull returning sbshadow's exit status. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'docs/superpowers/specs/2026-10-19-sb-builder-phase1-design.md')
-rw-r--r--docs/superpowers/specs/2026-10-19-sb-builder-phase1-design.md54
1 files changed, 33 insertions, 21 deletions
diff --git a/docs/superpowers/specs/2026-10-19-sb-builder-phase1-design.md b/docs/superpowers/specs/2026-10-19-sb-builder-phase1-design.md
index cf13c8c..2364269 100644
--- a/docs/superpowers/specs/2026-10-19-sb-builder-phase1-design.md
+++ b/docs/superpowers/specs/2026-10-19-sb-builder-phase1-design.md
@@ -1,7 +1,7 @@
# sb-builder phase 1: core design
Date: 2026-10-19
-Status: approved in brainstorming, pending spec review
+Status: approved
## Goal
@@ -64,12 +64,14 @@ sb_builder/remote.py the only code that runs ssh; also virsh
sb_builder/mainwindow.py tabs, menu, status bar, notifications
sb_builder/dashboard.py Dashboard tab
sb_builder/jobs.py Jobs tab
+sb_builder/fmt.py text formatting shared by the tabs
+sb-builder launcher (python3 -m sb_builder)
remote/sbb-remote VM helper, single file
config.example.toml GUI config example
remote/config.example.toml helper config example
install.sh local install; --remote deploys helper to VM
tests/test_remote.py
-tests/test_gui_smoke.py
+tests/test_gui.py
```
### Transport
@@ -97,7 +99,8 @@ No shell strings and no paths cross ssh. Validation rules:
### Version check
-On connect the GUI runs `sbb-remote version`. A mismatch with the GUI's own
+On connect the GUI runs `sbb-remote version`, which also returns the
+configured `overlays` (the GUI builds its sbpull menu from them). A mismatch with the GUI's own
version shows a banner telling the user to run `./install.sh --remote`.
## Helper: `sbb-remote`
@@ -122,7 +125,7 @@ All print one JSON object on stdout, except `job-follow`.
| command | behaviour |
|---|---|
-| `version` | `{"version": "..."}` |
+| `version` | `{"version": "...", "overlays": [...]}` |
| `stats` | Dashboard data: load, uptime, disk free for repo and state dirs, package counts (total, personal, pentesting, shadowed = overlay packages also present in the pristine `current` branch, as sbshadow computes it), last sync per overlay (time of the newest commit touching `<overlay>/` in `repo_dir`), slackrepo summary from its sqlite DB (built, failed, never built). Any missing source yields `null` for that field, never an error |
| `jobs` | list of all jobs' meta. A job whose meta says `running` but whose pid is gone (`os.kill(pid, 0)` fails) is reported and rewritten as `lost` |
| `job-start <kind> [args]` | validate; spawn `sbb-remote _run <id>` with `start_new_session=True`, `stdin=/dev/null`, stdout/stderr to `/dev/null`; wait up to 3s for `_run` to write status; return `{"id": ...}`, or `{"busy": "<running id>"}` after deleting the refused job's dir |
@@ -155,11 +158,12 @@ The flag is required in both kinds (a job has no stdin).
### Helper config: `/etc/sb-builder/config.toml`
```toml
-state_dir = "/var/lib/sb-builder"
-repo_dir = "/var/lib/sbopkg/SBo-danix"
-slackrepo_db = "/var/lib/slackrepo/SBo-danix/database_SBo-danix_x86_64.sqlite3"
-sbpull_script = "/usr/local/share/slackrepo_setup/sbpull.bash"
-overlays = ["personal", "pentesting"]
+state_dir = "/var/lib/sb-builder"
+repo_dir = "/var/lib/sbopkg/SBo-danix"
+upstream_branch = "current"
+slackrepo_db = "/var/lib/slackrepo/SBo-danix/database_SBo-danix_x86_64.sqlite3"
+sbpull_script = "/usr/local/share/slackrepo_setup/sbpull.bash"
+overlays = ["personal", "pentesting"]
```
Missing file: these defaults. Unknown key or wrong type: every command
@@ -178,7 +182,11 @@ Done in that repo, its own commit, redeployed to the VM.
`--yes` applies without asking. `--no` prints the plan and leaves the tree
unchanged (preview). `mkhint -F` runs in all cases, as it does today on
abort.
-- `sbpull <target> [--yes|--no]`: passes the flag to `sbshadow`.
+- `sbshadow` honours `SBO_REPO` to override the repo path, so it can be
+ tested against a throwaway repo.
+- `sbpull <target> [--yes|--no]`: passes the flag to `sbshadow`, and returns
+ `sbshadow`'s exit status (today it returns `popd`'s, so a failure would
+ show as a successful job).
GUI flow: `sbpull personal --no` pulls and shows the shadow plan in the log;
the Jobs tab offers "Apply shadowing", which starts `sbshadow --yes`.
@@ -220,9 +228,10 @@ values stay, greyed, with "stale since HH:MM".
`sbshadow --no`, `sbshadow --yes` (confirm dialog).
- After a `--no` run whose log lists `shadow:` or `unshadow:` lines, an
"Apply shadowing" button starts `sbshadow --yes`.
-- Selecting a running job starts `job-follow` from the stored offset. On
- stream drop: reconnect with backoff (2s doubling, max 30s) from the last
- offset.
+- Selecting a job starts `job-follow` from offset 0. If the stream drops
+ while the job is still running, the next successful `jobs` poll restarts
+ it from the last received offset (the poll itself backs off while the VM
+ is down, see Error handling).
- `job-start` returning busy: dialog "busy: <id>" with [Show job].
### Notifications
@@ -276,7 +285,7 @@ settings dialog yet.
| case | behaviour |
|---|---|
-| ssh down | status bar, VM check, stale tiles, retry with backoff |
+| ssh down | status bar, VM check, stale tiles; the `jobs` poll interval doubles from `refresh.jobs` up to 30s, and resets once ssh answers |
| helper missing or version mismatch | banner with `./install.sh --remote` |
| busy | dialog with [Show job] |
| follow drop | reconnect from offset, job unaffected |
@@ -293,18 +302,21 @@ stdlib `unittest`, no frameworks.
config, unknown flag), lock busy, `lost` detection, `job-follow` from an
offset, and a full `_run` of a fake kind patched into the kind table
(ok, failed, killed).
-- `tests/test_gui_smoke.py`: `QT_QPA_PLATFORM=offscreen`, MainWindow with a
- fake Remote; asserts tabs exist and the Dashboard renders a canned `stats`
- dict, including `null` fields.
+- `tests/test_gui.py`: config load and validation, `remote.parse`, GUI and
+ helper versions equal, both example configs equal the defaults. Smoke tests
+ under `QT_QPA_PLATFORM=offscreen` with a fake Remote: tabs, Dashboard from
+ a canned `stats` dict with `null` fields, job-end notification sent once,
+ VM-off offer, carriage-return handling in the log view.
- slackrepo_setup: sbshadow `--yes` and `--no` verified in a throwaway git
repo, the same way the current behaviour was tested.
- First run against the real VM: manual, with the user.
## Install
-- `install.sh`: installs the GUI locally (to `~/.local`, or system-wide as
- root), copies `config.example.toml` to the config path only if no config
- exists.
-- `install.sh --remote`: scp `remote/sbb-remote` to `<target>:/usr/local/bin/`,
+- `install.sh`: symlinks the `sb-builder` launcher into `~/bin` (`BINDIR`
+ overrides), so repo edits are live; copies `config.example.toml` to the
+ config path only if no config exists.
+- `install.sh --remote`: scp `remote/sbb-remote` to `<target>:/usr/local/bin/`
+ (`SBB_TARGET`, default `buildsystem`),
create `/etc/sb-builder/config.toml` from the example only if missing.
Asks before deploying. A SlackBuild comes later.