Commit Graph

24 Commits

Author SHA1 Message Date
Paul O'Reilly
3087ad7de1 fix(agent-repo): full-history clone for reference + upstream-ref fetch
Probe 7 (2026-05-08) finally surfaced the actual push error:

  ! [remote rejected] HEAD -> task-9dc266b5 (shallow update not allowed)

Cause: REFERENCE_BRANCHES handler clones with --depth 1, AR-14a then
fetches upstream-ref --depth 1. The agent's task branch is a single
commit on top of a single shallow commit — no ancestry visible. Gitea
rejects shallow pushes server-side.

Fix:
- Reference clone drops --depth 1 (full history)
- AR-14a fetch upstream-ref drops --depth 1 (full fetch from local-path)
- Cost: a few extra MB per task on tmpfs/PVC. Acceptable.

This unblocks AR-14a's upstream-seeding for the dogfood pipeline. The
agent's branch now has the full upstream history visible to gitea.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 17:09:53 +12:00
Paul O'Reilly
bf18ca3ef5 fix(agent-repo): capture git push output explicitly for diagnosis
Probe 6 (2026-05-08) showed git push failing twice with exit 1 and
zero visible output — the previous form `if cmd 2>&1; then` redirected
git's stderr to stdout where the entrypoint's stderr-only log capture
missed it. CP-side log showed only the bash `set -x` trace, not the
actual git error message (e.g., "Permission denied (publickey)" or
"remote: pre-receive hook rejected").

Refactor the push retry loop:
- Capture output to PUSH_OUT via $() with `2>&1`
- Wrap in set +e/set -e to detect non-zero without aborting
- echo PUSH_OUT to stderr (where set -x trace also goes) so the
  entrypoint's stderr capture sees it

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 17:03:12 +12:00
Paul O'Reilly
9cf016fa1c fix(agent-repo): harden AR-21 diff-verify against set-e/pipefail abort
The 2026-05-08 attempt-2 dogfood batch had 8/8 tasks "succeed" with
zero branches pushed. Root cause: my AR-21 diff-verification block was
running under set -euo pipefail without explicit error handling. A
single non-zero exit anywhere in the `git diff | tr | sed` pipeline
killed finalize.sh before the metadata write or push ran.

Specific risk: `git diff <REF_HEAD>..HEAD` returns non-zero when the
SHA is unreachable (e.g., shallow clone with init.sh fork-fallback
where upstream-ref wasn't fetched). pipefail then kills the pipeline,
set -e kills the script.

Fix: wrap the entire AR-21 block in `set +eo pipefail` (with explicit
`set -eo pipefail` restore at the end). Also:
- Use `${arr[@]:-}` instead of `${arr[@]}` for set -u safety on empty
  arrays
- Add `|| true` to git command substitutions (belt-and-braces)
- Use `printf` instead of `echo` for the comma-wrap (more portable)

Verified locally: when `/workspace/reference/main/.git` is absent the
block correctly skips with the existing fallback; when present and
upstream-ref is reachable, the block runs and reports DIFF_VERIFIED.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 16:31:33 +12:00
Paul O'Reilly
07309567d5 feat(agent-repo,airouter): seed from upstream + label-gate + diff verify
Three load-bearing fixes for the airouter dogfood pipeline, derived from
the 2026-05-08 batch-3 dogfood postmortem (gotchas-airouter.md items 27-30):

1. agent-repo/v1/init.sh — seed fresh task branches from
   /workspace/reference/main/ (the upstream clone) rather than the agent
   repo's stale main. This was THE killer for batch 3: the
   agent-runtimes-agents fork has been frozen at 2026-05-04 since the
   "Fork cleanup" PR, so every agent started from old state, missing
   recent test files and the M16/M22 scripts to delete. The fork remains
   the push remote (so finalize.sh works); only the working-tree seed
   moves to the upstream reference. Falls back to fork main when the
   reference clone isn't available (preserves legacy behavior). Tagged
   AR-14a.

2. requires_labels on contexts/composites — airouter context + both
   airouter composites declare requires_labels: [airouter] so the
   dispatcher's _collect_supported_harnesses (with the matching agent-
   runtimes change) advertises them only on dispatchers carrying the
   airouter label. Stops the main dispatcher from claiming airouter-
   labeled tasks and dying at init time. Composites that wrap label-
   restricted contexts MUST redeclare their own requires_labels — no
   auto-traversal of layers (kept simple).

