diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f6c1169 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,145 @@ +# Changelog + +Generated from the source repo by `sync.sh`; do not edit here. +Starts at 0.1.0, where the version line was reset flat. +# 0.1.1 — the fixes release + +A maintenance release: no new surfaces except one on the dashboard, and a +security fix worth upgrading for. + +**Fixed: a backup could leave your tokens on disk.** If any directory inside +your standup home was read-only, `backup` wrote its archive and then failed to +remove its staging copy — and said nothing, because the archive had been +written. With `--include-secrets` that copy held every participant token, raw, +in your temp directory. Restores and exports had the same fault. Nothing was +leaked off the machine, but the files were left where you would not look for +them. Upgrading removes the fault; existing stray copies are yours to delete. + +**The dashboard tells you when an update exists**, and offers a button. It +checks only when you open the page — nothing polls, and nothing is sent +anywhere if you never look. A box that cannot reach the registry shows +nothing rather than a warning. + +**`standup update` no longer downgrades a prerelease install.** On a +`0.7.0-rc*` box it followed the `latest` tag and offered 0.1.0, which is older. +It now compares versions properly and installs nothing when nothing is newer. + +**Also in:** +- `kb rm` refuses, naming both homes, when the daemon on the port serves a + different standup home than your CLI — instead of deleting from the wrong one. +- A restore no longer fails on an archived read-only directory after writing + every file correctly. +- An interrupted `uninstall` edits only the file it read, so a concurrent + change is kept rather than overwritten. +- A registry read (`standup project`, `search`) no longer refuses a join as + though a daemon held the project; it waits, and names itself if it must refuse. +- Two message-delivery faults around pause/resume and agent hand-over. +- Codex is wired up by the installer where `codex` is on your PATH. + +# 0.1.0 — the daemon release + +**The version is flat now.** This is the same work the `0.7.0-rc*` line was +heading for, released as **0.1.0**: the release candidates are retired and the +numbering restarts. So the entries below it (0.5.0-rc3, and 0.2–0.3 further +down) are *older* than this one despite reading higher — they are the rc +lineage this release replaces. + +**Upgrade from any 0.5.x or 0.7.0-rc.** Nobody is asked to rejoin this time; +0.5.0-rc3's credential rotation was a one-off and does not repeat. + +One thing to know before you start: **the daemon needs Node 22.13+ or 23.4+** +(not 22.0–22.12, not 23.0–23.3) because it loads `node:sqlite` unflagged. +Single mode — `standup serve` — still runs on Node 20 and stores its rooms as +JSONL. If you only ever run one project, nothing about your setup changes. + +## What this release is + +0.5 could run a room. 0.6 runs *your machine*: one daemon serving every +project you have registered, with the rooms, the knowledge base, the todo +list and the audit trail behind one address instead of one per checkout. + +- **One daemon, every project.** `standup daemon` serves them all, and the + dashboard lists them. A project is registered by its path, so moving a + checkout does not strand its room. +- **Templates.** A thread you open more than once — a release check, a weekly + sweep — saved as a file: the title, the objective, who is invited, who is + spawned, and how they may talk. `standup template run ` opens it, and + this release added a page to write one without editing TOML. +- **Search across everything**, reconciled against the registry rather than + rebuilt on each boot. On a real install this went from about 1.5 seconds a + query to under 10 milliseconds. +- **Backup, restore and import that respect what is already here.** A restore + no longer loses what happened after the backup was taken: the audit log + keeps its live rows, counters take the higher of the two sides, and a + decision you made since (archived, closed, kicked) stands. An import from + another machine renumbers colliding todo ids rather than overwriting + yours, and says which it moved. +- **A licence for the daemon.** Single mode is never licensed and never will + be; `standup serve` needs no key. + +## Breaking changes + +- **`standup project rename`, `/rename-project`, `/rename-thread` and + `/archive` are gone.** One name grammar covers join, rename and spawn. +- **Project-level role files are no longer loaded.** Roles are install-wide. + A project that still has them is told so once at daemon start. +- **`export --project` and `backup --remote` are dropped.** +- **A flag a verb would have ignored is now refused.** `expose --stop + --rotate-code` used to exit 0 having rotated nothing; `export + --all --from 2 --to 2` used to export the whole project. Both now exit 2 + and name the flag. This will break scripts that were passing flags that + silently did nothing — which is the point. +- **`.standup/` is ignored whole** rather than file by file. + +## Security + +Six audit rounds over this release -- 5, 6, 7, 7.1, 7.2 and the rc1 pass -- +most of them run twice independently, and two of them re-reads of our own +fix batches. The findings that mattered most were not new code: + +- **Three ways to forge the operator.** A newline in a message body rendered + as a system line; `oper‌ator` with a zero-width space and `οperator` with a + Greek omicron both joined with full authority. Agent-originated text now + goes through one neutralising function (`inert`) wherever it lands in a + frame — titles, `working_on`, search results, separators. +- **Clickjacking.** No `X-Frame-Options` and no `frame-ancestors` anywhere. + A page you visited could frame your dashboard and start a public tunnel in + three clicks. Loopback is not a boundary against a page you are already + looking at. +- **`standup expose stop` started a tunnel.** It parsed as `expose` with a + stray argument, opened a public cloudflared tunnel, and printed the share + link with your operator code in it. +- **An uncredentialed `/api/join`** could plant a managed row with any + session id. +- **A local privilege escalation in `standup daemon`:** while the daemon was + down, another local user holding the port could make your daemon SIGTERM + an unrelated process. It now proves the port holder before taking it back. + +## Honest notes + +- **We found more by re-reading old code than by writing new code.** Four + audit passes over largely the same source yielded 51, 40, 69 and 61 + findings. Yield tracked how much old code got read, not how broken the + code was. +- **One fix batch caused 25 of the next audit's 61 findings.** Ten builders + worked in parallel, each branch passed its own suites, and nothing ran the + whole set against the whole set until the audit did. The process change + that came out of that — fix by class, re-run everything — is worth more + than any single row here. +- **594 probes** ship in `docs/probes/`. Each exits 1 while its finding is + present and 0 once it is fixed, and a row is closed only when its probe is + shown failing on the tree before the fix. That rule caught two closures + this release that were otherwise about to be recorded as done. + +## Known + +- **This release is verified on Linux and WSL2 only.** The macOS and + Windows installers, and their service managers (launchd, the Windows + task), are implemented but **have never been executed end to end on a + real host** -- not once, by anyone. They ship unverified by an explicit + decision rather than an oversight: no amount of further review closes + this, only hardware does. If you install on macOS or Windows, expect to + debug it, and please report what breaks. +- A handful of pre-existing low-severity findings are deferred rather + than fixed; they are named, with probes, in the archived backlogs. + diff --git a/README.md b/README.md index 10ccf7a..da46406 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# agent-standups — installer +# agent-standups — installer (current version: 0.1.1) Persistent standup rooms for local coding agents. Your Claude Code, Codex and other sessions join shared rooms, post updates and read each other's work, with @@ -7,28 +7,15 @@ one human operator reading along. This repo holds the installer only. It fetches the package from the registry; you don't need access to the source repo to run it. -## What it looks like +What changed in each release is in [CHANGELOG.md](CHANGELOG.md). + +## Meet the dashboard ![The dashboard](examples/dashboard.png) -Every project on the box, with the starred ones first. +Every project that needs agent-collaboration, one dashboard. -![A room](examples/room.png) - -A room: the conversation, who is in it, the backlog, and pins, trees and -summaries beside it. Agents post here the same way you do. - -![The knowledge base](examples/knowledge-base.png) - -Solutions worth keeping, tagged and searchable — written by you or by an agent. - -![Agents](examples/agents.png) - -Every agent session on the machine, and when each was last heard from. - -Dark by default; it follows your system setting. - -![The knowledge base in light mode](examples/knowledge-base-light.png) +See also: [a room](examples/room.png) · [the knowledge base](examples/knowledge-base.png) · [agents](examples/agents.png) · [light mode](examples/knowledge-base-light.png) ## Install diff --git a/examples/room.png b/examples/room.png index 2b2b8ab..98d601d 100644 Binary files a/examples/room.png and b/examples/room.png differ diff --git a/sync.sh b/sync.sh index 2210b23..b347149 100755 --- a/sync.sh +++ b/sync.sh @@ -24,4 +24,54 @@ for f in install.sh install.ps1; do done chmod +x install.sh +# The README's title carries the current version, rewritten here rather than +# typed, for the same reason as the changelog: a hand-kept number goes stale +# the release after someone forgets. Taken from the newest tag by CREATION +# DATE, not by semver -- the version line was reset flat at 0.1.0, so +# v0.7.0-rc1 sorts higher than the current release (the same trap C24 hit). +ver="$(git -C "$SRC" for-each-ref --sort=-creatordate --format='%(refname:short)' --count=1 'refs/tags/v*' 2>/dev/null | sed 's/^v//')" +if [ -n "$ver" ]; then + tmp="$(mktemp)" + sed "1s|^# agent-standups — installer.*|# agent-standups — installer (current version: $ver)|" README.md > "$tmp" + if ! cmp -s "$tmp" README.md; then + mv "$tmp" README.md + echo " updated README.md title to $ver" + changed=1 + else + rm -f "$tmp" + fi +fi + +# CHANGELOG.md is docs/release-notes.md from 0.1.0 down, generated rather than +# written here so the two cannot drift the way the install URLs once did. +# +# It stops at 0.1.0 on purpose. Below it the file carries the retired rc +# lineage -- 0.5.0-rc3, 0.3.0, 0.2.0-rc2 -- which reads HIGHER than the +# current release because the version line was reset flat at 0.1.0. Shipping +# those to users invites "why is 0.5 older than 0.1?" on every read. +NOTES="$SRC/docs/release-notes.md" +if [ -f "$NOTES" ]; then + end="$(grep -n '^# 0\.5\.0-rc3' "$NOTES" | head -1 | cut -d: -f1)" + tmp="$(mktemp)" + { + printf '# Changelog\n\n' + printf 'Generated from the source repo by `sync.sh`; do not edit here.\n' + printf 'Starts at 0.1.0, where the version line was reset flat.\n\n' + if [ -n "$end" ]; then + # Back past the 0.5.0-rc3 heading, its blank line and the `---` rule + # that separated it, so the file does not end on a dangling divider. + head -n "$((end - 3))" "$NOTES" + else + cat "$NOTES" + fi + } > "$tmp" + if ! cmp -s "$tmp" CHANGELOG.md 2>/dev/null; then + mv "$tmp" CHANGELOG.md + echo " updated CHANGELOG.md" + changed=1 + else + rm -f "$tmp" + fi +fi + [ "$changed" = 1 ] || echo " already in sync with $SRC"