Merge pull request 'chore: gardener housekeeping' (#781) from chore/gardener-20260415-1007 into main
This commit is contained in:
commit
91f36b2692
10 changed files with 56 additions and 30 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
<!-- last-reviewed: 10c7a88416b14e849f80ad3fe7ea8e51d26177e8 -->
|
<!-- last-reviewed: be463c5b439aec1ef0d4acfafc47e94896f5dc57 -->
|
||||||
# Disinto — Agent Instructions
|
# Disinto — Agent Instructions
|
||||||
|
|
||||||
## What this repo is
|
## What this repo is
|
||||||
|
|
@ -198,5 +198,4 @@ at each phase boundary by writing to a phase file (e.g.
|
||||||
|
|
||||||
Key phases: `PHASE:awaiting_ci` → `PHASE:awaiting_review` → `PHASE:done`.
|
Key phases: `PHASE:awaiting_ci` → `PHASE:awaiting_review` → `PHASE:done`.
|
||||||
Also: `PHASE:escalate` (needs human input), `PHASE:failed`.
|
Also: `PHASE:escalate` (needs human input), `PHASE:failed`.
|
||||||
|
|
||||||
See [docs/PHASE-PROTOCOL.md](docs/PHASE-PROTOCOL.md) for the complete spec, orchestrator reaction matrix, sequence diagram, and crash recovery.
|
See [docs/PHASE-PROTOCOL.md](docs/PHASE-PROTOCOL.md) for the complete spec, orchestrator reaction matrix, sequence diagram, and crash recovery.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<!-- last-reviewed: 10c7a88416b14e849f80ad3fe7ea8e51d26177e8 -->
|
<!-- last-reviewed: be463c5b439aec1ef0d4acfafc47e94896f5dc57 -->
|
||||||
# Architect — Agent Instructions
|
# Architect — Agent Instructions
|
||||||
|
|
||||||
## What this agent is
|
## What this agent is
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<!-- last-reviewed: 10c7a88416b14e849f80ad3fe7ea8e51d26177e8 -->
|
<!-- last-reviewed: be463c5b439aec1ef0d4acfafc47e94896f5dc57 -->
|
||||||
# Dev Agent
|
# Dev Agent
|
||||||
|
|
||||||
**Role**: Implement issues autonomously — write code, push branches, address
|
**Role**: Implement issues autonomously — write code, push branches, address
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<!-- last-reviewed: 10c7a88416b14e849f80ad3fe7ea8e51d26177e8 -->
|
<!-- last-reviewed: be463c5b439aec1ef0d4acfafc47e94896f5dc57 -->
|
||||||
# Gardener Agent
|
# Gardener Agent
|
||||||
|
|
||||||
**Role**: Backlog grooming — detect duplicate issues, missing acceptance
|
**Role**: Backlog grooming — detect duplicate issues, missing acceptance
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,52 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"action": "edit_body",
|
"action": "remove_label",
|
||||||
"issue": 765,
|
"issue": 771,
|
||||||
"body": "## Problem\nPlanner phase 5 pushes ops repo changes directly to `main` (`planner/AGENTS.md:37`, `planner/planner-run.sh`). Branch protection blocks this — see #758 for the symptom (PR #30 stuck, ops `main` frozen at v0.2.0 since 2026-04-08).\n\n## Why a new issue\n#758 is assigned to dev-qwen and labelled blocked; this reframes the fix rather than the symptom.\n\n## Proposal\nFold planner into the same flow architect already uses for ops PRs (`pr_create` → `pr_walk_to_merge` against `FORGE_OPS_REPO`). Architect proves merge perms work; review-bot already gates ops PRs and decides auto-approve vs request-changes. No new reviewer, no permission changes.\n\n## Changes\n- `planner/planner-run.sh` phase 5: stop direct push; create branch `planner/run-YYYY-MM-DD`, call `pr_create` then `pr_walk_to_merge`.\n- Planner formula prompt: replace \"push directly\" instructions with phase-protocol terminology used by architect.\n- `planner/AGENTS.md`: update phase 5 description.\n\n## Acceptance\n- Planner run produces a PR on ops repo, walks to merged via review-bot.\n- PR #30 closed (superseded) once new flow lands.\n- ops `main` advances past v0.2.0.\n\n## Acceptance criteria\n- [ ] Planner run produces a PR on ops repo, walks to merged via review-bot\n- [ ] PR #30 closed (superseded) once new flow lands\n- [ ] ops `main` advances past v0.2.0\n- [ ] CI green on the planner changes\n\n## Affected files\n- `planner/planner-run.sh` — replace direct push with `pr_create` + `pr_walk_to_merge`\n- `planner/AGENTS.md` — update phase 5 description"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"action": "edit_body",
|
|
||||||
"issue": 429,
|
|
||||||
"body": "## Problem\n\nThe generated docker-compose.yml uses `build: context: . dockerfile: docker/agents/Dockerfile` which bakes the disinto code into the image via `COPY . /home/agent/disinto`. This causes:\n\n1. **Read-only code** — runtime state (`state/`), config (`projects/*.toml`), and `.env` are not in the image, but the baked-in directory is read-only. Manual volume mount workarounds break on every compose regeneration.\n2. **No versioning** — every `docker compose build` creates a new image from whatever code is on disk. No way to pin a known-good version or roll back.\n3. **No distribution** — new factory instances must clone the disinto repo and build locally. Cannot just `docker pull` and run.\n4. **Fragile rebuilds** — `docker system prune` removes the locally-built image, requiring a full rebuild that may fail (wrong Dockerfile, missing deps, stale cache).\n\n## Proposed solution: publish versioned images\n\nPublish container images to a registry (e.g. `ghcr.io/disinto/agents:v0.1.0`) on each release. The generated compose uses `image:` instead of `build:`.\n\n### Image structure\n\n```\ndisinto-agents:v0.1.0\n /home/agent/disinto/ # code (immutable, from COPY at build)\n /home/agent/data/ # VOLUME — runtime state, logs\n /home/agent/repos/ # VOLUME — project repos\n```\n\n### Runtime mounts (compose volumes)\n\n```yaml\nagents:\n image: ghcr.io/disinto/agents:v0.1.0\n volumes:\n - agent-data:/home/agent/data # logs, locks, state\n - project-repos:/home/agent/repos # cloned project repos\n - ./projects:/home/agent/disinto/projects:ro # project TOMLs\n - ./.env:/home/agent/disinto/.env:ro # tokens, config\n - ./state:/home/agent/disinto/state # agent activation markers\n - ~/.claude:/home/agent/.claude # Claude credentials\n - ~/.claude.json:/home/agent/.claude.json:ro\n - <claude-cli>:/usr/local/bin/claude:ro\n```\n\n### What changes\n\n- `bin/disinto init` generates compose with `image: ghcr.io/disinto/agents:<version>` instead of `build:`\n- CI pipeline (Woodpecker) builds + pushes images on tag/release\n- `disinto release` updates the image tag in the compose template\n- Same for edge, reproduce, and any other disinto containers\n- `state/` directory must be a writable mount point, not baked into the image\n\n### Images to publish\n\n| Image | Purpose |\n|-------|----------|\n| `disinto/agents` | Dev, review, gardener, planner, predictor, architect agents |\n| `disinto/reproduce` | Reproduce + triage sidecar (Playwright, Docker CLI) |\n| `disinto/edge` | Caddy reverse proxy + dispatcher |\n\n### Backwards compatibility\n\n- `disinto init --build` flag for dev mode (local build, same as today)\n- Default: `image:` from registry\n- Existing deployments: migration guide to switch from build to image\n\n## Files\n\n- `bin/disinto` — `generate_compose()` to emit `image:` instead of `build:`\n- New: CI pipeline for building + pushing images\n- New: `bin/disinto release` updates image tags\n- `docker/agents/Dockerfile` — declare VOLUME mount points explicitly\n- `docker/reproduce/Dockerfile` — same\n- `docker/edge/Dockerfile` — same\n\n## Acceptance criteria\n- [ ] CI pipeline builds and pushes `disinto/agents` image on tag/release\n- [ ] CI pipeline builds and pushes `disinto/reproduce` image on tag/release\n- [ ] CI pipeline builds and pushes `disinto/edge` image on tag/release\n- [ ] `bin/disinto init` generates compose with `image:` instead of `build:`\n- [ ] `bin/disinto init --build` flag enables local build mode for dev\n- [ ] `docker/agents/Dockerfile` declares VOLUME mount points explicitly\n- [ ] `docker/reproduce/Dockerfile` declares VOLUME mount points\n- [ ] `docker/edge/Dockerfile` declares VOLUME mount points\n\n## Affected files\n- `bin/disinto` — `generate_compose()` to emit `image:` instead of `build:`\n- `docker/agents/Dockerfile` — declare VOLUME mount points\n- `docker/reproduce/Dockerfile` — declare VOLUME mount points\n- `docker/edge/Dockerfile` — declare VOLUME mount points\n- `.woodpecker/` — new CI pipeline for building and pushing images"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"action": "add_label",
|
|
||||||
"issue": 429,
|
|
||||||
"label": "backlog"
|
"label": "backlog"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"action": "create_issue",
|
"action": "edit_body",
|
||||||
"title": "fix: vault_request RETURN trap fires prematurely when vault-env.sh is sourced",
|
"issue": 771,
|
||||||
"body": "## Problem\n\n`vault_request()` in `lib/vault.sh` uses `trap ... RETURN` to clean up its temp TOML file. However, when `vault-env.sh` is sourced inside the function (as part of validation), bash fires RETURN traps for each function call made during the source. This causes the temp file to be deleted before `validate_vault_action` reads it.\n\n## Repro\n\n```bash\nsource lib/env.sh\nsource lib/vault.sh\nsource lib/pr-lifecycle.sh\nvault_request \"test-id\" \"id = \\\"test\\\"\\nformula = \\\"run-rent-a-human\\\"\\ncontext = \\\"test\\\"\\nsecrets = []\"\n# => ERROR: File not found: /tmp/vault-XXXX.toml\n# => ERROR: TOML validation failed\n```\n\n## Root cause\n\n```bash\n# In vault_request:\ntmp_toml=$(mktemp /tmp/vault-XXXXXX.toml)\ntrap 'rm -f \"$tmp_toml\"' RETURN # <-- fires on source, not just on return\n\n# Later:\nsource \"$vault_env\" # <-- RETURN trap fires here, deleting tmp_toml\nvalidate_vault_action \"$tmp_toml\" # <-- file is gone\n```\n\n## Fix\n\nUse `EXIT` trap instead of `RETURN`, or set the trap AFTER sourcing vault-env.sh.\n\n```bash\n# Option A: trap on EXIT instead\ntrap 'rm -f \"$tmp_toml\"' EXIT\n\n# Option B: source first, set trap after \nsource \"$vault_env\"\ntrap 'rm -f \"$tmp_toml\"' RETURN\n```\n\n## Acceptance criteria\n- [ ] `vault_request` successfully validates TOML without \"File not found\" error\n- [ ] Temp file is still cleaned up after function returns\n- [ ] Existing vault test (if any) passes\n\n## Affected files\n- `lib/vault.sh` — fix `trap ... RETURN` in `vault_request()`",
|
"body": "## Symptom\n\n`docker/Caddyfile` is tracked in git with legacy content (`/forgejo/*` path). `lib/generators.sh` has a `generate_caddyfile` function that emits a different Caddyfile with `/forge/*` (post-#704 vision), `/ci/*`, `/staging/*`, and conditional `/chat/*` blocks when `EDGE_TUNNEL_FQDN` is set.\n\nBoth files exist. The edge container's compose block mounts `./docker/Caddyfile:/etc/caddy/Caddyfile`, so the **static** file is what actually serves traffic today. The generated file is written to a different path and effectively unused until someone rewires the mount.\n\nThis means:\n\n- Changes to the generator's Caddy block are invisible to running stacks (same drift class as #C).\n- The static file's `/forgejo/*` naming contradicts #704's `/forge/*` convention — anyone reading the vision will be confused by the real system.\n- Two places for the same configuration invites one-side-only edits.\n\n## Fix\n\nSingle source of truth: the file `generate_caddyfile` produces.\n\n1. Delete tracked `docker/Caddyfile`.\n2. Update `generate_caddyfile` to write to `docker/Caddyfile` (or a well-known path like `state/caddyfile/Caddyfile`, decide based on which side of the ignore/commit line fits the project) — whichever path the edge compose block mounts.\n3. Add the output path to `.gitignore` so it's a generated artifact, not tracked.\n4. Confirm `lib/generators.sh`'s compose block mounts the generator output path.\n5. Update `disinto init` flow: if a fresh init runs `generate_caddyfile` and `generate_compose` in the right order, the first `disinto up` already has a working Caddy. Document this ordering in `docs/commands.md` or equivalent.\n\n## Acceptance criteria\n\n- [ ] `docker/Caddyfile` is removed from git (no tracked static version)\n- [ ] `generate_caddyfile` writes to a single, documented output path; that path is what the edge compose block mounts\n- [ ] `.gitignore` excludes the generated Caddyfile path\n- [ ] After `disinto init` on a fresh clone, the edge container starts and serves the generator's Caddyfile — not a stale static one\n- [ ] `grep -rn \"/forgejo/\\*\" docker/` returns nothing — convention is consistently `/forge/*` everywhere\n- [ ] CI green\n\n## Note\n\nThis is independent of children A / B / C — can land whenever. No blocking dependency.\n\n## Affected files\n- `docker/Caddyfile` — delete (tracked static file to be removed)\n- `lib/generators.sh` — update `generate_caddyfile` to write to the edge-mounted path\n- `.gitignore` — exclude the generated Caddyfile path\n- `bin/disinto` — ensure `disinto init` calls `generate_caddyfile` in correct order\n- `docs/commands.md` — document Caddyfile generation ordering (if file exists)\n"
|
||||||
"labels": [
|
},
|
||||||
"backlog",
|
{
|
||||||
"bug-report"
|
"action": "add_label",
|
||||||
]
|
"issue": 771,
|
||||||
|
"label": "backlog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "edit_body",
|
||||||
|
"issue": 776,
|
||||||
|
"body": "## Problem\n\n`disinto secrets add NAME` uses `IFS= read -rs value` — TTY-only, cannot be piped. No automation path for multi-line key material (SSH keys, PEM, TLS certs). Every rent-a-human formula that needs to hand a secret to the factory currently requires either the interactive editor (`edit-vault`) or writing a plaintext file to disk first.\n\nConcrete blocker: importing `CADDY_SSH_KEY` for collect-engagement (#745) into the factory's secret store, ahead of starting the edge container.\n\n## Proposed solution\n\nMake stdin detection the dispatch inside `disinto_secrets() → add)`:\n\n- stdin is a TTY → prompt as today (preserves interactive use)\n- stdin is a pipe/redirect → read raw bytes verbatim, no prompt, no echo\n\nInvocations:\n\n```\ncat ~/caddy-collect | disinto secrets add CADDY_SSH_KEY\ndisinto secrets add CADDY_SSH_KEY < ~/caddy-collect\necho 159.89.14.107 | disinto secrets add CADDY_SSH_HOST\n```\n\nNo `--from-file` / `--from-stdin` flag ceremony. One flag exception: `--force` / `-f` to suppress the overwrite prompt for scripted upserts.\n\n## Acceptance criteria\n- [ ] Piped multi-line input stored verbatim; `disinto secrets show CADDY_SSH_KEY` round-trips byte-for-byte (diff against the source file is empty, including trailing newline)\n- [ ] TTY invocation unchanged (prompt + hidden read)\n- [ ] `-f` / `--force` skips overwrite confirmation\n- [ ] Stdin reading uses `cat` / `IFS= read -d ''` — NOT `read -rs` which strips characters\n\n## Affected files\n- `bin/disinto` — `disinto_secrets()` `add)` branch around line 1167\n\n## Context\n- `bin/disinto` → `disinto_secrets()` around line 1167 (`add)` branch).\n- Parent: sprint PR `disinto-admin/disinto-ops#10` (website-observability-wire-up).\n- Unblocks: issue C (#778 rent-a-human-caddy-ssh.toml fix).\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "add_label",
|
||||||
|
"issue": 776,
|
||||||
|
"label": "backlog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "edit_body",
|
||||||
|
"issue": 777,
|
||||||
|
"body": "## Problem\n\nTwo parallel secret stores:\n\n1. `secrets/<NAME>.enc` — per-key, age-encrypted. Populated by `disinto secrets add`. **No runtime consumer today.** Only `disinto secrets show` ever decrypts these.\n2. `.env.vault.enc` — monolithic, sops/dotenv-encrypted. The only store actually loaded into containers (via `docker/edge/dispatcher.sh` → `sops -d --output-type dotenv`).\n\nTwo mental models, redundant subcommands (`edit-vault`, `show-vault`, `migrate-vault`), and today`s `disinto secrets add` silently deposits secrets into a dead-letter directory. Operator runs the command, edge container still logs `CADDY_SSH_KEY not set, skipping` (docker/edge/entrypoint-edge.sh:207).\n\n## Proposed solution\n\nConsolidate on `secrets/<NAME>.enc` as THE store. One file per secret, granular, small surface.\n\n**1. Wire container dispatchers to load `secrets/*.enc` into env**\n- `docker/edge/dispatcher.sh` (and agent / ops dispatchers) decrypt declared secrets at startup and export them.\n- Granular per-secret — not a bulk dump.\n\n**2. Containers declare required secrets**\n- `secrets.required = [\"CADDY_SSH_KEY\", \"CADDY_SSH_HOST\", ...]` in the container's TOML, or equivalent in compose.\n- Missing required secret → **hard fail** with clear message. Replaces today's silent-skip branch at `entrypoint-edge.sh:207`.\n\n**3. Deprecate the monolithic vault**\n- Remove `.env.vault`, `.env.vault.enc`, and subcommands `edit-vault` / `show-vault` / `migrate-vault` from `bin/disinto`.\n- Remove sops round-trip from `docker/edge/dispatcher.sh` (lines 32-40 currently).\n\n**4. One-shot migration for existing operators**\n- `disinto secrets migrate-from-vault` splits an existing `.env.vault.enc` into `secrets/<KEY>.enc` files, verifies each, then removes the old vault on success.\n- Idempotent: safe to run multiple times.\n\n## Acceptance criteria\n- [ ] Edge container declares `secrets.required = [\"CADDY_SSH_KEY\", \"CADDY_SSH_HOST\", \"CADDY_SSH_USER\", \"CADDY_ACCESS_LOG\"]`. Dispatcher exports them. `collect-engagement.sh` runs without additional env wiring.\n- [ ] Container refuses to start when a required secret is missing (fail loudly, not skip silently)\n- [ ] `.env.vault*` files and all vault-specific subcommands removed from `bin/disinto` and all formulas / docs\n- [ ] `migrate-from-vault` converts an existing monolithic vault correctly (verified by round-trip test)\n- [ ] `disinto secrets` help text shows one store, four verbs: `add`, `show`, `remove`, `list`\n\n## Affected files\n- `bin/disinto` — `disinto_secrets()`: wire stdin to `secrets/<NAME>.enc`, add `migrate-from-vault` subcommand, remove `edit-vault`/`show-vault`/`migrate-vault`\n- `docker/edge/dispatcher.sh` — replace sops round-trip (lines 32-40) with per-secret decryption from `secrets/*.enc`\n- `docker/edge/entrypoint-edge.sh` — replace silent-skip branch at line 207 with hard fail on missing required secrets\n\n## Dependencies\n- #776 (piped stdin for `disinto secrets add` must land before deprecating `edit-vault`)\n\n## Context\n- Parent: sprint PR `disinto-admin/disinto-ops#10`.\n- Rationale (operator quote): \"containers should have option to load single secrets, granular. no 2 mental models, only 1 thing that works well and has small surface.\"\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "add_label",
|
||||||
|
"issue": 777,
|
||||||
|
"label": "backlog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "edit_body",
|
||||||
|
"issue": 778,
|
||||||
|
"body": "## Problem\n\n`formulas/rent-a-human-caddy-ssh.toml` step 3 tells the operator:\n\n```\necho \"CADDY_SSH_KEY=$(base64 -w0 caddy-collect)\" >> .env.vault.enc\n```\n\n**You cannot append plaintext to a sops-encrypted file.** The append silently corrupts `.env.vault.enc` — subsequent `sops -d` fails, all vault secrets become unrecoverable. Any operator who followed the docs verbatim has broken their vault.\n\nSteps 4 (`CADDY_HOST`) and 5 (`CADDY_ACCESS_LOG`) have the same bug.\n\n## Proposed fix\n\nRewrite the `>>` steps to use the stdin-piped `disinto secrets add` (from issue #776):\n\n```\ncat caddy-collect | disinto secrets add CADDY_SSH_KEY\necho '159.89.14.107' | disinto secrets add CADDY_SSH_HOST\necho 'debian' | disinto secrets add CADDY_SSH_USER\necho '/var/log/caddy/access.log' | disinto secrets add CADDY_ACCESS_LOG\n```\n\nAlso:\n- Remove the `base64 -w0` step — the new `secrets add` stores multi-line keys verbatim.\n- Remove the `shred -u caddy-collect` step from the happy path — let the operator keep the backup until they've verified the edge container picks it up.\n- Add a recovery note: operators with a corrupted vault from the old docs must `rm .env.vault.enc` (or `migrate-from-vault` if issue #777 landed) before re-running.\n\n## Acceptance criteria\n- [ ] Formula runs end-to-end without touching `.env.vault.enc` or `.env.vault` by hand\n- [ ] Re-running is idempotent (upsert via `disinto secrets add -f`)\n- [ ] Edge container starts cleanly with the imported secrets and the daily collect-engagement cron fires without `\"CADDY_SSH_KEY not set, skipping\"`\n- [ ] Recovery note present in formula for operators with corrupted vault\n\n## Affected files\n- `formulas/rent-a-human-caddy-ssh.toml` — rewrite steps 3-5 to use `disinto secrets add` instead of `>>` append to encrypted file\n\n## Dependencies\n- #776 (piped stdin for `disinto secrets add` must land first)\n\n## Context\n- Parent: sprint PR `disinto-admin/disinto-ops#10`.\n- Soft-depends on: #777 (if landed, drop all `.env.vault*` references entirely).\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "add_label",
|
||||||
|
"issue": 778,
|
||||||
|
"label": "backlog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "comment",
|
||||||
|
"issue": 758,
|
||||||
|
"body": "Vault item filed: [disinto-ops#33](http://forgejo:3000/disinto-admin/disinto-ops/pulls/33) — admin action required to unblock ops repo merges. Choose one of: (1) add planner-bot to merge allowlist in branch protection, (2) remove branch protection from disinto-ops main, or (3) create FORGE_ADMIN_TOKEN. See vault PR for details.\n"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<!-- last-reviewed: 10c7a88416b14e849f80ad3fe7ea8e51d26177e8 -->
|
<!-- last-reviewed: be463c5b439aec1ef0d4acfafc47e94896f5dc57 -->
|
||||||
# Shared Helpers (`lib/`)
|
# Shared Helpers (`lib/`)
|
||||||
|
|
||||||
All agents source `lib/env.sh` as their first action. Additional helpers are
|
All agents source `lib/env.sh` as their first action. Additional helpers are
|
||||||
|
|
@ -30,6 +30,7 @@ sourced as needed.
|
||||||
| `lib/git-creds.sh` | Shared git credential helper configuration. `configure_git_creds([HOME_DIR] [RUN_AS_CMD])` — writes a static credential helper script and configures git globally to use password-based HTTP auth (Forgejo 11.x rejects API tokens for `git push`, #361). **Retry on cold boot (#741)**: resolves bot username from `FORGE_TOKEN` with 5 retries (exponential backoff 1-5s); fails loudly and returns 1 if Forgejo is unreachable — never falls back to a wrong hardcoded default (exports `BOT_USER` on success). `repair_baked_cred_urls([--as RUN_AS_CMD] DIR ...)` — rewrites any git remote URLs that have credentials baked in to use clean URLs instead; uses `safe.directory` bypass for root-owned repos (#671). Requires `FORGE_PASS`, `FORGE_URL`, `FORGE_TOKEN`. | entrypoints (agents, edge) |
|
| `lib/git-creds.sh` | Shared git credential helper configuration. `configure_git_creds([HOME_DIR] [RUN_AS_CMD])` — writes a static credential helper script and configures git globally to use password-based HTTP auth (Forgejo 11.x rejects API tokens for `git push`, #361). **Retry on cold boot (#741)**: resolves bot username from `FORGE_TOKEN` with 5 retries (exponential backoff 1-5s); fails loudly and returns 1 if Forgejo is unreachable — never falls back to a wrong hardcoded default (exports `BOT_USER` on success). `repair_baked_cred_urls([--as RUN_AS_CMD] DIR ...)` — rewrites any git remote URLs that have credentials baked in to use clean URLs instead; uses `safe.directory` bypass for root-owned repos (#671). Requires `FORGE_PASS`, `FORGE_URL`, `FORGE_TOKEN`. | entrypoints (agents, edge) |
|
||||||
| `lib/ops-setup.sh` | `setup_ops_repo()` — creates ops repo on Forgejo if it doesn't exist, configures bot collaborators, clones/initializes ops repo locally, seeds directory structure (vault, knowledge, evidence, sprints). Evidence subdirectories seeded: engagement/, red-team/, holdout/, evolution/, user-test/. Also seeds sprints/ for architect output. Exports `_ACTUAL_OPS_SLUG`. `migrate_ops_repo(ops_root, [primary_branch])` — idempotent migration helper that seeds missing directories and .gitkeep files on existing ops repos (pre-#407 deployments). | bin/disinto (init) |
|
| `lib/ops-setup.sh` | `setup_ops_repo()` — creates ops repo on Forgejo if it doesn't exist, configures bot collaborators, clones/initializes ops repo locally, seeds directory structure (vault, knowledge, evidence, sprints). Evidence subdirectories seeded: engagement/, red-team/, holdout/, evolution/, user-test/. Also seeds sprints/ for architect output. Exports `_ACTUAL_OPS_SLUG`. `migrate_ops_repo(ops_root, [primary_branch])` — idempotent migration helper that seeds missing directories and .gitkeep files on existing ops repos (pre-#407 deployments). | bin/disinto (init) |
|
||||||
| `lib/ci-setup.sh` | `_install_cron_impl()` — installs crontab entries for bare-metal deployments (compose mode uses polling loop instead). `_create_forgejo_oauth_app()` — generic helper to create an OAuth2 app on Forgejo (shared by Woodpecker and chat). `_create_woodpecker_oauth_impl()` — creates Woodpecker OAuth2 app (thin wrapper). `_create_chat_oauth_impl()` — creates disinto-chat OAuth2 app, writes `CHAT_OAUTH_CLIENT_ID`/`CHAT_OAUTH_CLIENT_SECRET` to `.env` (#708). `_generate_woodpecker_token_impl()` — auto-generates WOODPECKER_TOKEN via OAuth2 flow. `_activate_woodpecker_repo_impl()` — activates repo in Woodpecker. All gated by `_load_ci_context()` which validates required env vars. | bin/disinto (init) |
|
| `lib/ci-setup.sh` | `_install_cron_impl()` — installs crontab entries for bare-metal deployments (compose mode uses polling loop instead). `_create_forgejo_oauth_app()` — generic helper to create an OAuth2 app on Forgejo (shared by Woodpecker and chat). `_create_woodpecker_oauth_impl()` — creates Woodpecker OAuth2 app (thin wrapper). `_create_chat_oauth_impl()` — creates disinto-chat OAuth2 app, writes `CHAT_OAUTH_CLIENT_ID`/`CHAT_OAUTH_CLIENT_SECRET` to `.env` (#708). `_generate_woodpecker_token_impl()` — auto-generates WOODPECKER_TOKEN via OAuth2 flow. `_activate_woodpecker_repo_impl()` — activates repo in Woodpecker. All gated by `_load_ci_context()` which validates required env vars. | bin/disinto (init) |
|
||||||
| `lib/generators.sh` | Template generation for `disinto init`: `generate_compose()` — docker-compose.yml (uses `codeberg.org/forgejo/forgejo:11.0` tag; adds `security_opt: [apparmor:unconfined]` to all services for rootless container compatibility; Forgejo includes a healthcheck so dependent services use `condition: service_healthy` — fixes cold-start races, #665; adds `chat` service block with isolated `chat-config` named volume and `CHAT_HISTORY_DIR` bind-mount for per-user NDJSON history persistence (#710); injects `FORWARD_AUTH_SECRET` for Caddy↔chat defense-in-depth auth (#709); cost-cap env vars `CHAT_MAX_REQUESTS_PER_HOUR`, `CHAT_MAX_REQUESTS_PER_DAY`, `CHAT_MAX_TOKENS_PER_DAY` (#711); subdomain fallback comment for `EDGE_TUNNEL_FQDN_*` vars (#713); all `depends_on` now use `condition: service_healthy/started` instead of bare service names), `generate_caddyfile()` — Caddyfile (routes: `/forge/*` → forgejo:3000, `/woodpecker/*` → woodpecker:8000, `/staging/*` → staging:80; `/chat/login` and `/chat/oauth/callback` bypass `forward_auth` so unauthenticated users can reach the OAuth flow; `/chat/*` gated by `forward_auth` on `chat:8080/chat/auth/verify` which stamps `X-Forwarded-User` (#709); root `/` redirects to `/forge/`), `generate_staging_index()` — staging index, `generate_deploy_pipelines()` — Woodpecker deployment pipeline configs. Requires `FACTORY_ROOT`, `PROJECT_NAME`, `PRIMARY_BRANCH`. | bin/disinto (init) |
|
| `lib/generators.sh` | Template generation for `disinto init`: `generate_compose()` — docker-compose.yml (uses `codeberg.org/forgejo/forgejo:11.0` tag; adds `security_opt: [apparmor:unconfined]` to all services for rootless container compatibility; Forgejo includes a healthcheck so dependent services use `condition: service_healthy` — fixes cold-start races, #665; adds `chat` service block with isolated `chat-config` named volume and `CHAT_HISTORY_DIR` bind-mount for per-user NDJSON history persistence (#710); injects `FORWARD_AUTH_SECRET` for Caddy↔chat defense-in-depth auth (#709); cost-cap env vars `CHAT_MAX_REQUESTS_PER_HOUR`, `CHAT_MAX_REQUESTS_PER_DAY`, `CHAT_MAX_TOKENS_PER_DAY` (#711); subdomain fallback comment for `EDGE_TUNNEL_FQDN_*` vars (#713); all `depends_on` now use `condition: service_healthy/started` instead of bare service names; all services now include `restart: unless-stopped` including the edge service — #768), `generate_caddyfile()` — Caddyfile (routes: `/forge/*` → forgejo:3000, `/woodpecker/*` → woodpecker:8000, `/staging/*` → staging:80; `/chat/login` and `/chat/oauth/callback` bypass `forward_auth` so unauthenticated users can reach the OAuth flow; `/chat/*` gated by `forward_auth` on `chat:8080/chat/auth/verify` which stamps `X-Forwarded-User` (#709); root `/` redirects to `/forge/`), `generate_staging_index()` — staging index, `generate_deploy_pipelines()` — Woodpecker deployment pipeline configs. Requires `FACTORY_ROOT`, `PROJECT_NAME`, `PRIMARY_BRANCH`. | bin/disinto (init) |
|
||||||
|
| `lib/sprint-filer.sh` | Post-merge sub-issue filer for sprint PRs. Invoked by the `.woodpecker/ops-filer.yml` pipeline after a sprint PR merges to ops repo `main`. Parses `<!-- filer:begin --> ... <!-- filer:end -->` blocks from sprint PR bodies to extract sub-issue definitions, creates them on the project repo using `FORGE_FILER_TOKEN` (narrow-scope `filer-bot` identity with `issues:write` only), adds `in-progress` label to the parent vision issue, and handles vision lifecycle closure when all sub-issues are closed. Uses `filer_api_all()` for paginated fetches. Idempotent: uses `<!-- decomposed-from: #<vision>, sprint: <slug>, id: <id> -->` markers to skip already-filed issues. Requires `FORGE_FILER_TOKEN`, `FORGE_API`, `FORGE_API_BASE`, `FORGE_OPS_REPO`. | `.woodpecker/ops-filer.yml` (CI pipeline on ops repo) |
|
||||||
| `lib/hire-agent.sh` | `disinto_hire_an_agent()` — user creation, `.profile` repo setup, formula copying, branch protection, and state marker creation for hiring a new agent. Requires `FORGE_URL`, `FORGE_TOKEN`, `FACTORY_ROOT`, `PROJECT_NAME`. Extracted from `bin/disinto`. | bin/disinto (hire) |
|
| `lib/hire-agent.sh` | `disinto_hire_an_agent()` — user creation, `.profile` repo setup, formula copying, branch protection, and state marker creation for hiring a new agent. Requires `FORGE_URL`, `FORGE_TOKEN`, `FACTORY_ROOT`, `PROJECT_NAME`. Extracted from `bin/disinto`. | bin/disinto (hire) |
|
||||||
| `lib/release.sh` | `disinto_release()` — vault TOML creation, branch setup on ops repo, PR creation, and auto-merge request for a versioned release. `_assert_release_globals()` validates required env vars. Requires `FORGE_URL`, `FORGE_TOKEN`, `FORGE_OPS_REPO`, `FACTORY_ROOT`, `PRIMARY_BRANCH`. Extracted from `bin/disinto`. | bin/disinto (release) |
|
| `lib/release.sh` | `disinto_release()` — vault TOML creation, branch setup on ops repo, PR creation, and auto-merge request for a versioned release. `_assert_release_globals()` validates required env vars. Requires `FORGE_URL`, `FORGE_TOKEN`, `FORGE_OPS_REPO`, `FACTORY_ROOT`, `PRIMARY_BRANCH`. Extracted from `bin/disinto`. | bin/disinto (release) |
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<!-- last-reviewed: 10c7a88416b14e849f80ad3fe7ea8e51d26177e8 -->
|
<!-- last-reviewed: be463c5b439aec1ef0d4acfafc47e94896f5dc57 -->
|
||||||
# Planner Agent
|
# Planner Agent
|
||||||
|
|
||||||
**Role**: Strategic planning using a Prerequisite Tree (Theory of Constraints),
|
**Role**: Strategic planning using a Prerequisite Tree (Theory of Constraints),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<!-- last-reviewed: 10c7a88416b14e849f80ad3fe7ea8e51d26177e8 -->
|
<!-- last-reviewed: be463c5b439aec1ef0d4acfafc47e94896f5dc57 -->
|
||||||
# Predictor Agent
|
# Predictor Agent
|
||||||
|
|
||||||
**Role**: Abstract adversary (the "goblin"). Runs a 2-step formula
|
**Role**: Abstract adversary (the "goblin"). Runs a 2-step formula
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<!-- last-reviewed: 10c7a88416b14e849f80ad3fe7ea8e51d26177e8 -->
|
<!-- last-reviewed: be463c5b439aec1ef0d4acfafc47e94896f5dc57 -->
|
||||||
# Review Agent
|
# Review Agent
|
||||||
|
|
||||||
**Role**: AI-powered PR review — post structured findings and formal
|
**Role**: AI-powered PR review — post structured findings and formal
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<!-- last-reviewed: 10c7a88416b14e849f80ad3fe7ea8e51d26177e8 -->
|
<!-- last-reviewed: be463c5b439aec1ef0d4acfafc47e94896f5dc57 -->
|
||||||
# Supervisor Agent
|
# Supervisor Agent
|
||||||
|
|
||||||
**Role**: Health monitoring and auto-remediation, executed as a formula-driven
|
**Role**: Health monitoring and auto-remediation, executed as a formula-driven
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue