diff --git a/.docs/design-adf-fleet-allowlist-and-stuck-prs.md b/.docs/design-adf-fleet-allowlist-and-stuck-prs.md new file mode 100644 index 000000000..5cec62201 --- /dev/null +++ b/.docs/design-adf-fleet-allowlist-and-stuck-prs.md @@ -0,0 +1,320 @@ +# Implementation Plan: Reconcile ADF auto-merge allowlist fix + remediate 10 stuck fleet PRs + +**Status**: Draft — Step 1 complete; `terraphim-agents` provenance proved +**Research Doc**: `.docs/research-adf-fleet-allowlist-and-stuck-prs.md` +**Author**: session agent (Claude Code) +**Date**: 2026-07-01 +**Estimated Effort**: 1.5–4 hours remaining, gated by fix selection and deploy timing + +## Overview + +### Summary +Step 1 proved that bigbox's installed `/usr/local/bin/adf` was built from +`terraphim-agents` main commit `0a093aa1803fdbec2f145c430f94fd6310848f40`. +The remaining plan is to merge exactly one allowlist fix in `terraphim-agents`, +verify it covers all six known fleet logins, deploy it from a tracked branch +with rollback, close only the superseded PRs, finish attributing the 9 remaining +stuck PRs' CI failures, and apply a fixed decision rule to each: merge, +fix-then-merge, or close. + +### Approach +Sequential, gated steps. Step 1 is complete: the installed binary hash matches +`/home/alex/projects/terraphim/terraphim-agents/target/release/deps/adf-b7d747a1c218d613`. +No `terraphim-ai` build artefact matched. The next gate is fix selection within +`terraphim-agents`: review #70/#69 and decide whether to merge them, stack them, +or port #3065's KG-driven design. + +### Scope + +**In Scope:** +- Recording the exact repo/branch that built the current bigbox binary +- Choosing exactly one surviving `terraphim-agents` fix path from #70/#69, or a + port of terraphim-ai#3065's KG-driven design +- Verifying the chosen fix covers all six known blocked fleet logins: + `implementation-swarm`, `odilo-developer`, `meta-coordinator`, + `quality-coordinator`, `security-sentinel`, and `test-guardian` +- Deploying the chosen fix to bigbox from a tracked branch +- Closing/redirecting only the PRs superseded by the proven canonical fix +- Attributing CI failure cause for the remaining unattributed stuck PRs +- A merge/fix/close decision for each of the 9 remaining stuck PRs + +**Out of Scope:** +- Fixing every attributed CI failure in this pass (attribution ≠ remediation + for every case — see Decision Rule) +- Reconciling the full `terraphim_orchestrator` divergence between + terraphim-ai and terraphim-agents beyond this incident's allowlist fix +- Redesigning the fleet-agent Gitea login convention (`adf-` prefix + for all agents) — valuable, but a separate proposal +- Any change to the orchestrator's reconcile-tick logging behaviour (the + "re-log every 30s forever" pattern) — a robustness improvement worth its + own small ticket, not bundled here + +**Avoid At All Cost** (5/25 elimination): +- Rewriting `terraphim_orchestrator`'s auto-merge module from scratch to + "finally fix it properly" — three fixes already exist; the job is to pick + one, not add a fourth +- Merging any of the 9 stuck PRs on `mergeable=true` alone without checking + combined CI status per this plan's Decision Rule (this is exactly the + mistake that would ship terraphim-clients#26's clippy bug) +- Attempting to fix the CI-runner `rustup-with-perms` infra bug as part of + this plan — it's an ops issue on the runner host, orthogonal to any PR's + content, and owning it here would blow the scope +- Re-opening the repo-identity question without new evidence — Step 1 is now + resolved by exact hash match to a `terraphim-agents` artefact +- Treating `implementation-swarm` as the whole incident — #3024 records six + blocked fleet logins and the fix must cover all six +- Blindly adopting the current task-branch binary's other off-main changes + just because they are "already deployed" — each must be reviewed and + either fast-tracked to main or explicitly rolled back + +## Architecture + +### Data Flow (of this remediation, not of the orchestrator) +``` +Step 1 (binary provenance: DONE) ------> Step 2 (choose agents fix) + | + v +Step 3 (verify six-login coverage) ----------> Step 4 (merge chosen fix) + | + v +Step 5 (deploy chosen fix) ------------------> Step 6 (close superseded fixes) + | + v +Step 7 (finish CI taxonomy) -----------------> Step 8 (apply decision rule per PR) +``` + +### Key Design Decisions + +| Decision | Rationale | Alternatives Rejected | +|----------|-----------|------------------------| +| Treat `terraphim-agents` as canonical for this incident | Installed binary SHA-256 matches a `terraphim-agents` build artefact; no candidate `terraphim-ai` artefact matched | Trusting shell history over hash evidence; merging #3065 as production remediation | +| Merge exactly one allowlist fix path in `terraphim-agents` | Three divergent fixes for one policy will drift and recreate the incident | Merging #3065 and agents#70/#69 independently | +| Require all-six-login acceptance before merge | #3024 impact spans six fleet accounts; #3065 currently only embeds `implementation-swarm` | Treating the largest offender as the only offender | +| Prefer KG-driven allowlist if implementation cost is comparable | Editable by ops without a rebuild; aligns with Terraphim KG conventions | Fleet-config-sourced allowlist as the only source if it requires deploys for login additions | +| Deploy from a tracked branch after merge | Avoids silently continuing a task-branch-deploy pattern that has already lost commits (#3024's `38f06db0`) | Deploying an unmerged task branch directly | + +### Eliminated Options (Essentialism) + +| Option Rejected | Why Rejected | Risk of Including | +|-----------------|--------------|---------------------| +| Fixing all 9 CI-failing PRs' underlying code issues in this plan | 5 are still unattributed; fixing blind is guessing | Wasted effort on PRs that may need to be closed instead (stale, superseded) | +| Unifying terraphim-ai and terraphim-agents' orchestrator copies | Separate, larger architecture problem flagged in research §Recommendations | Scope explosion — this plan would never ship | +| Automating the merge/close decisions with a script | Only 9 PRs; a human-reviewable table is faster to produce and safer to execute than automation for a one-time cleanup | Script bugs execute against real repos with no review step | +| Deploying the current task branch plus a local patch as a one-off | Reinforces the same branch-based-deploy pattern that caused the lost-commit confusion | Lost-traceability risk; main would still not reflect production | + +### Simplicity Check +**What if this could be easy?** It would be: "prove the binary source, merge +one fix there, verify the six known logins, deploy it safely, close only the +superseded fixes, then finish the CI taxonomy and apply a simple decision +rule." That's exactly this plan's shape — eight steps, no new abstractions, and +only a tiny allowlist data change if the surviving PR does not already include +all six logins. + +**Nothing Speculative Checklist**: +- [x] No features requested beyond "reconcile the fix and clear the queue" +- [x] No new abstractions — reuses existing Gitea PR/issue primitives +- [x] No flexibility added "just in case" — the decision rule is fixed, not configurable +- [x] No error handling for scenarios that cannot occur +- [x] No speculative unification of the two orchestrator copies + +## File Changes + +No new files are required if `terraphim-agents#70` and/or #69 are selected as +the surviving fix path. A small port may be required if #3065's KG-driven design +is preferred over #70's fleet-config-sourced design. + +Operational actions: +- Gitea PR merge of exactly one surviving allowlist fix +- Gitea PR close/comment for superseded allowlist fixes +- bigbox systemd deploy from the selected canonical branch +- Gitea PR merge/close/comment for the 9 remaining stuck PRs + +## Test Strategy + +### Verification of the Chosen Allowlist Fix +| Test | How | Expected Result | +|------|-----|-----------------| +| Unit tests | `cargo test -p terraphim_orchestrator --lib` in the selected canonical repo | Pass, including author-gate policy coverage | +| Six-login coverage | Unit test or direct policy harness covering `implementation-swarm`, `odilo-developer`, `meta-coordinator`, `quality-coordinator`, `security-sentinel`, `test-guardian` | All six satisfy the author gate | +| Runtime KG source | Check whether `ADF_RECOGNISED_AGENTS_KG` is set or `kg/recognised_agents.md` exists in orchestrator working directory | Runtime uses intended KG source, or embedded fallback is known to include all six | +| Pre-commit hook | `git commit` via repo hook | Pass (formatting + workspace tests) | +| Deploy smoke | After deploy, `sudo journalctl -u adf-orchestrator -f` | Previously-blocked login (`implementation-swarm`) either stops producing rejection lines or starts logging successful auto-merge enqueue | + +### Verification of Stuck-PR Decisions +| Test | How | Expected Result | +|------|-----|-----------------| +| Taxonomy completeness | Every one of the 9 PRs has a filled-in row | No "not yet isolated" entries | +| Decision-rule correctness | Each PR action is one of {merge, rebase, fix inline, re-run CI, close} with a cited reason | No PR left in ambiguous state | + +## Implementation Steps + +### Step 1: Prove binary provenance — COMPLETE +**Result:** Installed `/usr/local/bin/adf` metadata: +- SHA-256: `5d136a617bc5aebf4cadcce9464c6b6f3e2fe484d05d589879a51f8869bb5853` +- Size: `21151432` +- Mtime: `2026-06-23 17:26:22 CEST` +- Build ID: `e890326837dcc0fcedc8e747437e58bade8fea5f` + +Exact matching artefact: +- `/home/alex/projects/terraphim/terraphim-agents/target/release/deps/adf-b7d747a1c218d613` +- Same SHA-256 and size +- Mtime: `2026-06-23 17:10:55 CEST` +- Repo state before build: `terraphim-agents` main commit + `0a093aa1803fdbec2f145c430f94fd6310848f40` + +Current `target/release/adf` no longer matches because it was rebuilt later. +This does not invalidate provenance; the exact matching artefact still exists +under `target/release/deps/`. + +**Decision:** `terraphim-agents` is canonical for this incident. + +### Step 2: Choose the single surviving `terraphim-agents` allowlist fix +**Action:** Review `terraphim-agents#70` and `terraphim-agents#69` in full and +compare them with #3065's KG-driven design. Choose exactly one path: +- Merge #70 alone if fleet-config-sourced allowlist covers all six fleet logins + and #69's message improvement is unnecessary or already included. +- Merge #70 and #69 as a stack if #70 fixes policy and #69 provides the needed + operator-facing hint. +- Port #3065's KG-driven design to `terraphim-agents` if editable KG config is + preferred over fleet-config sourcing. + +**Depends on:** Step 1 (complete). +**Test/Verification:** One PR is explicitly named as surviving; the other two +are explicitly named as superseded but not yet closed. +**Estimated:** 15–30 minutes. + +### Step 3: Verify all-six-login coverage +**Action:** Before any merge, verify the chosen fix recognises every login in +the #3024 impact table: +- `implementation-swarm` +- `odilo-developer` +- `meta-coordinator` +- `quality-coordinator` +- `security-sentinel` +- `test-guardian` + +If the surviving fix is KG-driven, the `synonyms::` line must include all six. +If it is fleet-config-driven, tests must prove all six sample fleet names pass +the author gate. If either candidate is missing coverage, update that candidate +before merge rather than accepting a partial incident fix. + +**Depends on:** Step 2. +**Test/Verification:** A test or direct policy check shows all six logins clear +the author gate, and unknown human/bot logins remain rejected. +**Estimated:** 10–20 minutes. + +### Step 4: Merge the chosen fix +**Action:** Merge the chosen PR into its repo's main branch. If a port is needed, +create and verify that PR first; do not deploy a local-only patch. + +**Depends on:** Step 3. +**Test/Verification:** Merge commit appears on the selected repo's main branch; +the relevant crate tests pass on that branch. +**Estimated:** 5–30 minutes depending on whether a port is needed. + +### Step 5: Deploy the chosen fix to bigbox +**Action:** +```bash +ssh bigbox +cd /home/alex/projects/terraphim/terraphim-agents +git fetch origin +git checkout main +git pull origin main +cargo build --release -p terraphim_orchestrator --bin adf +sha256sum /usr/local/bin/adf target/release/adf +sudo cp /usr/local/bin/adf /usr/local/bin/adf.pre-allowlist-fix +sudo systemctl stop adf-orchestrator +sudo install -m 0755 target/release/adf /usr/local/bin/adf +sudo systemctl start adf-orchestrator +sudo systemctl status adf-orchestrator --no-pager +``` + +**Depends on:** Step 4. +**Test/Verification:** After restart, tail `sudo journalctl -u adf-orchestrator +-f` and confirm a PR previously blocked with "author `implementation-swarm` is +not a recognised agent" either stops recurring or proceeds to auto-merge +enqueue. +**Estimated:** 10–20 minutes build + deploy; owner's call on timing. + +### Step 6: Close/redirect superseded allowlist PRs +**Action:** Post a closing comment on each superseded PR explaining the proven +canonical repo and the surviving fix. Close only those superseded by the merged +and deployed fix. + +**Depends on:** Step 5. +**Test/Verification:** Superseded PRs show state=closed with a redirect comment; +the surviving PR is merged. +**Estimated:** 10 minutes. + +### Step 7: Finish the CI failure taxonomy for the remaining unattributed PRs +**Depends on:** nothing (can run in parallel with Steps 1–4, but listed after +because it's lower-priority than un-breaking the allowlist). +**Action:** For each of terraphim-agents#53, terraphim-clients#54, #35, #34, +#21, and terraphim-clients#18 (conflicts case), repeat this research session's +method: get the PR's head SHA → combined status → for each `failure` context, +find the matching Actions run/job via +`GET /repos/{owner}/{repo}/actions/runs?limit=100` filtered by +`head_branch` → `GET .../jobs/{id}/logs` → grep for `error:`/`Workflow +failed:`. Record one line per PR: gate, root cause, category (genuine code +bug / infra flake / orchestrator-gate-only). +**Test/Verification:** Every one of the 9 stuck PRs (10 minus #31, already +merged) has a filled-in row in the taxonomy table — no "not yet isolated" +or "empty response" entries left. +**Estimated:** 30–45 minutes (roughly what the first 4 took, this session). + +### Step 8: Apply the Decision Rule to each of the 9 remaining PRs + +**Decision Rule** (apply mechanically once each PR's category is known): + +| Category | Action | +|---|---| +| Combined CI status = success AND mergeable = true | Merge now | +| Combined CI status = success AND mergeable = false | Rebase (or ask the fleet agent to re-run on latest `main`) — do not merge unresolved conflicts | +| Genuine code bug in the PR's own diff (e.g. clippy failure) | Do not merge; either fix inline (small, obvious fixes only — e.g. terraphim-clients#26's `field_reassign_with_default`) or comment asking the agent/owner to re-run | +| CI-runner infrastructure bug (e.g. `rustup-with-perms`), unrelated to the PR's diff | Re-run the workflow once (infra flakes often clear); if it fails identically twice, file a separate infra ticket and do not block the PR on it | +| ADF-gate-only failure (adf/pr-reviewer, adf/validation, adf/verification) with native-ci green | Check whether it correlates with the bigbox Anthropic provider outage noted in this session's earlier findings; if so, treat as a review-agent availability problem, not a PR defect — do not close or force-merge, wait for provider health to recover and let the gate re-run | +| PR superseded/duplicated by another (e.g. terraphim-clients#18 vs #20, both Refs #2366) | Compare diffs; keep the more complete/correct one, close the other with a comment linking to the survivor | + +**Depends on:** Step 7 (needs full taxonomy) and Step 5 (the allowlist fix +must be live before any of these can auto-merge even if CI is green). +**Test/Verification:** Post-execution, `terraphim-clients#18`/`#20` overlap +is resolved to exactly one open or merged PR, not two; every other PR is +either merged, has a specific fix-and-retry action recorded against it, or +is closed with a linked reason. +**Estimated:** 45–60 minutes across 9 PRs, dominated by the two duplicate +`#2366` PRs needing an actual diff comparison. + +## Rollback Plan +- **Step 4/5 (merge/deploy of chosen fix):** if the deployed fix turns out wrong + post-deploy (e.g. still doesn't recognise a login), revert the merge commit + on the selected repo's main branch, rebuild/deploy the previous binary from + `/usr/local/bin/adf.pre-allowlist-fix` or the prior known-good source state. +- **Step 6 (close superseded PRs):** if the chosen fix is reverted, re-open the + runner-up PR or create a new one. +- **Step 8 (per-PR merges):** each merge is a normal Gitea PR merge — revertible + via `git revert` on the target branch if a merged PR turns out broken. +- No schema/data migrations involved; rollback is git-native throughout. + +## Dependencies +No new crate dependencies. This plan is entirely Gitea-API-level PR/issue +operations plus one standard Rust build/deploy to bigbox. + +## Open Items + +| Item | Status | Owner | +|------|--------|-------| +| Which repo/branch built the current bigbox binary? | **Resolved: terraphim-agents main commit `0a093aa`** | session agent | +| Which allowlist fix survives (#70/#69 vs KG port)? | Pending Step 2 | session agent + Alex | +| Does the surviving fix cover all six fleet logins? | Pending Step 3 | session agent | +| Close/redirect superseded allowlist PRs | Pending Step 6 | session agent | +| Deploy chosen fix to bigbox | Pending human approval and Steps 1–4 | Alex | +| CI taxonomy for remaining unattributed PRs | Pending | session agent | +| Duplicate resolution for terraphim-clients#18 vs #20 (issue #2366) | Pending Step 5 completion | session agent | + +## Approval + +- [x] Technical review complete +- [x] Binary provenance resolved by hash/build artefact (Step 1) +- [ ] Surviving allowlist fix selected (Step 2) +- [ ] Six-login coverage verified (Step 3) +- [ ] Human approval received to execute Steps 4–8 diff --git a/.docs/research-adf-fleet-allowlist-and-stuck-prs.md b/.docs/research-adf-fleet-allowlist-and-stuck-prs.md new file mode 100644 index 000000000..b18acba6e --- /dev/null +++ b/.docs/research-adf-fleet-allowlist-and-stuck-prs.md @@ -0,0 +1,284 @@ +# Research Document: ADF auto-merge allowlist bug and 10 stuck fleet PRs + +**Status**: Draft — Step 1 provenance proved 2026-07-02 +**Author**: session agent (Claude Code) +**Date**: 2026-07-01 +**Reviewers**: Alex Mikhalev + +## Executive Summary + +The ADF auto-merge author-allowlist bug has three layers, not one: +1. **Root cause**: `author_is_agent()` in the orchestrator only recognised + `claude-code`, `root`, and `adf-*`-prefixed logins, rejecting six real fleet + agents. +2. **Deploy-source question**: Step 1 provenance is now proved. The installed + `/usr/local/bin/adf` SHA-256 exactly matches + `/home/alex/projects/terraphim/terraphim-agents/target/release/deps/adf-b7d747a1c218d613`. + The matching artefact was built from `terraphim-agents` main at commit + `0a093aa1803fdbec2f145c430f94fd6310848f40`. Therefore the surviving fix must + land in **terraphim-agents**, not only in terraphim-ai. +3. **Remaining work**: 10 PRs are still stuck. One (`terraphim-clients#31`) was + merged in this session. The other 9 fail CI for at least three independent + reasons (orchestrator-gate/provider issue, CI-runner rustup proxy bug, + genuine per-PR clippy failure) that need individual attribution before any + merge decision. + +## Essential Questions Check + +| Question | Answer | Evidence | +|----------|--------|----------| +| Energizing? | Yes | Directly unblocks North Star priority #2 (ADF stabilisation, 5+ agents reliable overnight, target already past due) | +| Leverages strengths? | Yes | Repo/Gitea archaeology and cross-repo diffing is exactly the kind of investigation this session is suited for | +| Meets real need? | Yes | ~97,000 blocked auto-merge attempts in 48h per terraphim-ai#3024; 11 real PRs sitting stuck for 2+ weeks | + +**Proceed**: Yes (3/3 YES). + +## Problem Statement + +### Description +Two related problems: +1. The ADF orchestrator's auto-merge author gate only recognises + `claude-code`, `root`, and `adf-*`-prefixed logins. Six real fleet agents + (`implementation-swarm`, `odilo-developer`, `meta-coordinator`, + `quality-coordinator`, `security-sentinel`, `test-guardian`) don't match, + so every PR they open requires manual merge forever, and the orchestrator + re-logs the rejection every ~30s reconcile tick. +2. 10 of the 11 PRs this bug surfaced are *also* failing their own CI/ADF + gates for reasons unrelated to the allowlist bug, so fixing the allowlist + alone would not make them auto-mergeable. + +### Impact +- 11 real PRs (bug fixes, feature work, CI unblocks) sitting open 2–4 weeks. +- Orchestrator log spam (one warning per blocked PR per reconcile tick, + continuously, for potentially all 11 PRs since creation). +- Confusion risk: two competing agent-generated fixes for the same root + cause already exist (terraphim-agents#70, #69) plus this session's + independent fix (terraphim-ai#3065) — three fixes, two repos, none merged. + +### Success Criteria +- The allowlist fix lands in the repo proven to produce the installed bigbox + binary, is deployed from a tracked branch, and is confirmed via a subsequent + auto-merge log line showing a previously-blocked login now clearing the gate. +- No duplicate/competing PRs left open for the same issue. +- Each of the 9 CI-failing PRs has a known, attributed failure cause (even if + not yet fixed) rather than being lumped under "the allowlist bug." + +## Current State Analysis + +### Existing Implementation + +`crates/terraphim_orchestrator/src/pr_review.rs::author_is_agent()` — pure +function, hardcoded match on `claude-code | root | adf-*`. Called from two +sites: `pr_review::evaluate()` (used in `auto_merge_impl.rs`'s +`poll_pending_reviews`) and `pr_poller::evaluate_pr_gates()`. + +### Code Locations + +| Component | Location | Purpose | +|-----------|----------|---------| +| Allowlist policy candidate A | `terraphim-ai` repo, `crates/terraphim_orchestrator/src/pr_review.rs` | PR #3065 implements a KG-driven allowlist, but **does not target the proven deployed source**. It can still inform the design if its KG approach is ported. Gap found during review: its current KG synonyms list only `implementation-swarm`, not all six blocked fleet logins. | +| Allowlist policy candidate B (**proven deploy source**) | `terraphim-agents` repo, `crates/terraphim_orchestrator/src/pr_review.rs` | Installed binary hash matches a build artefact from this repo. Commit `0a093aa` contains the hardcoded `author_is_agent()` policy and `blocker_kind` logging observed in production. | +| Candidate fix #1 | `terraphim-agents#70` "Fix terraphim-ai#3024: source auto-merge agent-author allowlist from fleet config" | Open; now the primary candidate because it targets the proven deploy source. Must be reviewed for six-login coverage and interaction with #69. | +| Candidate fix #2 | `terraphim-agents#69` "Fix terraphim-ai#3028: include allowlist hint in auto-merge author-rejection" | Open; likely companion/supplement to #70. Must be reviewed before choosing final merge path. | +| Issue tracking | `terraphim-ai#3024`, `terraphim-ai#3028` | Filed 2026-06-29 by ADF itself, ~48h before ADF's own remediation agent produced #70/#69; #3024's body records that an *earlier* attempted fix (commit `38f06db0` on a branch that no longer exists) was silently lost | +| Orchestrator config (bigbox) | `/opt/ai-dark-factory/orchestrator.toml` + `conf.d/*.toml` | Service working directory is `/opt/ai-dark-factory`; service executes `/usr/local/bin/adf orchestrator.toml`. | + +### Data Flow +Orchestrator binary (`/usr/local/bin/adf`, proven built from `terraphim-agents` +main commit `0a093aa`) polls each configured project's Gitea PRs every +reconcile tick → `evaluate_pr_gates`/`evaluate` → rejects any PR whose author +isn't in the 3-entry hardcoded allowlist → re-logs the same rejection +indefinitely, no backoff. + +### Integration Points +Gitea REST API (PR list, PR comments, commit statuses, merge). ADF fleet +agents open PRs under their own service-account logins +(`implementation-swarm`, etc.) rather than a shared `claude-code`/`adf-*` +identity — this is itself worth a design question: should fleet agents use a +uniform `adf-` login convention going forward instead of raw +agent names, to make the prefix rule sufficient without a per-agent +allowlist? + +## Constraints + +### Technical Constraints +- `pr_review.rs` module has an explicit "zero I/O" contract (see its + top-of-file doc comment) — any KG/file-based allowlist loading must live in + a separate module (respected in PR #3065 via `agent_allowlist_kg.rs`). +- The deploy-source evidence is now resolved by hash provenance: deploy work + must use `terraphim-agents` unless a deliberate architecture decision moves + ownership elsewhere. + +### Business Constraints +- North Star: ADF stabilisation target was 2026-06-15; it's now 2026-07-01, + 16 days past due, and this specific bug has been silently discarding fleet + agent work for at least that long (`38f06db0` commit loss mentioned in + #3024 suggests this bug — or attempts to fix it — predate 2026-06-29). + +### Non-Functional Requirements +Not applicable in the traditional latency/throughput sense; the relevant +"non-functional" property here is *auto-merge availability* — currently 0% +for 6 of the fleet's most active agents. + +## Vital Few (Essentialism) + +### Essential Constraints (Max 3) + +| Constraint | Why It's Vital | Evidence | +|------------|----------------|----------| +| Fix must land in `terraphim-agents` and then be deployed | A merged fix in a non-deployed repo has zero production effect | Installed binary hash matches `terraphim-agents` artefact `target/release/deps/adf-b7d747a1c218d613` | +| Must not duplicate ADF's own in-flight remediation | Three independent fixes for one bug (agents#70, agents#69, ai#3065) is worse than one, and merging more than one risks conflicting `AutoMergeCriteria` shapes | agents#70/#69 predate this session's fix by 2 days; #3065 uses a different KG-driven design | +| Surviving allowlist fix must cover all six blocked fleet logins | Covering only `implementation-swarm` leaves five known fleet agents blocked | terraphim-ai#3024 impact table lists `implementation-swarm`, `odilo-developer`, `meta-coordinator`, `quality-coordinator`, `security-sentinel`, `test-guardian` | + +### Eliminated from Scope +| Eliminated Item | Why Eliminated | +|-----------------|----------------| +| Fixing all 9 remaining PRs' CI failures in this session | Each requires per-repo, per-PR code changes across 4 unfamiliar repos not checked out locally; out of scope for a research pass | +| Redesigning the fleet-agent login convention (`adf-` uniform prefix) | Valuable idea surfaced during research but is a separate, larger design decision affecting how every fleet agent authenticates to Gitea | +| Auditing every other file that diverged between terraphim-ai's and terraphim-agents' orchestrator copies | Only `pr_review.rs`'s allowlist-relevant diff was pulled; a full reconciliation is its own project | + +## Dependencies + +### Internal Dependencies +| Dependency | Impact | Risk | +|------------|--------|------| +| terraphim-ai repo's orchestrator copy | Non-deployed reference/duplicate for this incident; PR #3065 can inform design but will not fix production alone | Medium — keeping divergent fixes open creates confusion | +| terraphim-agents repo's orchestrator copy | Proven deploy source for `/usr/local/bin/adf` | High — fix must land here to affect production | +| ADF's own remediation agent (produced #70/#69) | Already attempted this exact fix in the proven deploy source | Medium — must review and merge/adjust rather than duplicate blindly | +| `terraphim-gitea-runner` CI infrastructure | One observed failure (`unknown proxy name: 'rustup-with-perms'`) is a runner-host bug, not a code bug | Medium — unknown how many other "failing" PRs are runner flakes vs real breakage | + +## Risks and Unknowns + +### Known Risks +| Risk | Likelihood | Impact | Mitigation | +|------|------------|--------|----------| +| Plan chooses the wrong repo because it trusts shell history or AGENTS.md alone | Low after Step 1 | High (fix has no production effect) | Resolved by exact hash match to `terraphim-agents` artefact; preserve evidence in this document | +| Surviving fix only allowlists `implementation-swarm` and leaves five known fleet agents blocked | Medium | High (incident partially persists) | Candidate-fix acceptance gate must verify all six fleet logins clear `author_is_agent` | +| The current bigbox binary was built from an unmerged task branch; merging a fix to main without accounting for off-main deployed changes regresses production behaviour | Medium | Medium-high | Diff current deployed source branch against main before deploying from main | +| terraphim-agents#70/#69 or terraphim-ai#3065 are left open after canonical fix lands, creating future conflicting `AutoMergeCriteria` shapes | Medium | Low-medium (rework, not breakage) | Close superseded PRs only after canonical repo and surviving design are decided | +| Blind-merging the other 9 PRs once CI is "green" ships unrelated regressions | Low if this document's guidance is followed | High | Never merge on `mergeable=true` alone; require green combined CI status, verified per PR | + +### Open Questions +1. Are terraphim-agents#70 and #69 mutually exclusive or stackable (i.e. does #69 depend on #70's allowlist-source refactor)? +2. Does #70's fleet-config-sourced allowlist cover all six known blocked logins in the deployed `/opt/ai-dark-factory/conf.d/*.toml` agent set? +3. Should #3065's KG-driven design be ported to `terraphim-agents`, or is #70's fleet-config design preferable because `conf.d` already defines fleet agents? +4. Is `terraphim-ai`'s `terraphim_orchestrator` copy still needed, or should this duplicate be removed/retired after the incident? +5. Was the `38f06db0` "lost commit" mentioned in issue #3024 ever real, or is that itself a symptom of a broken ADF remediation-tracking mechanism worth its own investigation? + +### Assumptions Explicitly Stated +| Assumption | Basis | Risk if Wrong | Verified? | +|------------|-------|---------------|-----------| +| terraphim-agents is the deploy source for `/usr/local/bin/adf` | Installed binary SHA-256 matches `/home/alex/projects/terraphim/terraphim-agents/target/release/deps/adf-b7d747a1c218d613`; matching repo was on main commit `0a093aa` before the artefact build | If wrong, effort goes toward wrong repo | **Yes — exact hash match** | +| The 9 CI failures are independent, not systemic | Spot-checked 4 of 9 job logs, found 3 different causes (allowlist-adjacent ADF-gate failures, a runner rustup bug, a genuine clippy lint) | If wrong, a shared fix could unblock more PRs at once than this document assumes | Partially — 4/9 checked | + +## Research Findings + +### Key Insights +1. Binary provenance is resolved: `/usr/local/bin/adf` matches a + `terraphim-agents` release artefact, not any current `terraphim-ai` build. +2. This bug already has an ADF-native paper trail (issues + two competing + fix PRs) that predates this session's independent discovery — the + session should reconcile with, not duplicate, that work. +3. PR #3065's KG-driven design is operationally attractive, but its current + default `recognised_agents.md` is incomplete for #3024: it lists + `implementation-swarm` but not `odilo-developer`, `meta-coordinator`, + `quality-coordinator`, `security-sentinel`, or `test-guardian`. +4. `crates/terraphim_orchestrator` is duplicated across two Gitea repos with + diverging implementations. This is now part of the incident, not merely a + follow-up, because the competing fixes are in different copies. +5. The deployed binary was built from `terraphim-agents` main commit `0a093aa`. + Current `target/release/adf` was rebuilt later and no longer matches, but the + exact matching artefact still exists in `target/release/deps/`. +5. CI failures on the 9 non-#31 stuck PRs are **not** one problem: at least + one ADF-gate-only failure pattern (adf/pr-reviewer, adf/validation — + plausibly downstream of the Anthropic provider outage found earlier this + session), one CI-runner infrastructure bug (broken `rustup-with-perms` + proxy), and one genuine per-PR code defect (clippy `field_reassign_with_default` + in terraphim-clients#26). + +### CI Failure Taxonomy (partial — 4/9 PRs job-log-verified) + +| PR | Failing gate | Root cause found | +|---|---|---| +| terraphim-clients#26 | native-ci | Genuine code bug: `clippy::field_reassign_with_default` in `crates/terraphim_agent/src/shared_learning/wiki_sync.rs:536-537` | +| terraphim-core#9 | native-ci | CI runner infra bug: `error: unknown proxy name: 'rustup-with-perms'` — broken rustup toolchain proxy on the runner host, unrelated to PR content | +| terraphim-config-persistence#7 | adf/pr-reviewer, adf/validation, adf/verification (native-ci itself passes) | Not yet isolated; tests pass locally in the log tail, so failure is in the ADF review/validation agent step itself, not the build | +| terraphim-agents#53 | native-ci | Job log fetch returned empty via the jobs-list endpoint used; needs the same per-job drill-down as the other PRs | +| terraphim-clients#54, #35, #34, #21, #18, terraphim-clients#20 (conflicts) | mixed adf/* and native-ci | Not yet drilled into — remaining PRs still need attribution or conflict/duplicate resolution | + +### Relevant Prior Art +- terraphim-ai#3024 / #3028: same-bug issues filed by ADF, with a detailed + 48h impact table this document's estimate should defer to. +- terraphim-ai#3026 (closed unmerged): earlier attempt at fleet-config-sourced + allowlist. +- terraphim-ai#3031 (closed unmerged): earlier attempt at allowlist-hint + messaging. +- terraphim-agents#42 (merged): "classify auto-merge blockers by kind" — + introduces `blocker_kind`, but in the non-deployed repo copy. +- terraphim-agents#48 (merged): "unify auto-merge on PrGateResult commit + statuses" — another orchestrator change absent from terraphim-ai's copy. + +### Technical Spikes Needed +| Spike | Purpose | Estimated Effort | +|-------|---------|-------------------| +| Verify candidate allowlist coverage for all six fleet logins | Ensure the chosen fix actually closes #3024 rather than only unblocking `implementation-swarm` | 10–15 min | +| Review #70 and #69 together | Decide whether #70 alone, #69 alone, both stacked, or a KG-design port is the surviving `terraphim-agents` fix | 15–30 min | +| Job-log drill-down for remaining unattributed PRs | Complete the CI failure taxonomy before any merge attempt | 30–45 min | +| Diff full terraphim-ai vs terraphim-agents `terraphim_orchestrator` trees | Quantify total divergence, scope a reconciliation | 1–2 hours, separate task | + +## Recommendations + +### Proceed/No-Proceed +Proceed to design with repo identity resolved: **terraphim-agents is canonical +for the deployed `adf` binary**. The design's focus is: +1. Choose between #70/#69 and a port of #3065's KG-driven design. +2. Verify the surviving fix covers all six known fleet agent logins. +3. Deploy from `terraphim-agents` main with rollback. +4. Finish CI failure attribution for the 9 remaining stuck PRs. +5. Apply a mechanical merge/fix/close decision rule. + +### Scope Recommendations +- Design phase should produce: (a) a `terraphim-agents` fix selection plan, + (b) an all-six-login acceptance gate, (c) a deploy/rollback plan, (d) a + per-PR remediation plan for the 9 CI-failing PRs *only after* their failure + causes are fully attributed. +- Do not expand scope to the terraphim-ai/terraphim-agents duplication + itself in this pass — flag it as a follow-up architecture issue. + +### Risk Mitigation Recommendations +- Do not merge #3065 as production remediation; it targets the non-deployed + duplicate for this incident. +- Do not close #3065 until the `terraphim-agents` surviving fix is merged and + deployed, because its KG approach may still be useful as design input. +- After merging the chosen `terraphim-agents` fix, explicitly build from + `terraphim-agents` main and deploy to bigbox; do not assume merging is enough. +- Do not merge any of the 9 remaining PRs until each has a filled-in taxonomy + row and passes the Decision Rule. + +## Next Steps + +If approved: +1. **Review #70/#69 and #3065's design**: select the surviving + `terraphim-agents` fix path. +2. **Verify coverage**: ensure the selected fix recognises all six fleet logins + and still rejects unknown human/bot logins. +3. **Deploy the chosen fix** (owner's call on deploy timing): build from the + selected canonical branch, copy to `/usr/local/bin/adf`, restart + `adf-orchestrator`. +4. **Close/redirect superseded PRs**: only after the chosen fix is merged and + deployed. +5. **Finish the CI failure taxonomy** for the remaining unattributed PRs. +6. **File a follow-up issue** for the terraphim-ai/terraphim-agents + `terraphim_orchestrator` duplication as a standalone architecture problem. + +## Appendix + +### Reference Materials +- terraphim-ai#3024, #3028, #3026, #3031 (Gitea issues/PRs) +- terraphim-agents#70, #69, #48, #42 (Gitea PRs) +- Session's own PR: terraphim-ai#3065 +- Bigbox zsh history entry (line 2361): `git clone --depth 1 -b task/2301-pr-gate-result-contract … terraphim-ai.git` followed by `cargo build -p terraphim_orchestrator --release --bin adf` + +### Code Snippets +See §CI Failure Taxonomy for the two concrete job-log excerpts (clippy +lint, rustup proxy error) captured during this research pass. diff --git a/README.md b/README.md index 83a0ab61a..96572ce8d 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ cargo install terraphim-cli # Automation CLI (8 commands) - ⚡ Lightweight (15 MB RAM, 13 MB disk) - 🚀 Fast (<200ms operations) -**Learn more**: [Quickstart](QUICKSTART.md) | [Changelog](https://docs.terraphim.ai/src/changelog.html) | [Cross-Platform Guide](docs/archive/root/CROSS_PLATFORM_STATUS.md) +**Learn more**: [Quickstart](QUICKSTART.md) | [AI Agent Onboarding](docs/src/howto/ai-agent-onboarding-quickstart.md) | [Changelog](https://docs.terraphim.ai/src/changelog.html) | [Cross-Platform Guide](docs/archive/root/CROSS_PLATFORM_STATUS.md) You can use it as a local search engine, configured to search for different types of content on StackOverflow, GitHub, and the local filesystem using a predefined folder, which includes your Markdown files. @@ -183,6 +183,10 @@ pip install terraphim-automata (See the [desktop README](desktop/README.md), [TUI documentation](docs/tui-usage.md), and [development setup guide](docs/src/development-setup.md) for more details.) +### AI Agent Onboarding + +AI coding agents should start with the [AI Agent Onboarding Quickstart](docs/src/howto/ai-agent-onboarding-quickstart.md). It covers repository orientation, Terraphim-first search, Gitea task tracking, quality gates, release checks, and safe handoff expectations for work in this repository. + ## 📚 Usage Examples ### 🦀 Rust CLI/TUI diff --git a/crates/terraphim_orchestrator/kg/recognised_agents.md b/crates/terraphim_orchestrator/kg/recognised_agents.md new file mode 100644 index 000000000..a9985f997 --- /dev/null +++ b/crates/terraphim_orchestrator/kg/recognised_agents.md @@ -0,0 +1,14 @@ +# Recognised Agent + +Fleet automation accounts authorised to open auto-merge-eligible PRs. A +Gitea PR author login matching one of these `synonyms::` entries (or +starting with the `adf-` prefix, enforced separately in code) satisfies the +`require_agent_author` auto-merge gate without a human re-reviewing PR +authorship. All other quality gates (confidence, P0/P1 counts, diff size, +acceptance criteria) still apply. + +To onboard a new fleet automation account, add its exact Gitea login to the +`synonyms::` line below and restart (or wait for) the orchestrator's next +allowlist reload — no code change or rebuild required. + +synonyms:: claude-code, root, implementation-swarm diff --git a/crates/terraphim_orchestrator/src/agent_allowlist_kg.rs b/crates/terraphim_orchestrator/src/agent_allowlist_kg.rs new file mode 100644 index 000000000..0be8eb364 --- /dev/null +++ b/crates/terraphim_orchestrator/src/agent_allowlist_kg.rs @@ -0,0 +1,128 @@ +//! Loads the `recognised_agent` KG concept +//! (`crates/terraphim_orchestrator/kg/recognised_agents.md`) into the set of +//! fleet automation logins consumed by [`crate::pr_review::author_is_agent`]. +//! +//! Follows the project-wide KG markdown convention (see root `CLAUDE.md` +//! "Extending Knowledge Graph"): a `synonyms::` line lists the recognised +//! terms — here, exact-match Gitea PR author logins. Operators extend the +//! allowlist by editing that line; no rebuild is required because the +//! deployed copy under `/opt/ai-dark-factory/kg/recognised_agents.md` (or +//! the path in `ADF_RECOGNISED_AGENTS_KG`) is re-read from disk, falling +//! back to the binary's embedded copy if the file is missing. +//! +//! Parsing is a pure function ([`parse_recognised_agents`]) so it is testable +//! without I/O; only [`load_recognised_agents`] touches the filesystem. + +use std::collections::BTreeSet; +use std::path::Path; + +/// The `recognised_agents.md` shipped in the repo, embedded at compile time +/// as the fallback used when no on-disk KG file is found. +const DEFAULT_KG_MARKDOWN: &str = include_str!("../kg/recognised_agents.md"); + +/// Env var pointing at an on-disk override of `recognised_agents.md` +/// (e.g. `/opt/ai-dark-factory/kg/recognised_agents.md` in production, so +/// operators can extend the allowlist without redeploying the orchestrator +/// binary). +const KG_PATH_ENV_VAR: &str = "ADF_RECOGNISED_AGENTS_KG"; + +/// Default on-disk path checked when `ADF_RECOGNISED_AGENTS_KG` is unset, +/// relative to the orchestrator's working directory. +const DEFAULT_KG_RELATIVE_PATH: &str = "kg/recognised_agents.md"; + +/// Parse a `synonyms::` line (case-insensitive key, comma-separated values) +/// out of KG markdown, returning the recognised logins verbatim (trimmed, +/// case-preserved — Gitea logins are case-sensitive). +/// +/// Multiple `synonyms::` lines are merged. Lines that don't match the +/// directive are ignored, matching the tolerant parsing style used +/// elsewhere for this KG format (see `kg_router.rs`). +pub fn parse_recognised_agents(markdown: &str) -> BTreeSet { + markdown + .lines() + .filter_map(|line| { + let trimmed = line.trim(); + let lower = trimmed.to_ascii_lowercase(); + lower + .starts_with("synonyms::") + .then(|| trimmed["synonyms::".len()..].to_string()) + }) + .flat_map(|rest| { + rest.split(',') + .map(|s| s.trim().to_string()) + .collect::>() + }) + .filter(|s| !s.is_empty()) + .collect() +} + +/// Load the recognised-agent allowlist from disk, falling back to the +/// embedded default when the file is absent or unreadable. +/// +/// Path resolution: `ADF_RECOGNISED_AGENTS_KG` env var if set, else +/// `kg/recognised_agents.md` relative to the current working directory. +pub fn load_recognised_agents() -> BTreeSet { + let path = + std::env::var(KG_PATH_ENV_VAR).unwrap_or_else(|_| DEFAULT_KG_RELATIVE_PATH.to_string()); + let markdown = std::fs::read_to_string(Path::new(&path)) + .unwrap_or_else(|_| DEFAULT_KG_MARKDOWN.to_string()); + parse_recognised_agents(&markdown) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parses_embedded_default() { + let logins = parse_recognised_agents(DEFAULT_KG_MARKDOWN); + assert!(logins.contains("claude-code")); + assert!(logins.contains("root")); + assert!(logins.contains("implementation-swarm")); + } + + #[test] + fn parses_multiple_synonyms_lines() { + let markdown = "# Concept\n\nsynonyms:: alpha, beta\nsynonyms:: gamma\n"; + let logins = parse_recognised_agents(markdown); + assert_eq!( + logins, + ["alpha", "beta", "gamma"] + .into_iter() + .map(String::from) + .collect::>() + ); + } + + #[test] + fn ignores_unrelated_lines_and_trims_whitespace() { + let markdown = "# Title\n\nSome prose.\n\nsynonyms:: claude-code , root \n"; + let logins = parse_recognised_agents(markdown); + assert_eq!( + logins, + ["claude-code", "root"] + .into_iter() + .map(String::from) + .collect::>() + ); + } + + #[test] + fn returns_empty_set_when_no_synonyms_line_present() { + assert!(parse_recognised_agents("# Title\n\nJust prose, no directive.\n").is_empty()); + } + + #[test] + fn load_falls_back_to_embedded_default_when_env_path_missing() { + // SAFETY: test-only env mutation, no concurrent readers of this var + // in this crate's test suite. + unsafe { + std::env::set_var(KG_PATH_ENV_VAR, "/nonexistent/path/recognised_agents.md"); + } + let logins = load_recognised_agents(); + assert!(logins.contains("implementation-swarm")); + unsafe { + std::env::remove_var(KG_PATH_ENV_VAR); + } + } +} diff --git a/crates/terraphim_orchestrator/src/auto_merge_impl.rs b/crates/terraphim_orchestrator/src/auto_merge_impl.rs index e702e4676..573961de8 100644 --- a/crates/terraphim_orchestrator/src/auto_merge_impl.rs +++ b/crates/terraphim_orchestrator/src/auto_merge_impl.rs @@ -10,8 +10,8 @@ use crate::dispatcher::DispatchTask; #[cfg(feature = "quickwit")] use crate::quickwit; use crate::{ - config, dispatcher, post_merge_gate, pr_gate, pr_poller, pr_review, truncate_for_issue, - AgentOrchestrator, OrchestratorError, + agent_allowlist_kg, config, dispatcher, post_merge_gate, pr_gate, pr_poller, pr_review, + truncate_for_issue, AgentOrchestrator, OrchestratorError, }; impl AgentOrchestrator { @@ -58,7 +58,10 @@ impl AgentOrchestrator { return Ok(()); } - let criteria = pr_review::AutoMergeCriteria::default(); + let criteria = pr_review::AutoMergeCriteria { + recognised_agent_logins: agent_allowlist_kg::load_recognised_agents(), + ..pr_review::AutoMergeCriteria::default() + }; for (project_id, gitea_cfg) in targets { let tracker_cfg = terraphim_tracker::GiteaConfig { diff --git a/crates/terraphim_orchestrator/src/lib.rs b/crates/terraphim_orchestrator/src/lib.rs index 617cbba08..f94ea02ae 100644 --- a/crates/terraphim_orchestrator/src/lib.rs +++ b/crates/terraphim_orchestrator/src/lib.rs @@ -29,6 +29,7 @@ //! ``` pub mod adf_commands; +pub mod agent_allowlist_kg; pub mod agent_registry; pub mod agent_run_command; pub mod agent_run_record; diff --git a/crates/terraphim_orchestrator/src/pr_poller.rs b/crates/terraphim_orchestrator/src/pr_poller.rs index 3ce7b74d6..92045b6ef 100644 --- a/crates/terraphim_orchestrator/src/pr_poller.rs +++ b/crates/terraphim_orchestrator/src/pr_poller.rs @@ -320,9 +320,14 @@ pub fn evaluate_pr_gates( project_id: &str, criteria: &AutoMergeCriteria, ) -> EvaluationOutcome { - if criteria.require_agent_author && !author_is_agent(&pr.author_login) { + if criteria.require_agent_author + && !author_is_agent(&pr.author_login, &criteria.recognised_agent_logins) + { return EvaluationOutcome::HumanReviewNeeded { - reason: agent_author_rejection_reason(&pr.author_login), + reason: agent_author_rejection_reason( + &pr.author_login, + &criteria.recognised_agent_logins, + ), }; } if pr.diff_loc > criteria.max_diff_loc { @@ -725,8 +730,8 @@ mod tests { "reason must mention allowlisting, got: {reason}" ); assert!( - reason.contains("orchestrator.toml"), - "reason must point to the fleet config file, got: {reason}" + reason.contains("kg/recognised_agents.md"), + "reason must point to the KG allowlist file, got: {reason}" ); } diff --git a/crates/terraphim_orchestrator/src/pr_review.rs b/crates/terraphim_orchestrator/src/pr_review.rs index 13d2c3721..63aa39b39 100644 --- a/crates/terraphim_orchestrator/src/pr_review.rs +++ b/crates/terraphim_orchestrator/src/pr_review.rs @@ -57,6 +57,17 @@ pub struct AutoMergeCriteria { pub require_all_criteria: bool, pub max_diff_loc: u32, pub require_agent_author: bool, + /// Exact-match logins recognised as fleet automation accounts, in + /// addition to the `adf-` prefix rule applied by [`author_is_agent`]. + /// + /// Populated from the `recognised_agent` KG concept + /// (`crates/terraphim_orchestrator/kg/recognised_agents.md`) by + /// [`crate::agent_allowlist_kg::load_recognised_agents`] at orchestrator + /// startup; operators extend the allowlist by editing that markdown + /// file's `synonyms::` line, no rebuild required. The hardcoded default + /// here is a pure, I/O-free fallback so this module and its tests never + /// touch the filesystem. + pub recognised_agent_logins: std::collections::BTreeSet, } impl Default for AutoMergeCriteria { @@ -68,6 +79,10 @@ impl Default for AutoMergeCriteria { require_all_criteria: true, max_diff_loc: 500, require_agent_author: true, + recognised_agent_logins: ["claude-code", "root", "implementation-swarm"] + .into_iter() + .map(String::from) + .collect(), } } } @@ -155,7 +170,7 @@ pub fn parse_verdict(body: &str, comment_id: u64) -> Result String { +/// when it appears (verbatim) in `recognised_logins`, or starts with the +/// `adf-` fleet prefix. To allowlist a new automation account, add its login +/// to the `synonyms::` line in +/// `crates/terraphim_orchestrator/kg/recognised_agents.md` — no rebuild +/// required, the orchestrator re-reads it via +/// [`crate::agent_allowlist_kg::load_recognised_agents`]. +pub fn agent_author_rejection_reason( + login: &str, + recognised_logins: &std::collections::BTreeSet, +) -> String { + let known = recognised_logins + .iter() + .map(String::as_str) + .collect::>() + .join(", "); format!( "author `{login}` is not a recognised agent; human-authored PRs require manual merge. \ - To allowlist, add the login to the orchestrator fleet config \ - (`[[agents]]` in `orchestrator.toml`); recognised logins are exactly \ - `claude-code`, `root`, or any `adf-` prefix." + To allowlist, add the login to the `synonyms::` line in \ + `crates/terraphim_orchestrator/kg/recognised_agents.md`; recognised logins are \ + currently [{known}] or any `adf-` prefix." ) } /// Return `true` when `login` belongs to an automation account authorised to /// open auto-merge-eligible PRs. /// -/// Policy: the login exactly matches `claude-code` or `root`, or starts with -/// `adf-` (the ADF fleet agent prefix). Anything else — including human -/// maintainers, bots from other tenants, and Renovate/Dependabot — is -/// considered non-agent for auto-merge purposes. -pub fn author_is_agent(login: &str) -> bool { - matches!(login, "claude-code" | "root") || login.starts_with("adf-") +/// Policy: the login exactly matches an entry in `recognised_logins` (KG- +/// driven allowlist, see [`crate::agent_allowlist_kg`]), or starts with +/// `adf-` (the ADF fleet agent prefix, a structural convention rather than a +/// KG concept). Anything else — including human maintainers, bots from other +/// tenants, and Renovate/Dependabot — is considered non-agent for auto-merge +/// purposes. +pub fn author_is_agent(login: &str, recognised_logins: &std::collections::BTreeSet) -> bool { + recognised_logins.contains(login) || login.starts_with("adf-") } /// Strip HTML attributes from `

