510 Commits
Author SHA1 Message Date
Gabriel Brown 8316c8a1bf Drop the rule that left starting dev servers to the user 2026-11-05 16:51:31 -05:00
gibandClaude Opus 5.5 86ea1a3374 Wait up to 6 hours for Nextcloud's borg lock before deferring the weekly media backup
AIO's borg now runs daily from 07:30 UTC for about 2 hours, so the Saturday
05:00 fire can find the lock held and skip the whole week, which trips Uptime
Kuma's 8-day push window. Poll the lock every 5 minutes for up to 6 hours and
defer only if it is still held after that.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-05 15:31:24 -05:00
Gabriel Brown c6d299b675 Seat the ticket review council on Claude Opus 5.5 and Codex GPT-6.1 Sol
MiMo leaves the council. Codex is now installed and signed in through T3,
so the second model family comes from GPT-6.1 Sol instead.
2026-11-05 15:30:54 -05:00
Gabriel Brown 6c8db4e845 Fix: Install the whole Codex package so codex-code-mode-host sits beside bin/codex
Codex 0.165.0 ships as a package tree and runs bin/codex-code-mode-host,
codex-path/ and codex-resources/ relative to itself. The installer verified
the full archive but kept only bin/codex, so code mode failed. Keep the whole
verified tree, link ~/.local/bin/codex to its bin/codex, and replace the
receipted flat install earlier runs left behind once the new tree verifies.
2026-11-05 15:14:47 -05:00
Gabriel Brown 86f6e391b7 Run the ticket skills through T3 Code, with a review council before pre-mr-review
Agents now start pre-mr-review on the work account themselves, through a Klaude
delegate, and continue from its verdict. A council of models on my own accounts
replaces the single reviewer before it, and adding a seat is one table row.

Proof comes from T3's browser, with mp4 recordings, and capture-proof stays as
the fallback. Ticket pages carry their own data, so index.html renders alone in
T3's file viewer, and worktree threads share the main checkout's docs.

A PreToolUse hook sends subagents in T3 threads to delegate_task. Blocker, proof,
review and MR summary rules come from ideas in t3dotgg/tstack.
2026-11-04 18:04:18 -05:00
gib 968e0a1045 VPS: add the WristCode gateway stack 2026-11-01 13:05:24 -05:00
Gabriel Brown d5ef335d61 Link the agent half of user/ on the Mac mini
Manifest lines can now name an OS. The SSH config, pwswitch and espanso are
Linux only, so link-user on the Mac links CLAUDE.md, rules, skills and the
opencode commands and nothing else. link-mcp no longer aborts under the Mac's
bash 3.2 when a server has no token.
2026-11-01 12:41:58 -05:00
Gabriel Brown 27e28b0dd1 Show mocks in T3 Code threads, and drop the t3 MCP servers that T3 already provides 2026-10-30 14:58:46 -04:00
Gabriel Brown 4e39be860c Make ticket pages open in T3 Code's file viewer, and show plans, mocks and progress inline in the thread 2026-10-30 14:58:46 -04:00
Gabriel Brown e21a4c24f0 Offer T3 Code as the preferred agent, opening a thread in the Panama project, and install it with the desktop 2026-10-30 14:58:46 -04:00
Gabriel Brown 5af5d7a96b Pin Terra on a server that already has it, before its package update 2026-10-30 14:19:14 -04:00
Gabriel Brown c6fb2fa767 Pin Terra through repos.override.d so terra-release updates stop undoing it 2026-10-30 14:04:31 -04:00
Gabriel Brown 042885f113 Keep the home server's root backup scripts with the rest of its tooling 2026-10-30 13:59:29 -04:00
Gabriel Brown 7cc98cd318 Make pwswitch off cover sudo -v, so Panama's installer runs without a password 2026-10-30 13:57:39 -04:00
Gabriel Brown cb089b39ea Sync both server snapshots with what the live machines run, and keep their backup tooling 2026-10-30 13:44:29 -04:00
Gabriel Brown 95fa480469 Let a server service carry helper scripts and timers, starting with UseSend's cert refresh 2026-10-30 13:44:29 -04:00
Gabriel Brown a987cbab66 Generate opencode's config from a tracked template, with matching commands, agents and pwswitch 2026-10-30 13:44:29 -04:00
Gabriel Brown 68c7d6a3ea Put OpenCode 2's bin on PATH in the tracked shell config 2026-10-29 17:48:00 -04:00
Gabriel Brown a787480688 Register each machine's T3 Code environment as an MCP server 2026-10-29 16:57:39 -04:00
Gabriel Brown 11c3ede473 Keep the T3 Code SnapShots shortcut its setup wrote into hyprland.lua 2026-10-29 16:49:29 -04:00
Gabriel Brown 44d32d78ae Pin Codex 0.165.0 and read its archive layout from a reviewed listing file 2026-10-29 15:29:44 -04:00
Gabriel Brown 0add5af1b9 Load the whole config again under Hyprland 0.57
Hyprland 0.57 refuses a bind on a bare modifier and an empty input:scroll_method. The switcher's Super-release commit now binds Super_L and Super_R, which also brings back every bind below it in keybinds.lua. An empty scroll method is left unwritten, so libinput still picks per device.
2026-10-29 13:56:53 -04:00
Gabriel Brown 48e5ea9a39 Let agents invoke the ticket skill, like its create-ticket and review-ticket siblings 2026-10-22 18:24:30 -04:00
Gabriel Brown ffad7e56ea Give the Mac mini a zsh config with the Panama prompt and SSH tmux 2026-10-12 08:06:20 -04:00
Gabriel Brown 34bc1663ed Give opencode the same agent instructions, rules and MCP servers as Claude Code
opencode already reads skills from ~/.claude/skills and ~/.agents/skills, so
those needed nothing. The rest did.