3. agent-repo/v1/finalize.sh — AR-21 diff-against-upstream verification.
   New env-var protocol:
     - AGENT_EXPECTED_CHANGED_FILES (comma-separated paths that MUST
       appear in `git diff <ref/main>..HEAD`)
     - AGENT_FORBIDDEN_CHANGED_FILES (paths that MUST NOT appear)
   finalize.sh fails the task (exit 1) if either invariant is violated;
   the branch is still pushed for forensics so the operator can inspect.
   Catches BOTH the false-success mode (item 30 — agent reports succeeded
   but never changed the target file) AND the destructive-Write mode
   (item 21 — task 4a2f2988 stripped 9 unrelated functions). Also writes
   diff_verified, diff_mismatch, diff_changed_files into ci_metadata.json.

CRS pulls all three on next CP poll — no agent-runtimes image rebuild
needed for the framework parts. The matching dispatcher poller filter
ships in agent-runtimes (separate commit).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 15:45:41 +12:00
Paul O'Reilly
45ec4af0e9 docs(qwen-methodology): add real incident to Rule 1 (Write on existing files)
After the 2026-05-08 M16 Wave A1 dogfood task `4a2f2988` produced a
correct narrow change but accidentally deleted 9 unrelated functions in
the same file (Write rather than Edit), the existing Rule 1 in
qwen-code-methodology/v1/CLAUDE.md is clearly insufficient — the agent
saw it and overrode it anyway. Concrete real-world example added inline
so the next agent reading this CLAUDE.md sees the consequence: "the
narrow test passed but the application broke everywhere else, the
branch was rejected, the agent thought it had succeeded."

CRS picks up automatically on next CP poll.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 13:09:52 +12:00
Paul O'Reilly
79f00b566d feat(cp-harness): migrate from agent-runtimes/harnesses/
The agent-runtimes repo is moving to a CRS-only harness model: dispatcher
images stop baking in /app/harnesses/ and discover them via CRS at runtime.
cp-harness was the only harness that lived only in the agent-runtimes
checkout (created in M16 Phase 1 D4, commit c1df4cc) — copied here so
nothing is lost in the cutover.

cp-harness has no current consumers (no composite or template references
it); it is infrastructure for the future M16 self-orchestration api_key
submission flow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 12:25:47 +12:00
Paul O'Reilly
b1e3ee7052 fix(airouter): match ESO Secret schema (api_key) + drop dead wrapper
The airouter ESO ExternalSecret materialises a single key `api_key`
(matching the provider schema in agent-runtimes M22 Phase 8e cutover,
acct-59b7fb0b). The harness init script was checking for `auth_token` +
`base_url` (an Anthropic-compat shape that never existed in real Vault
state) and failing on every dispatch:

  ERROR: /run/agent/secrets/airouter/auth_token not readable.
  Check ESO ExternalSecret for airouter.

Surfaced as the second blocker for the M16 Wave A1 dogfood (the first
was the airouter dispatcher missing CRS sync; that fix went into
agent-runtimes-deploy@0f11cd1).

Same shape of bug as the minimax + gitea-ssh init scripts that landed
during the same M22 phase — those were fixed at the time, airouter was
not.

Changes:
- init.sh: verify the single `api_key` file (root-only, 0400 ESO mount).
  Stage to /var/agent-secrets/airouter/api_key with mode 0600 agent-owned
  (mirrors minimax pattern). H-SECRET-4 compliant — no exports.
- harness.yaml: add OPENAI_API_KEY_FILE pointing at the staged path.
  Agentic runner reads the file at request time per
  entrypoint/runners/agentic.py:146 (OPENAI_API_KEY_FILE precedence).