` tags so that formatting variations @@ -368,18 +399,21 @@ mod tests { #[test] fn author_is_agent_policy() { - assert!(author_is_agent("claude-code")); - assert!(author_is_agent("root")); - assert!(author_is_agent("adf-fleet")); - assert!(author_is_agent("adf-reviewer")); - assert!(!author_is_agent("alex")); - assert!(!author_is_agent("dependabot[bot]")); - assert!(!author_is_agent("renovate[bot]")); + let recognised = AutoMergeCriteria::default().recognised_agent_logins; + assert!(author_is_agent("claude-code", &recognised)); + assert!(author_is_agent("root", &recognised)); + assert!(author_is_agent("implementation-swarm", &recognised)); + assert!(author_is_agent("adf-fleet", &recognised)); + assert!(author_is_agent("adf-reviewer", &recognised)); + assert!(!author_is_agent("alex", &recognised)); + assert!(!author_is_agent("dependabot[bot]", &recognised)); + assert!(!author_is_agent("renovate[bot]", &recognised)); } #[test] fn agent_author_rejection_reason_includes_login_and_allowlist_hint() { - let reason = agent_author_rejection_reason("dependabot[bot]"); + let recognised = AutoMergeCriteria::default().recognised_agent_logins; + let reason = agent_author_rejection_reason("dependabot[bot]", &recognised); // AC: the rejection message contains the exact author login. assert!( reason.contains("author `dependabot[bot]`"), @@ -391,8 +425,8 @@ mod tests { "reason must mention allowlisting, got: {reason}" ); assert!( - reason.contains("orchestrator.toml"), - "reason must point to the fleet config file, got: {reason}" + reason.contains("kg/recognised_agents.md"), + "reason must point to the KG allowlist file, got: {reason}" ); // Hint must mirror the real author_is_agent() policy so operators are // not misled about which logins are already accepted. @@ -429,7 +463,7 @@ mod tests { }; assert!(reason.contains("renovate[bot]")); assert!(reason.contains("allowlist")); - assert!(reason.contains("orchestrator.toml")); + assert!(reason.contains("kg/recognised_agents.md")); } #[test] diff --git a/crates/terraphim_workspace/src/lib.rs b/crates/terraphim_workspace/src/lib.rs index 8acd7758e..5f5c147d0 100644 --- a/crates/terraphim_workspace/src/lib.rs +++ b/crates/terraphim_workspace/src/lib.rs @@ -676,7 +676,11 @@ mod tests { }; let mgr = WorkspaceManager::new(&config).unwrap(); - let valid = tmp.path().join("MT-42"); + // Join against the manager's canonicalised root, not `tmp.path()` + // directly: on macOS `TMPDIR` resolves through a `/var` -> `/private/var` + // symlink, so a path built from the raw `tmp.path()` would fail + // `starts_with(&mgr.root)` even though it is genuinely inside the root. + let valid = mgr.root.join("MT-42"); assert!(mgr.validate_path(&valid, "MT-42").is_ok()); } } diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 9518b0ef5..f5dd32425 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -36,6 +36,7 @@ ## How-Tos +- [AI Agent Onboarding Quickstart](./howto/ai-agent-onboarding-quickstart.md) - [Learning Capture for Claude Code](./howto/learning-capture-claude-code.md) - [Learning Capture for opencode](./howto/learning-capture-opencode.md) - [Personal Assistant Role (JMAP + Obsidian)](./howto/personal-assistant-role.md) diff --git a/docs/src/howto/ai-agent-onboarding-quickstart.md b/docs/src/howto/ai-agent-onboarding-quickstart.md new file mode 100644 index 000000000..f716a156b --- /dev/null +++ b/docs/src/howto/ai-agent-onboarding-quickstart.md @@ -0,0 +1,150 @@ +# AI Agent Onboarding Quickstart + +This quickstart is for AI coding agents working in `github.com/terraphim/terraphim-ai`. It describes the minimum safe workflow for becoming useful quickly without losing context, duplicating work, or bypassing project quality gates. + +## First five minutes + +1. Confirm where you are working. + + ```bash + pwd + git status --short + git branch --show-current + ``` + +2. Read the repository instructions before changing code. + + ```bash + git ls-files 'AGENTS.md' 'CLAUDE.md' '.docs/summary.md' 'docs/src/howto/ai-agent-onboarding-quickstart.md' + ``` + +3. Check whether the work is already in progress. + + ```bash + git fetch origin + git branch -r + gtr list-pulls --owner terraphim --repo terraphim-ai --state open + ``` + +4. Inspect existing tasks before creating new ones. + + ```bash + gtr ready --owner terraphim --repo terraphim-ai + gtr triage --owner terraphim --repo terraphim-ai + ``` + +5. Search the codebase with Terraphim tools first. + + ```bash + terraphim-grep "symbol or behaviour" --haystack code + terraphim-grep "symbol or behaviour" --haystack code -C 3 + ``` + +## Working rules + +- Treat Gitea as the source of truth for task tracking. +- Claim or update the relevant issue before substantial implementation work. +- Prefer the smallest correct change. +- Do not revert user or other-agent changes unless explicitly asked. +- Use `terraphim-grep` for content search before falling back to lower-level tools. +- Use `gtr` for issue and pull request operations. +- Keep secrets out of the repository; use 1Password injection or existing secrets configuration. +- Do not overwrite `.env` files. +- Do not use mocks in tests. +- Keep local, GitHub, and Gitea state aligned when publishing changes. + +## Common commands + +### Search + +```bash +terraphim-grep "DeviceStorage::init" --haystack code -C 3 +terraphim-grep "session persistence" --paths . --thesaurus .terraphim/thesaurus.json -n 8 -C 2 +``` + +Use file-name lookup only when you are looking for paths rather than content: + +```bash +git ls-files '*orchestrator*.rs' +fd 'config.toml' crates/ +``` + +### Task management + +```bash +gtr ready --owner terraphim --repo terraphim-ai +gtr view-issue --owner terraphim --repo terraphim-ai --index ISSUE_NUMBER +gtr comment --owner terraphim --repo terraphim-ai --index ISSUE_NUMBER --body "Progress update" +gtr list-pulls --owner terraphim --repo terraphim-ai --state open +``` + +### Rust backend + +```bash +cargo fmt --check +cargo test --workspace +cargo clippy --workspace --all-targets -- -D warnings +``` + +For focused work, prefer package-scoped commands: + +```bash +cargo test -p terraphim_service +cargo clippy -p terraphim_agent --all-targets -- -D warnings +``` + +### Frontend + +```bash +cd desktop +bun install +bun run check +bun test +``` + +## Update and release checks + +Terraphim client binaries are released from `terraphim/terraphim-clients`. Autoupdate validation should check both binaries: + +```bash +terraphim-grep --version +terraphim-grep check-update +terraphim-grep update + +terraphim-agent --version +terraphim-agent check-update +terraphim-agent update +``` + +Release archives used by autoupdate must be embedded-signed with zipsign. If update verification fails with `could not find read signatures in .tar.gz file`, the published archive is unsigned and the release pipeline must sign tar archives before upload. + +## Quality gate before handoff + +Before reporting completion, collect evidence rather than relying on intent: + +```bash +git status --short +cargo fmt --check +cargo test -p CHANGED_PACKAGE +cargo clippy -p CHANGED_PACKAGE --all-targets -- -D warnings +``` + +For changed files, run the bug scanner where available: + +```bash +ubs $(git diff --name-only --cached) +``` + +If code changed, check coverage with the most focused feasible command, for example: + +```bash +cargo llvm-cov -p CHANGED_PACKAGE --summary-only +``` + +## Handoff checklist + +- Summarise what changed and why. +- List tests, linters, coverage, and release checks that ran. +- State any blocked work or follow-up issues. +- Update the Gitea issue or pull request with the same evidence. +- Leave unrelated working tree changes untouched.