The manifest now links agents/AGENTS.md to ~/.config/opencode/AGENTS.md as
well, beside the Claude Code and Codex names.

opencode keeps its MCP servers in its own JSON config, with no file to point
a symlink at, so link-mcp now writes them there too. It builds one entry per
row of user/agents/mcp/servers with the same tokens it gives Claude Code,
and adds ~/.claude/rules/*.md under instructions, since opencode does not
read that directory by itself. Only those two keys change. The file is
written private and swapped in whole because it now holds tokens, and a
config with JSONC comments is left alone with a warning rather than
rewritten without them.
2026-10-07 01:06:44 -04:00
Gabriel Brown f4fca1797f Skip locally built images by their build: stanza instead of a list of names
update-containers kept LOCAL_BUILD, a hand-written list of services whose
images are built on the machine, so it would not try to pull them. The
compose files already say this. A service with a build: stanza names a
local image, and there is nothing in a registry to pull.

images_for now drops the image of any compose service that has a build:
stanza. Those services resolve no images, and the run skips them without a
log line. The list, and the app names it carried, are gone. On the VPS,
--list resolves the same images as before for every other service.
2026-10-06 10:52:47 -04:00
Gabriel Brown 913a57f8a6 Snapshot both servers: the VPS catalog moves to server/vps, the home server joins as server/home
The catalog in server/containers had drifted from the VPS. Eleven compose
files and twelve units no longer matched what runs, and two running
services, Gbrown_Agent and BillTracker, were missing. server/vps is now a
copy of the live files, and panama-server reads from it.

server/home holds the home server's services in the same shape. They still
run under Docker, so the podman units there are drafts for a later move, and
nothing installs from that tree. PostHog, Sentry and Plausible are checkouts
of upstream repositories. Only their local files are kept, next to an
UPSTREAM file naming the repository and the exact commit.

Every secret in both trees is a ${VAR} reference named in .env.example, or
CHANGE_ME in files that are not compose files. The live servers still hold
some of those values inline, so a live file can differ from its copy on
those lines alone. Existing .env.example files keep their comments, with new
keys appended at the end.

The ignore rules and the secrets contract now cover both trees, plus the
volume/ and volumes/ directories the home server uses.
2026-10-06 10:52:47 -04:00
gibandClaude Opus 5 a664cf68f5 Do not prefer a dialog on a machine with nowhere to draw it
config/bash/shell exported SSH_ASKPASS=ksshaskpass with
SSH_ASKPASS_REQUIRE=prefer on every machine. `prefer` means ssh and git reach
for that dialog BEFORE the terminal prompt, so on a headless server the
prompt that would have worked was skipped in favour of a KDE window that
cannot open:

  error: /usr/bin/ksshaskpass died of signal 6
  fatal: could not read Username for 'https://git.gbrown.org'

A push from a server failed to ASK for a username rather than failing to
authenticate. The shell environment is the one half of Panama a server does
install, so nothing downstream was going to catch this.

The export is now conditional on a session existing to draw in, and on the
binary being there to run. The headless branch also CLEARS both variables
rather than merely declining to set them: an askpass inherited from a parent
that had a display, or from a login predating this guard, is exactly as
unusable and a guard that only withholds leaves it in place. A fresh login
shell on the repaired server still reported prefer until it cleared them.

A desktop is unchanged; a server, a serial console and a plain SSH session
fall back to the terminal prompt, which works.

tests/setup/headless-askpass-contract pins every branch, the inherited case
included, and fails against the previous code and against a guard that only
withholds.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-10-01 17:50:14 -04:00
gibandClaude Opus 5 a493fa5584 Open what is shut, and never widen what is already open
setup-server queried --query-port, saw "not open", and blanket-opened 80,
443 and 81. On a machine reaching those ports through rich rules -- one CDN's
address sets, say -- neither appears in --list-ports, so the step read a
deliberate, narrow grant as "closed" and replaced it with a public one. It
did this during `panama update`, which asks nothing and runs unattended, so
there was no prompt and no diff for anyone to read. A production server lost
its firewall posture that way, port 81 and the proxy's admin portal included.

Each port is now checked for existing coverage first -- an open port, an
enabled service, or an accepting rich rule, ranges included -- and left alone
when it has some. The bias stays where it was: only a rule that positively
reads as a grant counts, and anything unrecognised still falls through to
opening the port, because a fresh server that is silently unreachable is the
worse failure. A rule that DROPS a port is not coverage either; reading a
block as a grant would leave the proxy unreachable on a machine nobody
thought to re-check.

A machine that manages its own firewall opts out once, with
`echo manual > ~/.local/state/panama/firewall`, and Panama never touches
firewalld on it again. A file rather than an environment variable, because
the run that needs to respect it is the unattended one.

tests/setup/server-firewall-contract pins all of it, and fails against the
previous code on exactly the cases that cost the posture.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-10-01 17:50:14 -04:00
Gabriel Brown eb0056ac94 Register the self-hosted Sentry MCP server and keep OAuth sign-ins across updates
link-mcp gains a stdio transport: the servers row names a launcher under user/agents/mcp/, run from the checkout so every machine runs the same script, and the token reaches it as MCP_TOKEN. sentry-gbrown is the first, pointing Sentry's MCP server at sentry.gbrown.org with a read-only token from the ignored env file.

A server already registered exactly as its row would register it is now left alone. Re-adding it every run threw away the sign-in of the OAuth servers, bill-tracker and atlassian, on every update.
2026-10-01 17:33:12 -04:00
Gabriel Brown 58a64d6cee Register Penpot's MCP server, whose token rides in the URL
Penpot's MCP server reads the user key only from the userToken query
parameter, never a header. link-mcp now substitutes a {TOKEN} placeholder
in a server's URL instead of sending the Authorization header, and the
servers list gains a penpot row that uses it.
2026-09-23 08:48:12 -04:00
Gabriel Brown 933c197060 Capture ticket proof with Playwright instead of the browser extension 2026-09-22 15:18:38 -04:00
Gabriel Brown de3591a25b Add capture-proof for sharp Playwright proof screenshots 2026-09-22 15:18:38 -04:00
Gabriel Brown 88032490c0 Drive the ticket skill through the live ticket page 2026-09-22 14:54:05 -04:00
Gabriel Brown fd24b69039 Add the live ticket page and epic index renderer 2026-09-22 14:54:05 -04:00
Gabriel Brown dfc009e422 Add the ticket page data model and its ticket-page CLI 2026-09-22 14:54:05 -04:00
Gabriel Brown 1843c0495a Fix: Teach the update contract about the link-mcp stage 2026-09-22 14:04:32 -04:00
Gabriel Brown 43ae5bb53b Test: Fail the provenance contract when a case writes into the real nvm tree 2026-09-22 14:04:32 -04:00
Gabriel Brown 05054afdc9 Fix: Leave logind alone while a desktop session is running 2026-09-22 14:04:32 -04:00
Gabriel Brown 9022847971 Add a plan.html review page to the ticket skill 2026-09-22 12:54:32 -04:00
Gabriel Brown 9b06e9e24b Docs: Add a spelling rule to unslop
American spelling is the default and British forms turn up in generated
prose. Catch the common endings and the irregulars, with two exceptions:
project vocabulary that the client's documents, ticket titles and codebase
all spell the British way, such as "enrolment" on APSCA, and existing code
identifiers, which are never renamed to fix spelling.

The rules after it shift by one.
2026-09-18 17:28:56 -04:00
Gabriel Brown 59da2bb2fc Docs: Scope the Ksense sections to the Ksense folder
The merge request conventions and the coworker list only apply to Ksense
work, but they load in every project including personal ones. Name the
folder that work lives in so an agent reads those sections as conditional
rather than as always in force.
2026-09-18 17:28:56 -04:00
Gabriel Brown 5ebecd59bb Register Atlassian's MCP server
Jira and Confluence reached Claude Code through two claude.ai connectors,
which are configured per account rather than per machine and had been added
twice. Atlassian hosts its own MCP server, so it belongs in this list with
the others and a new machine picks it up from the installer.

It carries no token. The server runs its own OAuth and Claude Code signs in
through /mcp on first use.
2026-09-18 17:28:56 -04:00
Gabriel Brown 501f7306ed Point every ssh host at the desktop keyring agent
Claude Code and Codex start their shells without SSH_AUTH_SOCK. id_ed25519
has a passphrase, so batch ssh from a harness had no way to unlock it and
failed with "Permission denied (publickey)" against server.gib, junior.gib
and git.gbrown.org. The key and the per-host blocks were right the whole
time, which made it read as a key problem and cost several rounds of
checking keys that were never wrong.

The desktop keyring agent at $XDG_RUNTIME_DIR/keyring/ssh already holds the
key unlocked for the interactive session. A Host * block with IdentityAgent
points every host at that socket, so a shell that inherits no agent gets the
same one the terminal uses.
2026-09-18 17:28:56 -04:00
gibandClaude Opus 5 130137485e Guard the mesa driver swap on its end state
mesa-va-drivers-freeworld Provides mesa-va-drivers, so once the swap has run
once it becomes the only thing providing the name we ask dnf to remove. The
command then resolves to removing and installing the same package in a single
transaction and exits with "conflicting requests", which reported
install-packages as a failed stage on every run after the first even though
the machine was already in the desired state.

The failure also produced misleading output, because dnf explored removing
mesa-dri-drivers and surfaced unrelated complaints from steam, lutris and
tigervnc-server-common.

Check for the freeworld package with rpm -q and skip the swap when it is
already installed.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01TFG55mJBiH2tV38tXcNzJ9
2026-09-18 11:02:35 -04:00
Gabriel Brown 5ebebc8aa2 Docs: Treat the pre-MR audit verdict as the gate and converge reruns with context 2026-09-17 11:43:07 -04:00
Gabriel Brown 85160ffef2 Stop writing text into the Working Feature Proof field
Dragging attachments into customfield_10142 replaces whatever text is
sitting there, so the list the skill wrote was destroyed by the upload it
was describing. Naming each artifact is already the job of the Test Cases
proof cells, which reference it by filename, and step 10 tells me which
files to upload.

Only the Story rule changes. On a Bug that field carries the test-case
table rather than prose.
2026-09-14 14:36:54 -04:00
Gabriel Brown 59192143c5 Docs: Add the prose bar and record Josh as the APSCA lead
Anything a person other than me reads now gets an unslop pattern pass
before it ships: MR descriptions, ticket fields, deliverables. The rules
that matter most are the ones a generic pass misses. Write as me in first
person, never name me or use a stand-in, and never flag one of my own
decisions as a deviation, because that reads as my own AI tattling on me
and costs the PM time investigating a non-issue.

Punctuation is stricter than unslop's own pattern: no em dashes, en
dashes, semicolons or arrow glyphs. Generated output is never rewritten,
only regenerated.

Also records Josh as the tech lead on apsca_next, whose dev reviews carry
draft code and schema and are a starting point rather than a finished spec.
2026-09-14 14:36:52 -04:00
Gabriel Brown 2f2e3b88e8 Register MCP servers through the installer
An MCP server is a URL plus a bearer token, and the token is why this is a
stage rather than a manifest line. Panama is public, so the tokens cannot
live in it, and neither runtime keeps its server list in a file worth
symlinking: Codex writes them into config.toml beside dozens of unrelated
settings, and Claude Code into ~/.claude.json. link-mcp registers them
through the runtime's own CLI instead.

user/agents/mcp/servers is tracked and names which variable carries each
token. user/agents/mcp/env holds the tokens and is ignored. A new machine
gets the servers by dropping its own env file beside the tracked one.

Only Claude Code is handled, and only where the interview enabled personal
content. Rewriting a section of somebody's live Codex TOML is a worse
failure mode than leaving two lines to paste once.
2026-09-14 14:36:44 -04:00
Gabriel Brown 9156a7e05c Fix: Scan for DDC displays when quick settings opens
The scan was keyed off this item becoming visible, but on a machine with no
backlight `visible` stays false until a scan has already found a DDC
display, so it never scanned at all. It now keys off the quick settings
panel opening, which still keeps the second-long I2C probe off shell
startup.
2026-09-14 14:36:39 -04:00