feat: source Pi skills from agent catalog

This commit is contained in:
Tobias Ostner 2026-07-24 20:30:06 +07:00
parent 087cab63de
commit 7aa6e5b0b2
5 changed files with 267 additions and 14 deletions

View file

@ -0,0 +1,215 @@
# Plan: retire `oibot-agent` from the Tobserver change workflow
## Status
Implementation is in progress.
- The standalone catalog skill is implemented on `agent/tobserver-personal-agit` at commit `ce0f775` and published as [oibot/agent-skills PR #1](https://codeberg.org/oibot/agent-skills/pulls/1).
- AGit creation and a second linear push were verified against the same pull request without a write API token.
- Dotfiles are prepared to pin that catalog revision, load `create-skill` and `tobserver-change` in Pi, and package the bundled helper as `tobserver-agent`; Nix evaluation and the Home Manager build pass.
- The new configuration has not been activated, the Agent Skills PR has not been merged, and no bot credential has been revoked or deleted.
- Cutover is blocked until the user confirms Tobserver `main` protection in Codeberg and resolves or deliberately migrates the active legacy workspace at `/private/var/folders/5p/sj0yx8w92b51dzngrxn9gqz00000gn/T/tobserver-agent.d9wG9L` (`agent/add-bitcoin-pulse-module-wiring-20260723-130500-866d04`).
## Goal
Use the personal Codeberg account `oibot` for Tobserver change branches while preserving a human-only merge boundary:
- `main` accepts changes only through pull requests;
- direct, force, and deletion pushes to `main` are denied, including for `oibot`;
- the agent can create and update pull requests without a write-capable Codeberg API token;
- a human merges through the Codeberg web UI;
- the agent never accesses or deploys to the live Tobserver machine.
The trade-off is accepted: Codeberg will attribute both agent-created pull requests and human merges to `oibot`, so there is no account-level distinction or independently enforceable approval by a second user.
## Repository and distribution decision
The personal-only workflow will be implemented as a self-contained skill in the separate Agent Skills catalog rather than maintained as local skill and script copies in dotfiles.
- The canonical skill directory will be `~/Code/agent-skills/tobserver-change/`.
- The directory will contain `SKILL.md`, the `tobserver-agent` helper under `scripts/`, routing and execution evals, focused helper tests, and every reference or asset required to install, operate, validate, and recover the workflow.
- The helper script will have one canonical source in the skill directory. Dotfiles may package or wrap that bundled script to expose `tobserver-agent` on `PATH`, but must not retain a second editable copy.
- The completed Agent Skills change will be pushed as an `oibot` branch and reviewed through a pull request in `oibot/agent-skills`; do not push directly to protected `main`.
- Dotfiles will add the Agent Skills repository as a pinned, non-flake input and configure Pi to load both `create-skill` and `tobserver-change` from that input.
- The local `config/pi/skills/tobserver-change` copy and the upstream `anthropic-skills`/`skill-creator` input will be removed after Pi successfully loads the catalog's `tobserver-change` and `create-skill` skills.
- `flake.lock` will pin the reviewed Agent Skills revision so a future catalog push cannot silently change Pi's active instructions or helper.
- Existing local helper and skill files remain available during migration and are removed only after the catalog-backed installation passes local and end-to-end validation.
The intended order is: build and evaluate the standalone catalog skill, publish and review it, pin it in dotfiles, verify Pi and the packaged helper, complete the personal-only AGit cutover, and only then revoke or remove `oibot-agent` credentials.
## Security invariants
1. Protect `main` before changing credentials or helper behavior.
2. Do not place `oibot` on a direct-push or branch-protection bypass list.
3. Do not expose an `oibot` API token capable of merging pull requests or changing repository settings to the helper or agent.
4. Keep the existing workspace, branch-prefix, validation, secret, no-force-push, no-merge, and no-deployment checks.
5. Keep the current bot credentials available for rollback until the personal-only workflow completes an end-to-end test.
6. Do not delete keys, tokens, open pull requests, or active workspaces automatically.
## Preconditions and decisions
- [x] Confirm that independent Codeberg attribution and approval are no longer requirements.
- [x] Confirm that human merging will happen only in the Codeberg web UI.
- [x] Inspect existing open `oibot-agent` pull requests; the public API returned none.
- [x] Check `tobserver-agent status` and preserve the active legacy workspace recorded above.
- [x] Establish an isolated Agent Skills branch and preserve existing dotfiles commits and owner work.
## Phase 1: enforce the server-side merge boundary
In `oibot/Tobserver` repository settings, configure an exact protection rule for `main`:
- [ ] deny direct pushes for everyone, including repository owners;
- [ ] deny force pushes;
- [ ] deny branch deletion;
- [ ] require changes to arrive through pull requests;
- [ ] retain applicable status-check and conversation-resolution requirements;
- [ ] ensure there is no `oibot` push or bypass exception.
Verify the saved rule in the Codeberg UI. Do not probe Tobserver by attempting a push that could succeed if the rule is wrong. If a push-level test is desired, reproduce the rule in a disposable repository first.
**Gate:** do not continue until the UI confirms that `oibot` cannot directly update `main`.
## Phase 2: prove a tokenless pull-request path
Use Forgejo AGit over SSH so pull requests can be created without an API write token:
```sh
git push origin HEAD:refs/for/main/<topic> \
-o title="<title>" \
-o description="<description>"
```
Before refactoring the helper:
- [ ] confirm `oibot` authenticates through `git@codeberg.org` with the personal SSH key;
- [ ] confirm the public Codeberg API exposes the pull-request metadata needed by `revise` without authentication;
- [ ] determine the exact AGit representation of the topic, head ref, and pull-request URL;
- [ ] determine a robust multiline Markdown description encoding supported by Codeberg;
- [ ] create a clearly labeled AGit test pull request from an empty commit so it has no tree changes;
- [ ] push a second linear empty commit with the same topic and verify that it updates the same pull request without force-pushing;
- [ ] close the test pull request manually in Codeberg.
Do not merge the test pull request.
**Gate:** if AGit creation, URL discovery, or linear revision cannot be made reliable without a write-capable personal API token, stop and reconsider the account-removal design.
## Phase 3: refactor `tobserver-agent`
Target file: `~/Code/agent-skills/tobserver-change/scripts/tobserver-agent`
### Identity and credentials
- [ ] change the push URL from `git@codeberg-tobserver-agent:oibot/Tobserver.git` to the normal personal Codeberg SSH endpoint;
- [ ] retain an informational agent-specific commit author name;
- [ ] replace the bot noreply email with an agreed `oibot`-compatible address;
- [ ] remove bot Keychain account/service constants;
- [ ] remove write-token loading and temporary authorization-header handling;
- [ ] use unauthenticated HTTPS only for public read operations.
### Pull-request creation
- [ ] replace API `POST /pulls` creation with one AGit push;
- [ ] generate a unique, shell-safe AGit topic tied to the existing `agent/*` branch/workspace identity;
- [ ] pass the validated title and Markdown body without unsafe quoting, truncation, or accidental shell interpretation;
- [ ] capture and validate the returned pull-request URL under `https://codeberg.org/oibot/Tobserver/pulls/`;
- [ ] preserve the workspace when AGit submission or URL discovery fails.
### Pull-request revision
- [ ] replace the `oibot-agent` author assertion with checks appropriate to the new model: canonical repository, open state, `main` base, expected AGit topic/head, and helper-compatible history;
- [ ] fetch revision metadata without a write-capable token;
- [ ] push additional commits using the same AGit topic;
- [ ] preserve normal fast-forward/linear updates and continue to forbid force-push;
- [ ] verify that updating a pull request cannot create a duplicate PR silently.
### Existing safety checks
Retain and regression-test:
- [ ] helper-created temporary workspaces only;
- [ ] exact canonical fetch and push endpoints;
- [ ] `agent/*` local branch naming;
- [ ] refusal to work on `main`;
- [ ] no SOPS payload changes or plaintext secret access;
- [ ] Nix formatting, flake evaluation, derivation evaluation, Git whitespace, and Gitleaks checks;
- [ ] no merge, deployment, live-server access, force-push, remote branch deletion, or automatic failed-workspace cleanup.
## Phase 4: update the Tobserver skill and evaluations
Target files:
- `~/Code/agent-skills/tobserver-change/SKILL.md`
- `~/Code/agent-skills/tobserver-change/evals/evals.json`
Changes:
- [ ] remove the dedicated-bot compatibility requirement;
- [ ] replace the bot SSH alias with the normal Codeberg SSH endpoint;
- [ ] describe protected `main` plus human web merge as the approval boundary;
- [ ] state that the helper must not use a write-capable Codeberg API token;
- [ ] replace bot-owned PR revision checks with canonical AGit topic/head checks;
- [ ] retain the `agent/*`, no-force-push, no-merge, no-deploy, and recoverability constraints;
- [ ] update eval expectations that currently require `oibot-agent` ownership;
- [ ] add or revise an eval covering rejection of direct `main` pushes and API-based merge attempts.
## Phase 5: validate locally
- [x] run `bash -n tobserver-change/scripts/tobserver-agent` from the Agent Skills repository;
- [x] run focused helper tests for branch/topic restrictions, AGit metadata, push-option encoding, URL validation, and revision matching;
- [ ] test failure paths with mocked Git/HTTP commands so failed submission preserves the workspace;
- [ ] run the repository's Nix checks without deploying or building a Tobserver system closure;
- [x] inspect the Agent Skills diff for credentials, token remnants, unrelated edits, and stale bot assumptions;
- [ ] rerun the `tobserver-change` routing and representative workflow evaluations.
## Phase 6: end-to-end cutover test
With bot credentials still intact for rollback:
1. [ ] create a fresh helper workspace;
2. [ ] make an empty or harmless non-system test commit;
3. [ ] run all helper checks;
4. [ ] submit through AGit as `oibot`;
5. [ ] verify that one PR targets `main` and no direct `main` update occurred;
6. [ ] revise the same PR with one additional linear commit;
7. [ ] verify that no duplicate PR or force-push occurred;
8. [ ] close the test PR manually without merging;
9. [ ] verify failed and interrupted workspaces remain resumable.
**Gate:** retire the bot only after the complete create-and-revise workflow passes.
## Phase 7: retire bot credentials
Perform these as explicit human-approved cleanup steps:
- [ ] remove `codeberg-tobserver-agent` from `~/.ssh/config`;
- [ ] unload `~/.ssh/oibot-agent` from `ssh-agent`;
- [ ] revoke the bot API token in Codeberg;
- [ ] remove the bot token from macOS Keychain;
- [ ] remove the bot SSH key from Codeberg;
- [ ] remove `oibot-agent` repository access;
- [ ] archive or delete the bot account only after confirming it owns no required issues, PRs, repositories, or audit artifacts;
- [ ] delete local bot key files only with separate explicit confirmation and after any desired backup.
Do not rewrite historical commits or pull requests merely to remove the bot name.
## Rollback
Until Phase 7:
1. restore the previous helper push URL and bot token path;
2. restore the skill's bot-identity requirements;
3. use the preserved bot SSH alias and token;
4. resume preserved helper workspaces rather than recreating changes;
5. keep `main` protection in place throughout rollback.
After bot credentials are revoked, rollback requires issuing a new constrained credential or restoring the bot account deliberately; do not weaken `main` protection as a shortcut.
## Completion criteria
- `oibot` cannot directly, forcibly, or destructively update `main`.
- The helper creates and linearly updates a single PR through SSH/AGit.
- The helper and agent have no write-capable Codeberg API token.
- Only a human web action merges a PR.
- All existing local checks and secret boundaries still pass.
- No live Tobserver access or deployment occurred.
- Bot credentials and access are removed only after successful cutover and explicit approval.

23
flake.lock generated
View file

@ -1,19 +1,20 @@
{
"nodes": {
"anthropic-skills": {
"agent-skills": {
"flake": false,
"locked": {
"lastModified": 1784256457,
"narHash": "sha256-QZ+zJkyLd/42rxgtJEZSUOz9R75Tse6UXW7G0nOkFS8=",
"owner": "anthropics",
"repo": "skills",
"rev": "fa0fa64bdc967915dc8399e803be67759e1e62b8",
"type": "github"
"lastModified": 1784899453,
"narHash": "sha256-/tMA9LQkVClOOXREW4oeXhq9PzyEUK9RIo3GrfLl2SI=",
"ref": "agent/tobserver-personal-agit",
"rev": "ce0f775ccb125ea34b604e282496da18cea7c061",
"revCount": 3,
"type": "git",
"url": "https://codeberg.org/oibot/agent-skills.git"
},
"original": {
"owner": "anthropics",
"repo": "skills",
"type": "github"
"ref": "agent/tobserver-personal-agit",
"type": "git",
"url": "https://codeberg.org/oibot/agent-skills.git"
}
},
"bun2nix": {
@ -309,7 +310,7 @@
},
"root": {
"inputs": {
"anthropic-skills": "anthropic-skills",
"agent-skills": "agent-skills",
"darwin": "darwin",
"emacsConfig": "emacsConfig",
"hermes-agent": "hermes-agent",

View file

@ -24,8 +24,8 @@
hermes-agent.url = "github:NousResearch/hermes-agent";
anthropic-skills = {
url = "github:anthropics/skills";
agent-skills = {
url = "git+https://codeberg.org/oibot/agent-skills.git?ref=agent/tobserver-personal-agit";
flake = false;
};

View file

@ -2,6 +2,9 @@
let
piFirecrawl = pkgs.callPackage ../pkgs/pi-firecrawl.nix { };
tobserverAgent = pkgs.callPackage ../pkgs/tobserver-agent.nix {
script = inputs.agent-skills + "/tobserver-change/scripts/tobserver-agent";
};
in
{
home.file.".clojure/deps.edn".source = ../config/clojure/deps.edn;
@ -59,6 +62,7 @@ in
sops
tree
typescript
tobserverAgent
wget
];
@ -102,7 +106,8 @@ in
pi.coding-agent = {
enable = true;
skills = [
(inputs.anthropic-skills + "/skills/skill-creator")
(inputs.agent-skills + "/create-skill")
(inputs.agent-skills + "/tobserver-change")
];
environment = {
FIRECRAWL_API_KEY.file = config.sops.secrets.firecrawl_api_key.path;

32
pkgs/tobserver-agent.nix Normal file
View file

@ -0,0 +1,32 @@
{ script
, coreutils
, curl
, findutils
, git
, gitleaks
, gnused
, jq
, nix
, nixpkgs-fmt
, openssh
, writeShellApplication
}:
writeShellApplication {
name = "tobserver-agent";
runtimeInputs = [
coreutils
curl
findutils
git
gitleaks
gnused
jq
nix
nixpkgs-fmt
openssh
];
text = builtins.readFile script;
}