- Delete dead bin/anthropic-compat-wrapper.sh — confirmed unused per
  agent-runtimes/memory/log/2026-05-07.214249.md (post-M22-Phase-9 cleanup
  found these per-provider wrappers were never invoked; runner only
  prepends /opt/agent/claude-wrapper.sh).

CRS picks this up automatically on next CP poll; no agent-runtimes image
rebuild needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 12:24:58 +12:00
Paul O'Reilly
335e6f5b34 chore(z-ai): retire harness — no active subscription
Removes harnesses/contexts/z-ai/v1/. Mirrors the agent-runtimes
companion PR — the z-ai harness was never cut over to ESO and
there's no active subscription. Revival path documented in
agent-runtimes planning/future/providers F49.

Removed:
- harnesses/contexts/z-ai/v1/{harness.yaml, init.sh}

Updated:
- CLAUDE.md — drops z-ai from the 'no secrets in this repo'
  context list. Retirement note added.
- gitea-admin/v1/init.sh — comment ref to z-ai removed.
- gitea-https/v1/init.sh — comment ref to z-ai removed.

No composites layer z-ai (verified via grep across composites/);
no model registry entries reference it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 09:40:44 +12:00
Paul O'Reilly
7a75c5ec3b fix(b22b): retarget composites + delete stale anthropic-cloud/v1
Phase 8 cutover (M22) renamed the anthropic harness from anthropic-cloud
→ anthropic-cloud-paul-oauth in agent-runtimes, but the equivalent
rename in this framework's composites and the deletion of the stale
SOPS-era anthropic-cloud/v1 dir were never done. Every cloud-Anthropic
composite (haiku/sonnet/opus code, planning, security-review, spec-
writing, test-writing, best-practices) layered the dead context and
silently never resolved a credential.

- Update 14 composites to layer anthropic-cloud-paul-oauth/v1
- Delete the orphaned harnesses/contexts/anthropic-cloud/v1/ directory
  (used pre-Phase-9 SOPS file delivery; now obsolete)

Closes BUG-22b. Pairs with the live PATCH /v1/accounts/{id} rename
shipped under BUG-22a.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 07:16:09 +12:00
Paul O'Reilly
3d6a50fdd7 fix(b22a): bare account_ref for anthropic-cloud-paul-oauth harness
Same bug as upstream agent-runtimes: scoped form never matched
because the dispatcher does literal scope.id compare and
owner_principal_id stores the user UUID, not 'paul'.

Pairs with PATCH /v1/accounts/{id} renaming the live account_label
to 'anthropic-cloud-paul-oauth' so the bare ref resolves uniquely.

Closes BUG-22a.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 06:58:39 +12:00
Paul O'Reilly
f8c8805f26 fix(harnesses): stage tokens for agent UID, fix three broken auth paths
Mirrors agent-runtimes audit + fix. All three M22 Phase 9 wrapper-script
auth paths were broken since the cutover: each one ran as the agent UID
trying to `cat` a 0400 root-only ESO mount. Same root cause we hit on
minimax/z-ai earlier today.

Changes:

