diff --git a/.pi/memory-vault/packs/paperclip-harness/20-context/nix.md b/.pi/memory-vault/packs/paperclip-harness/20-context/nix.md index 7c8a660..896948a 100644 --- a/.pi/memory-vault/packs/paperclip-harness/20-context/nix.md +++ b/.pi/memory-vault/packs/paperclip-harness/20-context/nix.md @@ -211,3 +211,116 @@ Say which you want and I will do only that. - session: [[packs/paperclip-harness/80-sessions/nixos-13-outage-postmortem-nixos-rebuild-switch-broke-static|NixOS .13 outage postmortem: nixos-rebuild switch broke static network via NetworkManager race]] - session: [[packs/paperclip-harness/80-sessions/rich-discovery-35-2026-11-12-03-14-45|Rich discovery: .35 2026-11-12 03:14:45]] - context: [[packs/paperclip-harness/20-context/nix|.35]] + + +--- + +## Update (2026-11-12 05:27:33) + +## Rich discovery update (2026-11-12 05:27:33) + +## Is everything working — measured just now + +| | State | Evidence | +|---|---|---| +| **Pi** | 1.5.0 on all three, same bytes | `.13`, `.27`, `.51` all resolve to `/nix/store/4yxlfj0…-pi-coding-agent-1.5.0` | +| **Paperclip** | running, `2026.916.1` | service active, `GET /api/health` 200, web UI 200 at `http://127.0.0.1:3100/` | +| **Agent runs** | working again | 3 runs failed before the shim, then succeeded (69 s, then 24 s) | +| **omniroute** | working | `/v1/models` returns **486** models, a direct completion returns 200 | +| **headroom** | tool yes, provider no | `/v1/compress` 200 with `messages`; `/v1/chat/completions` returns **502** for both `auto/cheap` and `deepseek/deepseek-v4-flash`; `/stats` shows 153 requests, 2 compressed | +| **TUIOS** | 0.12.1 | both sessions' 8 panes verified by process: each runs `bin/harness -screen …` | + +One thing worth knowing about headroom: my earlier `compress` call returned 400 because I sent `text`; it wants `messages`. Tool path is fine. Provider path is still broken for the same reason as before — its LiteLLM has no entry for the router's model ids. + +## CLI versus web — they are the same thing + +There is no separate web backend. Port 3100 serves the HTML at `/` and the JSON at `/api/...` from one process and one Postgres database. So a cost problem on the web is an **API and aggregation** problem, not a web problem — and you can see the same fault in my terminal panes, which is how I found it. + +Two separate faults, both measured: + +**1. `costCents` is a whole number, so small tasks show zero.** + +``` +AUD-173 costCents 0 inputTokens 13023 outputTokens 2253 + usageJson.costUsd = 0.001934786 → 0.19 cents → displayed as 0 +AUD-163 costCents 3 303609 input tokens → 2.9 cents → displayed as 3 +``` + +Any task that costs less than half a cent reads as free. On the web that looks like "costs aren't working"; the exact figure is in each run's `usageJson`. + +**2. One task's summary is stale and I cannot explain it away.** + +``` +AUD-172 costCents 0 inputTokens 0 outputTokens 0 runCount 4 runtimeMs 81645 + its successful run's usageJson: 34217 input, 7187 output, $0.014134 +``` + +`runtimeMs` proves the summary did aggregate all four runs. But tokens and cost are zero, while the run record holds both. So the per-task summary was computed before that run's usage was written and never recomputed. AUD-173 and AUD-163 summarise correctly, so it is not universal. + +This is a Paperclip-side defect, in the endpoint the web uses. `docs/NEXT.md` records it with these numbers. I am not going to patch Paperclip's internals without you asking me to. + +## `office --help` + +``` +office — open a folder as a project, and say what else can be run + +THE RULE + The folder and the session live on the same machine. … +WHEN IT DOES / WHAT IT WRITES +COMMANDS office, board, board --replace, board --reset, project-reset, + merge, review, triage — one line each +MACHINES nixos-desktop .13 192.168.20.13 + sam-4screen-desktop .27 192.168.20.27 + sam-thinkpad .51 192.168.20.51 +EVERY MACHINE, FROM THE TERMINAL a worked example for .13, .27 and .51 +ON ANOTHER MACHINE where the copy lives +AFTER A REBOOT run board once per project +KEYS INSIDE THE PANES ( ) tab, tab panel, r, R, ? +MORE the four docs to read next +``` + +## While writing it I found `--host` could never have worked + +It ran `office` on the other machine at *this* machine's path, so it needed the repository to exist there at the same location. `.27` and `.51` have no repository at all. It also checked the folder existed **locally** before deciding the folder was remote, so it always answered "no folder at …". + +Both fixed. `--host` now copies `bin/` to `~/.local/share/paperclip-harness/bin/` on the other machine, leaves a short `~/.local/bin/office` there, and the folder is checked on the machine that owns it. Verified against both machines: + +``` +$ office /tmp/office-remote-test --new office-remote-test --host sam-4screen-desktop --dry +on sam-4screen-desktop: exec "$HOME/.local/share/paperclip-harness/bin/office" … +would create a Paperclip project 'office-remote-test' for /tmp/office-remote-test +dry run: nothing was written +switch to it: tuios attach --host sam-4screen-desktop office-remote-test +``` + +`.27` and `.51` now have the commands and a working `--help`. One gap remains: **`~/.local/bin` is not on `PATH` on either machine**, so bare `office` does not resolve there — the full path does: + +```sh +~/.local/share/paperclip-harness/bin/office +``` + +Fixing that is one line in each machine's home-manager config (`home.sessionPath = [ "$HOME/.local/bin" ]`), and one rebuild. Say the word and I will add it to both and hand you the two commands. + +Committed and pushed: `paperclip-harness` → `3043e9d`. + +## Source paths observed + +- `HOME/.local/share/paperclip-harness/bin/office` +- `tmp/office-remote-test/.harness/harness.toml` +- `.local/bin/office` +- `.local/share/paperclip-harness/bin` +- `.local/share/paperclip-harness/bin/office` +- `nix/store/4yxlfj0` +- `v1/chat/completions` +- `HOME/.local/bin` + +## Evidence + +- Confidence: 84% +- Reason: Update component/repository context with detailed discovered structure and configuration + +## Related learned memories + +- session: [[packs/paperclip-harness/80-sessions/rich-discovery-nix-2026-11-12-05-27-33|Rich discovery: nix 2026-11-12 05:27:33]] +- context: [[packs/paperclip-harness/20-context/nix|nix]] +- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|nix repository structure and configuration patterns]] diff --git a/.pi/memory-vault/packs/paperclip-harness/20-context/paperclip-harness.md b/.pi/memory-vault/packs/paperclip-harness/20-context/paperclip-harness.md index 64dd68a..d2408c4 100644 --- a/.pi/memory-vault/packs/paperclip-harness/20-context/paperclip-harness.md +++ b/.pi/memory-vault/packs/paperclip-harness/20-context/paperclip-harness.md @@ -910,3 +910,34 @@ Open issues: pi_local runs report costCents 0 / outputTokens 0 / usageJson null - action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Fixed four harness defects, verified clean tests, pushed 6c6bacc]] - session: [[packs/paperclip-harness/80-sessions/rich-discovery-session-2026-11-12-04-50-42|Rich discovery: --session 2026-11-12 04:50:42]] - context: [[packs/paperclip-harness/20-context/omni|--session]] + + +--- + +## Update (2026-11-12 06:13:12) + +Multi-host NixOS dev setup used to drive `office` CLI work across three machines. + +Hosts and roles: +- 192.168.20.13 (host alias `nixos-desktop`): primary desktop; had ~/.local/bin on home-manager PATH all along (so `office` works there). Source of folders when driving other hosts: `office --project "" --host nixos-desktop`. +- 192.168.20.27 (sam-4screen-desktop): 4-screen desktop; Android SDK lives outside Nix at ~/Android/Sdk (nix-ld block in configuration.nix), with cmdline-tools/latest/bin and platform-tools on sessionPath. Also has ~/bin on PATH. +- 192.168.20.51 (sam-thinkpad): laptop; minimal sessionPath. + +Config layout: /etc/nixos/home/sam/home.nix per host (home-manager). Common sessionVariables: QT_QPA_PLATFORM=wayland, QT_WAYLAND_DISABLE_WINDOWDECORATION=1, GOOSE_DISABLE_KEYRING=1, NIXOS_OZONE_WL=1 (Electron/Obsidian), FALCODE_NOTIFY_SCRIPT=/bin/true. Both desktops use gtk Adwaita-dark via gnome-themes-extra. + +office CLI: installed at ~/.local/share/paperclip-harness/bin/office with wrapper at ~/.local/bin/office. Supports `--host ` to copy commands to another host, and `--new` / `--project` flags. Repo commit 3043e9d fixed `office --help` and `--host`. + +Operational note: home-manager-only changes are safe to `nixos-rebuild switch` (only home-manager-sam.service restarts); avoid changes that move nixpkgs system packages or touch networking unless intended. + +## Evidence + +- Confidence: 80% +- Reason: Durable component/fleet overview: host roles, config layout, shared env vars, CLI install paths, and safe-rebuild convention. + +## Related learned memories + +- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]] +- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|NixOS host PATH diffs and prebuilt switch closures]] +- runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|Rebuild .27 and .51 after adding ~/.local/bin to home-manager PATH]] +- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Added ~/.local/bin to PATH on .27/.51 and committed; .13 untouched]] +- session: [[packs/paperclip-harness/80-sessions/nixos-fleet-path-fix-rollout-27-and-51-need-home-rebuild-13-|NixOS fleet PATH fix rollout: .27 and .51 need home rebuild, .13 already correct]] diff --git a/.pi/memory-vault/packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups.md b/.pi/memory-vault/packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups.md index ae0e234..dea1ab5 100644 --- a/.pi/memory-vault/packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups.md +++ b/.pi/memory-vault/packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups.md @@ -1260,3 +1260,35 @@ Next suggested work: cost capture. - action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Fixed four harness defects, verified clean tests, pushed 6c6bacc]] - session: [[packs/paperclip-harness/80-sessions/rich-discovery-session-2026-11-12-04-50-42|Rich discovery: --session 2026-11-12 04:50:42]] - context: [[packs/paperclip-harness/20-context/omni|--session]] + + +--- + +## Update (2026-11-12 06:13:12) + +Deliverable: put `~/.local/bin` on home-manager PATH on the two NixOS hosts missing it so the paperclip-harness `office` wrapper becomes reachable. + +Changes: one line in /etc/nixos/home/sam/home.nix on each of 192.168.20.27 (sam-4screen-desktop) and 192.168.20.51 (sam-thinkpad): prepend `${config.home.homeDirectory}/.local/bin` to `home.sessionPath`. 192.168.20.13 (nixos-desktop) required no change. + +Artifacts / commits: +- .27: master c2d10d6 "home: put ~/.local/bin on PATH" (pushed f56f053..c2d10d6) +- .51: main ea2cb86 "home: put ~/.local/bin on PATH" (pushed 40ccd7e..ea2cb86) +- paperclip-harness: 3043e9d (office --help / --host fix) +- Backups: home/sam/home.nix.bak-path on both hosts + +Validation: both builds succeeded; closures produced for sam-4screen-desktop and sam-thinkpad. `switch` not yet confirmed by the user; post-switch checks are `grep -c '.local/bin'` on PATH and `office --help` in a new shell. + +Follow-up: machines can then drive each other via `office --project|--new ... --host `; --host copies commands, so a host that never saw the repo still works. + +## Evidence + +- Confidence: 90% +- Reason: Concrete completed edits, commit hashes, build validation, and pending verification steps. + +## Related learned memories + +- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]] +- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|NixOS host PATH diffs and prebuilt switch closures]] +- runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|Rebuild .27 and .51 after adding ~/.local/bin to home-manager PATH]] +- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Added ~/.local/bin to PATH on .27/.51 and committed; .13 untouched]] +- session: [[packs/paperclip-harness/80-sessions/nixos-fleet-path-fix-rollout-27-and-51-need-home-rebuild-13-|NixOS fleet PATH fix rollout: .27 and .51 need home rebuild, .13 already correct]] diff --git a/.pi/memory-vault/packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns.md b/.pi/memory-vault/packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns.md index fc96c9d..575b60c 100644 --- a/.pi/memory-vault/packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns.md +++ b/.pi/memory-vault/packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns.md @@ -5399,3 +5399,213 @@ The one thing I would do next, if you agree, is the cost capture: it is the only - action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Fixed four harness defects, verified clean tests, pushed 6c6bacc]] - session: [[packs/paperclip-harness/80-sessions/rich-discovery-session-2026-11-12-04-50-42|Rich discovery: --session 2026-11-12 04:50:42]] - context: [[packs/paperclip-harness/20-context/omni|--session]] + + +--- + +## Update (2026-11-12 05:27:33) + +## Structural/configuration observations (2026-11-12 05:27:33) + +## Is everything working — measured just now + +| | State | Evidence | +|---|---|---| +| **Pi** | 1.5.0 on all three, same bytes | `.13`, `.27`, `.51` all resolve to `/nix/store/4yxlfj0…-pi-coding-agent-1.5.0` | +| **Paperclip** | running, `2026.916.1` | service active, `GET /api/health` 200, web UI 200 at `http://127.0.0.1:3100/` | +| **Agent runs** | working again | 3 runs failed before the shim, then succeeded (69 s, then 24 s) | +| **omniroute** | working | `/v1/models` returns **486** models, a direct completion returns 200 | +| **headroom** | tool yes, provider no | `/v1/compress` 200 with `messages`; `/v1/chat/completions` returns **502** for both `auto/cheap` and `deepseek/deepseek-v4-flash`; `/stats` shows 153 requests, 2 compressed | +| **TUIOS** | 0.12.1 | both sessions' 8 panes verified by process: each runs `bin/harness -screen …` | + +One thing worth knowing about headroom: my earlier `compress` call returned 400 because I sent `text`; it wants `messages`. Tool path is fine. Provider path is still broken for the same reason as before — its LiteLLM has no entry for the router's model ids. + +## CLI versus web — they are the same thing + +There is no separate web backend. Port 3100 serves the HTML at `/` and the JSON at `/api/...` from one process and one Postgres database. So a cost problem on the web is an **API and aggregation** problem, not a web problem — and you can see the same fault in my terminal panes, which is how I found it. + +Two separate faults, both measured: + +**1. `costCents` is a whole number, so small tasks show zero.** + +``` +AUD-173 costCents 0 inputTokens 13023 outputTokens 2253 + usageJson.costUsd = 0.001934786 → 0.19 cents → displayed as 0 +AUD-163 costCents 3 303609 input tokens → 2.9 cents → displayed as 3 +``` + +Any task that costs less than half a cent reads as free. On the web that looks like "costs aren't working"; the exact figure is in each run's `usageJson`. + +**2. One task's summary is stale and I cannot explain it away.** + +``` +AUD-172 costCents 0 inputTokens 0 outputTokens 0 runCount 4 runtimeMs 81645 + its successful run's usageJson: 34217 input, 7187 output, $0.014134 +``` + +`runtimeMs` proves the summary did aggregate all four runs. But tokens and cost are zero, while the run record holds both. So the per-task summary was computed before that run's usage was written and never recomputed. AUD-173 and AUD-163 summarise correctly, so it is not universal. + +This is a Paperclip-side defect, in the endpoint the web uses. `docs/NEXT.md` records it with these numbers. I am not going to patch Paperclip's internals without you asking me to. + +## `office --help` + +``` +office — open a folder as a project, and say what else can be run + +THE RULE + The folder and the session live on the same machine. … +WHEN IT DOES / WHAT IT WRITES +COMMANDS office, board, board --replace, board --reset, project-reset, + merge, review, triage — one line each +MACHINES nixos-desktop .13 192.168.20.13 + sam-4screen-desktop .27 192.168.20.27 + sam-thinkpad .51 192.168.20.51 +EVERY MACHINE, FROM THE TERMINAL a worked example for .13, .27 and .51 +ON ANOTHER MACHINE where the copy lives +AFTER A REBOOT run board once per project +KEYS INSIDE THE PANES ( ) tab, tab panel, r, R, ? +MORE the four docs to read next +``` + +## While writing it I found `--host` could never have worked + +It ran `office` on the other machine at *this* machine's path, so it needed the repository to exist there at the same location. `.27` and `.51` have no repository at all. It also checked the folder existed **locally** before deciding the folder was remote, so it always answered "no folder at …". + +Both fixed. `--host` now copies `bin/` to `~/.local/share/paperclip-harness/bin/` on the other machine, leaves a short `~/.local/bin/office` there, and the folder is checked on the machine that owns it. Verified against both machines: + +``` +$ office /tmp/office-remote-test --new office-remote-test --host sam-4screen-desktop --dry +on sam-4screen-desktop: exec "$HOME/.local/share/paperclip-harness/bin/office" … +would create a Paperclip project 'office-remote-test' for /tmp/office-remote-test +dry run: nothing was written +switch to it: tuios attach --host sam-4screen-desktop office-remote-test +``` + +`.27` and `.51` now have the commands and a working `--help`. One gap remains: **`~/.local/bin` is not on `PATH` on either machine**, so bare `office` does not resolve there — the full path does: + +```sh +~/.local/share/paperclip-harness/bin/office +``` + +Fixing that is one line in each machine's home-manager config (`home.sessionPath = [ "$HOME/.local/bin" ]`), and one rebuild. Say the word and I will add it to both and hand you the two commands. + +Committed and pushed: `paperclip-harness` → `3043e9d`. + +## Source paths observed + +- `HOME/.local/share/paperclip-harness/bin/office` +- `tmp/office-remote-test/.harness/harness.toml` +- `.local/bin/office` +- `.local/share/paperclip-harness/bin` +- `.local/share/paperclip-harness/bin/office` +- `nix/store/4yxlfj0` +- `v1/chat/completions` +- `HOME/.local/bin` + +## Evidence + +- Confidence: 82% +- Reason: Captured factual repo structure/configuration patterns from rich discovery + +## Related learned memories + +- session: [[packs/paperclip-harness/80-sessions/rich-discovery-nix-2026-11-12-05-27-33|Rich discovery: nix 2026-11-12 05:27:33]] +- context: [[packs/paperclip-harness/20-context/nix|nix]] +- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|nix repository structure and configuration patterns]] + + +--- + +## Update (2026-11-12 06:13:12) + +Before fix, only .13 had `~/.local/bin` in home.sessionPath; .27 and .51 did not, which is why `office` failed there despite the wrapper existing. + +Post-patch sessionPath: +- .27: ${home}/.local/bin, ${home}/.local/share/npm-global/bin, ${home}/bin, Android SDK cmdline-tools/latest/bin, Android SDK platform-tools +- .51: ${home}/.local/bin, ${home}/.local/share/npm-global/bin +- .13 (unchanged): /home/sam/.cargo/bin, /home/sam/.local/bin, $HOME/.local/share/npm-global/bin + +Build outputs already present so switch is fast: +- .27: /nix/store/3g5j39n0kxdfzv7l311vckczmsz2ca17-nixos-system-sam-4screen-desktop-26.11.20260729.0954f7e +- .51: /nix/store/hl6mby792nq37d5cm87nk567jck86d9w-nixos-system-sam-thinkpad-26.11.20260926.e158d9e + +Caveat: existing interactive shells keep the old PATH; verification must be in a new shell. + +## Detailed observations from completed turn + +## Rebuild these two machines only + +**`.27` (sam-4screen-desktop):** + +```sh +sudo nixos-rebuild switch --flake /etc/nixos#sam-4screen-desktop +``` + +**`.51` (sam-thinkpad):** + +```sh +sudo nixos-rebuild switch --flake /etc/nixos#sam-thinkpad +``` + +**`.13` needs nothing.** It already has `/home/sam/.local/bin` on `PATH` — that is why `office` works there — and I changed nothing else on it. + +## What I changed, and why `switch` is safe this time + +One line in each machine's `home/sam/home.nix`: + +```nix +home.sessionPath = [ + "${config.home.homeDirectory}/.local/bin" + ... +]; +``` + +That is a home-manager setting, not a nixpkgs change. No system package moves, so the only unit that restarts is `home-manager-sam.service`. Unlike the last time, nothing on the network side is touched. + +Both systems are **already built**, so each `switch` is seconds, not a download: + +``` +.27 /nix/store/3g5j39n0kxdfzv7l311vckczmsz2ca17-nixos-system-sam-4screen-desktop-26.11.20260729.0954f7e +.51 /nix/store/hl6mby792nq37d5cm87nk567jck86d9w-nixos-system-sam-thinkpad-26.11.20260926.e158d9e +``` + +## What to check afterwards + +On each of `.27` and `.51`, in a **new** shell (the old one keeps the old `PATH`): + +```sh +echo $PATH | tr ':' '\n' | grep -c '.local/bin' # expect 1 +office --help # should print the help +``` + +`office` was already copied to both machines as `~/.local/share/paperclip-harness/bin/office`, with a wrapper at `~/.local/bin/office`. The wrapper was there but unreachable; this rebuild is what puts it on `PATH`. + +From then on, every machine can drive every other one: + +```sh +office --project "" --host nixos-desktop # folder on .13 +office --new "" --host sam-thinkpad # folder on .51 +``` + +`--host` copies the commands to the named machine each time, so a machine that has never seen this repository still works. + +## Committed and pushed + +- `.27` → `c2d10d6` on `nixos-4screen` +- `.51` → `ea2cb86` on `nixos-thinkpad` +- `paperclip-harness` → `3043e9d` (the `office --help` and `--host` fix) + +Backups kept as `home/sam/home.nix.bak-path` on both, if you want to see the exact diff before switching. + +## Evidence + +- Confidence: 85% +- Reason: Factual per-host config deltas, exact store paths, and a practical PATH caveat.; enriched with final-answer detail because persistence should be reusable + +## Related learned memories + +- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]] +- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|NixOS host PATH diffs and prebuilt switch closures]] +- runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|Rebuild .27 and .51 after adding ~/.local/bin to home-manager PATH]] +- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Added ~/.local/bin to PATH on .27/.51 and committed; .13 untouched]] +- session: [[packs/paperclip-harness/80-sessions/nixos-fleet-path-fix-rollout-27-and-51-need-home-rebuild-13-|NixOS fleet PATH fix rollout: .27 and .51 need home rebuild, .13 already correct]] diff --git a/.pi/memory-vault/packs/paperclip-harness/70-runbooks/tmp-deployment-operations.md b/.pi/memory-vault/packs/paperclip-harness/70-runbooks/tmp-deployment-operations.md index b0af5ef..d262798 100644 --- a/.pi/memory-vault/packs/paperclip-harness/70-runbooks/tmp-deployment-operations.md +++ b/.pi/memory-vault/packs/paperclip-harness/70-runbooks/tmp-deployment-operations.md @@ -1640,3 +1640,43 @@ Rollback: restore the `.bak-*` files and `git checkout` the previous commit per - session: [[packs/paperclip-harness/80-sessions/pi-1-5-0-alignment-across-three-nixos-hosts-via-pinned-nixpk|Pi 1.5.0 alignment across three NixOS hosts via pinned nixpkgs-pi input]] - session: [[packs/paperclip-harness/80-sessions/rich-discovery-nixos-2026-11-12-03-29-14|Rich discovery: NixOS 2026-11-12 03:29:14]] - context: [[packs/paperclip-harness/20-context/home|NixOS]] + + +--- + +## Update (2026-11-12 06:13:12) + +Trigger: `office` is not found on a NixOS box because `~/.local/bin` is missing from home-manager's sessionPath. + +Prerequisites: SSH access to the host; edits already made to /etc/nixos/home/sam/home.nix; a backup at home/sam/home.nix.bak-path. + +Which machines: only 192.168.20.27 (sam-4screen-desktop) and 192.168.20.51 (sam-thinkpad) need the rebuild. 192.168.20.13 (nixos-desktop) already had ~/.local/bin on PATH and was not changed. + +Steps: +1. On .27: `sudo nixos-rebuild switch --flake /etc/nixos#sam-4screen-desktop` +2. On .51: `sudo nixos-rebuild switch --flake /etc/nixos#sam-thinkpad` + Both are pre-built (no download); typically seconds. +3. Verify in a NEW shell (old shell keeps stale PATH): + `echo $PATH | tr ':' '\n' | grep -c '.local/bin'` -> expect 1 + `office --help` -> should print help + +Expected system closures: .27 -> ...-nixos-system-sam-4screen-desktop-26.11.20260729.0954f7e; .51 -> ...-nixos-system-sam-thinkpad-26.11.20260926.e158d9e. + +Scope/safety: home-manager-only change (home.sessionPath), so only home-manager-sam.service restarts. No system package moves and no network changes. + +Rollback: restore home/sam/home.nix.bak-path over home/sam/home.nix and re-run the same nixos-rebuild switch command on that host (or git revert the commit: .27 c2d10d6, .51 ea2cb86). + +Troubleshooting: if `office` still isn't found, confirm wrapper exists at ~/.local/bin/office pointing at ~/.local/share/paperclip-harness/bin/office, then open a fresh login shell. + +## Evidence + +- Confidence: 90% +- Reason: Repeatable, evidence-backed multi-host procedures with exact commands, verification, expected outputs, and rollback. + +## Related learned memories + +- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]] +- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|NixOS host PATH diffs and prebuilt switch closures]] +- runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|Rebuild .27 and .51 after adding ~/.local/bin to home-manager PATH]] +- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Added ~/.local/bin to PATH on .27/.51 and committed; .13 untouched]] +- session: [[packs/paperclip-harness/80-sessions/nixos-fleet-path-fix-rollout-27-and-51-need-home-rebuild-13-|NixOS fleet PATH fix rollout: .27 and .51 need home rebuild, .13 already correct]] diff --git a/.pi/memory-vault/packs/paperclip-harness/80-sessions/nixos-fleet-path-fix-rollout-27-and-51-need-home-rebuild-13-.md b/.pi/memory-vault/packs/paperclip-harness/80-sessions/nixos-fleet-path-fix-rollout-27-and-51-need-home-rebuild-13-.md new file mode 100644 index 0000000..6bd84f2 --- /dev/null +++ b/.pi/memory-vault/packs/paperclip-harness/80-sessions/nixos-fleet-path-fix-rollout-27-and-51-need-home-rebuild-13-.md @@ -0,0 +1,66 @@ +--- +type: session +id: session.paperclip-harness.nixos-fleet-path-fix-rollout-27-and-51-need-home-rebuild-13- +title: NixOS fleet PATH fix rollout: .27 and .51 need home rebuild, .13 already correct +status: active +source_of_truth: false +freshness: current +last_reviewed: 2026-11-12 +tags: + - pack/paperclip-harness + - agent-memory/session + - nixos + - home-manager + - paperclip-harness + - office-cli + - flux-fleet + - path + - rebuild +--- + +# NixOS fleet PATH fix rollout: .27 and .51 need home rebuild, .13 already correct + +Goal: make the `office` CLI (from paperclip-harness) reachable on all three NixOS machines by getting `~/.local/bin` onto home-manager PATH. + +Findings per machine (config at /etc/nixos/home/sam/home.nix on each): +- 192.168.20.13 (nixos-desktop): already had `/home/sam/.local/bin` in `home.sessionPath` (line ~4, alongside /home/sam/.cargo/bin and npm-global bin). Why `office` already works there. NOT changed. +- 192.168.20.27 (sam-4screen-desktop): sessionPath originally only npm-global bin + $HOME/bin + Android SDK paths; missing `~/.local/bin`. PATCHED. +- 192.168.20.51 (sam-thinkpad): sessionPath originally only npm-global bin; missing `~/.local/bin`. PATCHED. + +Patch applied (one line, prefixed first in home.sessionPath using `${config.home.homeDirectory}/.local/bin`): +- .27 resulting list: .local/bin, .local/share/npm-global/bin, ~/bin, Android SDK cmdline-tools + platform-tools (with comment that Android SDK is NOT Nix-managed, see nix-ld block in configuration.nix). +- .51 resulting list: .local/bin, .local/share/npm-global/bin. + +Shared home.sessionVariables on both: QT_QPA_PLATFORM=wayland, QT_WAYLAND_DISABLE_WINDOWDECORATION=1, GOOSE_DISABLE_KEYRING=1, NIXOS_OZONE_WL=1, FALCODE_NOTIFY_SCRIPT=/bin/true. .27 additionally sets ANDROID_HOME / ANDROID_SDK_ROOT to ~/Android/Sdk. Both use gtk with Adwaita-dark (gnome-themes-extra). + +Build results (already built, no download needed at switch time): +- .27 -> /nix/store/3g5j39n0kxdfzv7l311vckczmsz2ca17-nixos-system-sam-4screen-desktop-26.11.20260729.0954f7e +- .51 -> /nix/store/hl6mby792nq37d5cm87nk567jck86d9w-nixos-system-sam-thinkpad-26.11.20260926.e158d9e + +Commits pushed: +- .27 branch master -> c2d10d6 "home: put ~/.local/bin on PATH" (f56f053..c2d10d6) +- .51 branch main -> ea2cb86 "home: put ~/.local/bin on PATH" (40ccd7e..ea2cb86) +- paperclip-harness -> 3043e9d (office --help and --host fix) + +Safety note: change is home-manager-only, so only home-manager-sam.service restarts; no nixpkgs/system package movement, network config untouched (unlike a previous incident). Backups left at home/sam/home.nix.bak-path on both .27 and .51. + +`office` binary already copied to .27 and .51 at ~/.local/share/paperclip-harness/bin/office with wrapper ~/.local/bin/office that was unreachable until rebuild. + +Cross-machine usage: `office --project "" --host nixos-desktop` (folder lives on .13) or `office --new "" --host sam-thinkpad`; `--host` copies commands to the named machine so a machine that has never seen the repo still works. + +## Evidence + +- Confidence: 85% +- Reason: Rich sanitized snapshot of a multi-machine config discovery, patch, build, and commit rollout with exact hostnames, paths, store outputs, and commit hashes. + +## Related + +- [[packs/paperclip-harness/00-system/pi-agent/memory-manifest|Memory Manifest]] + +## Related learned memories + +- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]] +- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|NixOS host PATH diffs and prebuilt switch closures]] +- runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|Rebuild .27 and .51 after adding ~/.local/bin to home-manager PATH]] +- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Added ~/.local/bin to PATH on .27/.51 and committed; .13 untouched]] +- session: [[packs/paperclip-harness/80-sessions/nixos-fleet-path-fix-rollout-27-and-51-need-home-rebuild-13-|NixOS fleet PATH fix rollout: .27 and .51 need home rebuild, .13 already correct]] diff --git a/.pi/memory-vault/packs/paperclip-harness/80-sessions/rich-discovery-nix-2026-11-12-05-27-33.md b/.pi/memory-vault/packs/paperclip-harness/80-sessions/rich-discovery-nix-2026-11-12-05-27-33.md new file mode 100644 index 0000000..ce90cea --- /dev/null +++ b/.pi/memory-vault/packs/paperclip-harness/80-sessions/rich-discovery-nix-2026-11-12-05-27-33.md @@ -0,0 +1,132 @@ +--- +type: session +id: session.paperclip-harness.rich-discovery-nix-2026-11-12-05-27-33 +title: Rich discovery: nix 2026-11-12 05:27:33 +status: active +source_of_truth: false +freshness: current +last_reviewed: 2026-11-12 +tags: + - pack/paperclip-harness + - agent-memory/session + - autolearn + - rich-discovery + - repo/nix +--- + +# Rich discovery: nix 2026-11-12 05:27:33 + +Sanitized rich discovery snapshot captured automatically from the completed turn. + + +## Source paths observed + +- `HOME/.local/share/paperclip-harness/bin/office` +- `tmp/office-remote-test/.harness/harness.toml` +- `.local/bin/office` +- `.local/share/paperclip-harness/bin` +- `.local/share/paperclip-harness/bin/office` +- `nix/store/4yxlfj0` +- `v1/chat/completions` +- `HOME/.local/bin` + +## Final answer snapshot + +## Is everything working — measured just now + +| | State | Evidence | +|---|---|---| +| **Pi** | 1.5.0 on all three, same bytes | `.13`, `.27`, `.51` all resolve to `/nix/store/4yxlfj0…-pi-coding-agent-1.5.0` | +| **Paperclip** | running, `2026.916.1` | service active, `GET /api/health` 200, web UI 200 at `http://127.0.0.1:3100/` | +| **Agent runs** | working again | 3 runs failed before the shim, then succeeded (69 s, then 24 s) | +| **omniroute** | working | `/v1/models` returns **486** models, a direct completion returns 200 | +| **headroom** | tool yes, provider no | `/v1/compress` 200 with `messages`; `/v1/chat/completions` returns **502** for both `auto/cheap` and `deepseek/deepseek-v4-flash`; `/stats` shows 153 requests, 2 compressed | +| **TUIOS** | 0.12.1 | both sessions' 8 panes verified by process: each runs `bin/harness -screen …` | + +One thing worth knowing about headroom: my earlier `compress` call returned 400 because I sent `text`; it wants `messages`. Tool path is fine. Provider path is still broken for the same reason as before — its LiteLLM has no entry for the router's model ids. + +## CLI versus web — they are the same thing + +There is no separate web backend. Port 3100 serves the HTML at `/` and the JSON at `/api/...` from one process and one Postgres database. So a cost problem on the web is an **API and aggregation** problem, not a web problem — and you can see the same fault in my terminal panes, which is how I found it. + +Two separate faults, both measured: + +**1. `costCents` is a whole number, so small tasks show zero.** + +``` +AUD-173 costCents 0 inputTokens 13023 outputTokens 2253 + usageJson.costUsd = 0.001934786 → 0.19 cents → displayed as 0 +AUD-163 costCents 3 303609 input tokens → 2.9 cents → displayed as 3 +``` + +Any task that costs less than half a cent reads as free. On the web that looks like "costs aren't working"; the exact figure is in each run's `usageJson`. + +**2. One task's summary is stale and I cannot explain it away.** + +``` +AUD-172 costCents 0 inputTokens 0 outputTokens 0 runCount 4 runtimeMs 81645 + its successful run's usageJson: 34217 input, 7187 output, $0.014134 +``` + +`runtimeMs` proves the summary did aggregate all four runs. But tokens and cost are zero, while the run record holds both. So the per-task summary was computed before that run's usage was written and never recomputed. AUD-173 and AUD-163 summarise correctly, so it is not universal. + +This is a Paperclip-side defect, in the endpoint the web uses. `docs/NEXT.md` records it with these numbers. I am not going to patch Paperclip's internals without you asking me to. + +## `office --help` + +``` +office — open a folder as a project, and say what else can be run + +THE RULE + The folder and the session live on the same machine. … +WHEN IT DOES / WHAT IT WRITES +COMMANDS office, board, board --replace, board --reset, project-reset, + merge, review, triage — one line each +MACHINES nixos-desktop .13 192.168.20.13 + sam-4screen-desktop .27 192.168.20.27 + sam-thinkpad .51 192.168.20.51 +EVERY MACHINE, FROM THE TERMINAL a worked example for .13, .27 and .51 +ON ANOTHER MACHINE where the copy lives +AFTER A REBOOT run board once per project +KEYS INSIDE THE PANES ( ) tab, tab panel, r, R, ? +MORE the four docs to read next +``` + +## While writing it I found `--host` could never have worked + +It ran `office` on the other machine at *this* machine's path, so it needed the repository to exist there at the same location. `.27` and `.51` have no repository at all. It also checked the folder existed **locally** before deciding the folder was remote, so it always answered "no folder at …". + +Both fixed. `--host` now copies `bin/` to `~/.local/share/paperclip-harness/bin/` on the other machine, leaves a short `~/.local/bin/office` there, and the folder is checked on the machine that owns it. Verified against both machines: + +``` +$ office /tmp/office-remote-test --new office-remote-test --host sam-4screen-desktop --dry +on sam-4screen-desktop: exec "$HOME/.local/share/paperclip-harness/bin/office" … +would create a Paperclip project 'office-remote-test' for /tmp/office-remote-test +dry run: nothing was written +switch to it: tuios attach --host sam-4screen-desktop office-remote-test +``` + +`.27` and `.51` now have the commands and a working `--help`. One gap remains: **`~/.local/bin` is not on `PATH` on either machine**, so bare `office` does not resolve there — the full path does: + +```sh +~/.local/share/paperclip-harness/bin/office +``` + +Fixing that is one line in each machine's home-manager config (`home.sessionPath = [ "$HOME/.local/bin" ]`), and one rebuild. Say the word and I will add it to both and hand you the two commands. + +Committed and pushed: `paperclip-harness` → `3043e9d`. + +## Evidence + +- Confidence: 86% +- Reason: Preserve full sanitized discovery detail from a rich investigation turn + +## Related + +- [[packs/paperclip-harness/00-system/pi-agent/memory-manifest|Memory Manifest]] + +## Related learned memories + +- session: [[packs/paperclip-harness/80-sessions/rich-discovery-nix-2026-11-12-05-27-33|Rich discovery: nix 2026-11-12 05:27:33]] +- context: [[packs/paperclip-harness/20-context/nix|nix]] +- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|nix repository structure and configuration patterns]] diff --git a/bin/harness b/bin/harness index f073296..0145dcb 100755 Binary files a/bin/harness and b/bin/harness differ diff --git a/bin/lib/harness.py b/bin/lib/harness.py index c9f7184..4afbc0c 100644 --- a/bin/lib/harness.py +++ b/bin/lib/harness.py @@ -28,9 +28,63 @@ for _p in ( if _p not in os.environ.get("PATH", ""): os.environ["PATH"] = _p + ":" + os.environ.get("PATH", "") -API = os.environ.get("PAPERCLIP_API_URL", "http://127.0.0.1:3100").rstrip("/") -COMPANY = os.environ.get("PAPERCLIP_COMPANY_ID", "d4921013-e5da-4d88-8016-57d5746e2d03") -KEY = os.environ.get("PAPERCLIP_BOARD_KEY", "") + +def _from_secrets(name): + """Read one variable out of the person's own secrets file. + + A terminal started by a desktop session does not inherit systemd's + environment.d, so the key can be missing from the shell on a machine where + the file is right there. The file is the person's; this only reads it. + """ + path = os.path.join(_HOME, ".config", "environment.d") + try: + names = sorted(os.listdir(path)) + except OSError: + return "" + for fn in names: + if not fn.endswith(".conf"): + continue + try: + with open(os.path.join(path, fn)) as fh: + for line in fh: + line = line.strip() + if line.startswith("#") or "=" not in line: + continue + k, _, v = line.partition("=") + if k.strip() == name: + return v.strip().strip('"').strip("'") + except OSError: + continue + return "" + + +def _var(name, default=""): + return os.environ.get(name) or _from_secrets(name) or default + + +def _resolve_api(): + """Where Paperclip is. Its own address if given, else this machine, else .13. + + Paperclip runs on one machine in this house. A session on another machine + has to ask that one, so the fallback is the machine that hosts it rather + than a loopback address that answers nothing there. + """ + told = _var("PAPERCLIP_API_URL") + if told: + return told.rstrip("/") + here = "http://127.0.0.1:3100" + try: + with urllib.request.urlopen(here + "/api/health", timeout=2) as f: + if f.status == 200: + return here + except Exception: + pass + return "http://192.168.20.13:3100" + + +API = _resolve_api() +COMPANY = _var("PAPERCLIP_COMPANY_ID", "d4921013-e5da-4d88-8016-57d5746e2d03") +KEY = _var("PAPERCLIP_BOARD_KEY") BLOCKING_SEVERITIES = {"critical", "high"} diff --git a/bin/office b/bin/office index 9241e3d..148ddf3 100755 --- a/bin/office +++ b/bin/office @@ -74,19 +74,33 @@ def create_project(name, folder, dry): Adopting a folder that Paperclip has never heard of needed two steps before: make the project in the browser, then run this command. One command is enough. """ + # A folder that is a git repository gets a branch per task in its own + # worktree. A folder that is not cannot: asking for a worktree would create + # a project whose every run fails. Those keep the folder in place instead. + is_repo = subprocess.run(["git", "-C", folder, "rev-parse", "--is-inside-work-tree"], + capture_output=True, text=True, timeout=30).stdout.strip() == "true" body = { "name": name, "description": "Adopted from %s by bin/office." % folder, "status": "backlog", - "workspace": {"name": name, "sourceType": "git_repo", "cwd": folder, "isPrimary": True}, - "executionWorkspacePolicy": { + "workspace": { + "name": name, + "sourceType": "git_repo" if is_repo else "local_path", + "cwd": folder, + "isPrimary": True, + }, + } + if is_repo: + body["executionWorkspacePolicy"] = { "enabled": True, "defaultMode": "isolated_workspace", "allowIssueOverride": True, "workspaceStrategy": {"type": "git_worktree", "baseRef": "main", "branchTemplate": "{{issue.identifier}}-{{slug}}"}, - }, - } + } + else: + h.say(" not a git repository: the project keeps the folder in place,") + h.say(" so a task changes these files directly. No branch, no worktree.") if dry: h.say("would create Paperclip project %r for %s" % (name, folder)) return "(dry run)" diff --git a/tui/paperclip/client.go b/tui/paperclip/client.go index db4755e..ecccb0f 100644 --- a/tui/paperclip/client.go +++ b/tui/paperclip/client.go @@ -13,6 +13,7 @@ import ( "net/http" "net/url" "os" + "path/filepath" "strings" "time" ) @@ -29,21 +30,79 @@ type Client struct { // // The token is read from PAPERCLIP_BOARD_KEY (the person's key) and only falls // back to PAPERCLIP_API_KEY for read-only use. The token is never logged. +// fromSecrets reads one variable out of the person's own secrets file, for the +// same reason as the shell fallback: a terminal started by a desktop session +// does not inherit systemd's environment.d. The file is the person's; this +// only reads it, and the value is never logged. +func fromSecrets(name string) string { + home, err := os.UserHomeDir() + if err != nil { + return "" + } + dir := filepath.Join(home, ".config", "environment.d") + entries, err := os.ReadDir(dir) + if err != nil { + return "" + } + for _, e := range entries { + if e.IsDir() || !strings.HasSuffix(e.Name(), ".conf") { + continue + } + data, err := os.ReadFile(filepath.Join(dir, e.Name())) + if err != nil { + continue + } + for _, line := range strings.Split(string(data), "\n") { + line = strings.TrimSpace(line) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + k, v, ok := strings.Cut(line, "=") + if ok && strings.TrimSpace(k) == name { + return strings.Trim(strings.TrimSpace(v), `"'`) + } + } + } + return "" +} + +func varOr(name, fallback string) string { + if v := os.Getenv(name); v != "" { + return v + } + if v := fromSecrets(name); v != "" { + return v + } + return fallback +} + +// reachable says whether something answers at this address right now. +func reachable(url string) bool { + c := &http.Client{Timeout: 2 * time.Second} + resp, err := c.Get(url) + if err != nil { + return false + } + resp.Body.Close() + return resp.StatusCode == 200 +} + func New() (*Client, error) { - base := os.Getenv("PAPERCLIP_API_URL") + base := varOr("PAPERCLIP_API_URL", "") if base == "" { base = "http://127.0.0.1:3100" + if !reachable(base + "/api/health") { + // Paperclip lives on one machine; a session on another has to ask it. + base = "http://192.168.20.13:3100" + } } - company := os.Getenv("PAPERCLIP_COMPANY_ID") - if company == "" { - company = "d4921013-e5da-4d88-8016-57d5746e2d03" - } - token := os.Getenv("PAPERCLIP_BOARD_KEY") + company := varOr("PAPERCLIP_COMPANY_ID", "d4921013-e5da-4d88-8016-57d5746e2d03") + token := varOr("PAPERCLIP_BOARD_KEY", "") if token == "" { - token = os.Getenv("PAPERCLIP_API_KEY") + token = varOr("PAPERCLIP_API_KEY", "") } if token == "" { - return nil, errors.New("no key in the environment: set PAPERCLIP_BOARD_KEY") + return nil, errors.New("no key found: set PAPERCLIP_BOARD_KEY, or put it in ~/.config/environment.d/*.conf") } return &Client{ BaseURL: strings.TrimRight(base, "/"),