1. anthropic-cloud-paul-oauth/v1 (NEW in framework)
   - Mirrors agent-runtimes — was previously only present there.
   - init.sh stages oauth_token into ~/.claude/.credentials.json (Claude
     Code's native subscription-OAuth schema). No env, no apiKeyHelper,
     no wrapper. Restores the equivalent of what harness_init.py used to
     do for the legacy SOPS path.
   - The legacy `bin/anthropic-wrapper.sh` was dead code (never wired).

2. gitea-https/v1
   - init.sh stages the token to $HOME/.config/git/gitea-https-token
     (0600 agent:agent) and points the per-host git credential helper at
     the staged copy. Previously the helper `cat`d the ESO mount path
     and silently failed at every git invocation.

3. gitea-admin/v1
   - init.sh stages the token to $HOME/.config/gitea-admin/token
     (0600 agent:agent). Wrapper updated to read from the staged copy.
   - Removes stale `requires: anthropic-cloud/v1` (the only anthropic
     harness in agent-runtimes is anthropic-cloud-paul-oauth/v1).

Pattern matches gitea-ssh / minimax / z-ai: ESO mount stays root-only,
init.sh runs as root and `install -m 0600 -o agent -g agent`s a single
explicit copy. Per-secret enumeration; future ESO Secret keys remain
inaccessible by default.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 21:05:33 +12:00
Paul O'Reilly
12c2172835 fix(minimax,z-ai): stage api_key as root, restore mount mode 0400
Mirrors agent-runtimes commit 6f20b51. CRS serves these harness files to
dispatchers, so this repo must match.

Switches from "mount mode 0444 so the agent user can `cat` the ESO file"
to the gitea-ssh staging pattern: keep the ESO mount root-only (0400),
init.sh as root `install`s a per-secret 0600 agent-owned copy, and
apiKeyHelper points at the staged copy. Stronger blast-radius guarantee
— if the ESO Secret later grows additional keys, they remain root-only
unless the harness explicitly stages them.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 20:43:13 +12:00
Paul O'Reilly
d68d1b0f6b fix(minimax,z-ai): write apiKeyHelper to agent home, mode 0444 for read
Mirrors agent-runtimes commit 166c19b. CRS serves these harness files to
dispatchers, so this repo must match.

Two fixes from the failing smoke test:

1. init.sh resolves the agent user's home via getent (init.sh runs as
   root, but claude runs as the agent user — different $HOME).
2. secrets_required mode "0400" → "0444" so the agent user can read the
   ESO-mounted secret via apiKeyHelper. The file is in pod-local tmpfs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 15:42:20 +12:00
Paul O'Reilly
455d8d135b fix(z-ai): wire auth_token via apiKeyHelper, never via env
Mirrors the minimax fix to z-ai. Replaces bin/anthropic-compat-wrapper.sh
(which did `exec env ANTHROPIC_AUTH_TOKEN="$(cat ...)" claude "$@"`,
exposing the secret in the claude subprocess' /proc/<pid>/environ) with
init.sh that writes ~/.claude/settings.json with:

  apiKeyHelper: "cat /run/agent/secrets/z-ai/auth_token"

Claude Code routes apiKeyHelper output to `Authorization: Bearer <value>`
when ANTHROPIC_BASE_URL is non-anthropic.com — exactly what the Z.ai
proxy at api.z.ai/api/anthropic requires.

The legacy K8s Secret may still ship a `base_url` file; it is
intentionally ignored by init.sh (the base URL is not a credential and
lives in harness.yaml).

Mirrors agent-runtimes commit (z-ai apiKeyHelper). CRS serves these
harness files to dispatchers, so this repo must match.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 13:47:52 +12:00
Paul O'Reilly
cb1b411276 fix(minimax): wire api_key via apiKeyHelper, never via env
Mirrors agent-runtimes commit 1b83c81. CRS serves these harness files
to dispatchers, so this repo must match for the fix to take effect.

The minimax harness now writes ~/.claude/settings.json with an
apiKeyHelper that `cat`s the ESO-mounted /run/agent/secrets/minimax/
api_key file. Claude Code routes apiKeyHelper output to
`Authorization: Bearer <value>` for non-anthropic.com base URLs, which
is what MiniMax's /anthropic proxy requires. The secret is never read
into env, never written into a config file, and ESO rotation is
auto-recovered via Claude Code's per-session helper invocation
(also re-runs after a 401).

- harness.yaml: drop ANTHROPIC_AUTH_TOKEN_FILE env var
- init.sh: write (or jq-merge) settings.json with apiKeyHelper, chmod 0600
- bin/anthropic-compat-wrapper.sh: deleted

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 13:14:21 +12:00
Paul O'Reilly
dcdc6b5e0a fix(minimax): wire ANTHROPIC_AUTH_TOKEN_FILE for runner apiKeyHelper
Mirrors agent-runtimes — adds ANTHROPIC_AUTH_TOKEN_FILE pointer so the
Claude runner sets apiKeyHelper. Without it Claude Code CLI exits with
'Not logged in'.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 08:51:31 +12:00
Paul O'Reilly
3d8627f17c fix(harnesses): scoped account_ref for ambiguous gitea-* providers
gitea-ssh has 3 accounts (ai_enablement, accelerators, homelab) and
gitea-https has 2 (primary, admin) — bare provider_type refs hit
MultipleAccountsError per SR-DISP-8. Switch to scoped name.space:platform
form. Mirrors the agent-runtimes commit; the dispatcher-side parser
also gains underscore support for account names like 'ai_enablement'.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 08:27:23 +12:00
Paul O'Reilly
5f85d895c1 fix(harnesses): migrate to ESO secrets_required form, mirror agent-runtimes
The CRS-served harnesses still carried `secrets_files: [{encrypted: true}]`
which now hard-fails on H-SECRET-1 ("SOPS-encrypted secrets_files entries
are no longer permitted") in the dispatcher's harness validator. Sync the
8 provider harnesses with the agent-runtimes copies: same `secrets_required`
shape, same `init.sh` (ESO-mounted file paths), same `bin/` wrappers.

Use bare `account_ref: "<provider>"` (not `<provider>.cp:cp` — that
scope-kind isn't valid per SR-DISP-1-FIELD).

Provider key names follow the per-provider schema as emitted by the CP
provisioner: minimax/airouter/z-ai → api_key; gitea-https/gitea-admin →
{token,base_url,username}; gitea-ssh* → {host,private_key}.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 07:26:19 +12:00
Paul O'Reilly
8bbf6cbb2f BUG-5/BUG-20 fixes: pre_test.sh, output validation, push retry, workflow output tags
- pre_test.sh: harness-level test folder revert script, replaces inline
  _revert_test_folders() in entrypoint. Writes {"reverted": N} atomically.
- finalize.sh: set -x debug tracing; push retry (AGENT_PUSH_RETRIES, default 1
  retry after 5s); output validation (AGENT_EXPECTED_OUTPUT env var); wrong-path
  detection moves .agent-output/ files to working dir and writes
  .correction-prompt.txt for entrypoint re-invoke
- harness.yaml: add scripts.pre_test
- spec-planning.yaml: all 17 nodes tagged with output:{path,min_bytes}

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 18:29:47 +12:00
Paul O'Reilly
21bdeb0c0e Add MiniMax test-writing composite and template
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 09:32:46 +12:00
Paul O'Reilly
f392f72ca2 Add tdd-gate context, *-code-tdd-repo composites, and TDD task templates
Adds the tdd-gate harness context (TEST_PASS_REQUIRED=true + CLAUDE.md
methodology) and five provider-specific TDD composites that layer
tdd-protect + tdd-gate + python-dev + tdd-file-lock on top of the
existing code-* layer stacks. Matching task templates for sonnet, opus,
haiku, minimax, and airouter. allowed_failing_tests support noted in
warnings pending M19 pre-test hook implementation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 13:48:35 +12:00
Paul O'Reilly
c0758cbd71 fix(harness): align workspace paths and best-practices mount target
- Mount best-practices context at /workspace/best-practices/ (was
  /opt/harness/context/best-practices/) for consistent agent access
- Fix /workspace/working/ → /workspace/project/ in all CLAUDE.md files
  (planning, spec-writing, security-review, code-methodology,
  qwen-code-methodology, test-writing)
- Update best-practices path references in all CLAUDE.md files to
  /workspace/best-practices/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 13:24:10 +12:00
Paul O'Reilly
0272e3df1e chore: sync framework content from agent-runtimes main (2026-04-28)
- Hardens qwen-code-methodology rules (no code deletion, pre-commit diff)
- Fixes test-writing CLAUDE.md workspace path
- Adds best-practices-opus-repo/v1 composite harness
- Adds opus-best-practices-review task template
- Adds max_turns support to airouter task template defaults
- Updates agent-repo finalize.sh

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 10:58:01 +12:00
Paul O'Reilly
37a5165dfb init: seed framework reference content from agent-runtimes main repo 2026-04-26 12:17:42 +12:00