diff --git a/AGENTS.md b/AGENTS.md index ee495cb1..ce7d3cd2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -244,58 +244,35 @@ When you create a PR or start work, leave a clear signal: - **PR title prefix**: Use a descriptive prefix like `[nostr-core]`, `[playback]`, `[ui]`, `[ci]` so the scope is visible at a glance. - **PR description**: Include a "Files Modified" section listing the key files touched, so other agents can quickly detect conflicts. -### Task Claiming Protocol (Distributed Lock) +### Task Claiming Protocol (Nostr Distributed Lock) -To prevent multiple agents from working on the same task simultaneously, every agent must **check for existing locks and create a claim** before starting work. The lock is broadcast via Nostr relays (primary) or GitHub PRs (fallback), making it visible to all agents across all platforms (Claude Code, Codex, Jules). +To prevent multiple agents from working on the same task simultaneously, every agent must **check for existing locks and create a claim** before starting work. Locks are broadcast via Nostr relays using NIP-78 events with NIP-40 auto-expiration, making them visible to all agents across all platforms (Claude Code, Codex, Jules). No tokens or secrets are needed. **Before starting any task:** -1. **Check for existing claims.** Run both checks and merge the exclusion sets: - - **Nostr lock check (primary):** +1. **Check for existing claims:** ```bash node scripts/agent/nostr-lock.mjs check --cadence ``` - Returns JSON with `locked` (claimed agents) and `available` (free agents) arrays. + Returns JSON with `locked` (claimed agents) and `available` (free agents) arrays. If an agent appears in the `locked` list, **skip it**. - **GitHub PR check (supplemental):** - ``` - curl -s "https://api.github.com/repos/PR0M3TH3AN/bitvid/pulls?state=open&per_page=100" | jq '{count: length, titles: [.[].title]}' - ``` - If an agent appears in either the Nostr lock list or has an open PR, **skip that agent**. - -2. **Claim the task.** Use one of these methods (try in order): - - **Method A — Nostr lock (preferred, works everywhere Node.js is available):** +2. **Claim the task:** ```bash AGENT_PLATFORM= \ node scripts/agent/nostr-lock.mjs lock \ --agent \ --cadence ``` - Generates an ephemeral keypair, publishes a NIP-78 lock event with NIP-40 auto-expiration (2 hours) to public Nostr relays. No tokens or secrets needed — the key is used once and discarded. The script includes a built-in race check: exit code 0 = lock acquired, exit code 3 = lost race (another agent claimed first). + The script generates an ephemeral keypair, publishes a lock event to public Nostr relays, and performs a built-in race check. The key is used once and discarded. Locks auto-expire after 2 hours (configurable via `NOSTR_LOCK_TTL`). - **Method B — git push (fallback when relays are unreachable):** - a. Create your working branch. - b. Make a minimal initial commit (e.g., create a `context/CONTEXT_.md` with the task scope). - c. Create a `started` log file in the appropriate task-log directory. - d. Push the branch immediately to make the claim visible. - e. Only after the branch is pushed and the claim is visible, begin the actual work. + - **Exit 0** = lock acquired, begin work. + - **Exit 3** = race lost, another agent claimed first. Go back to step 1. + - **Exit 2** = relay error. Write a `_failed.md` log and stop. - **Method C — GitHub API (fallback when git push is also blocked):** +3. **View all active locks** (optional, for debugging): ```bash - bash scripts/agent/claim-task-api.sh \ - --agent \ - --cadence \ - --base unstable + node scripts/agent/nostr-lock.mjs list ``` - Creates the remote branch, `_started.md` log file, and draft PR via `curl` and the GitHub REST API. Requires `GITHUB_TOKEN` or `GH_TOKEN` in the environment. - - If **no method** works, do not proceed — write a `_failed.md` log and stop. - -3. **Handle race conditions.** - - If Method A (Nostr) was used, the race check is automatic (built into the script). - - If Method B or C was used, re-check for duplicates via the GitHub PR preflight. If you find another agent's claim that was created **before** yours, abandon your branch and skip the task. **Scheduler agents:** Daily and weekly scheduler agents must follow this protocol in addition to their directory-based rotation logic (`docs/agents/task-logs/daily/` and `docs/agents/task-logs/weekly/`). The lock check happens _after_ determining the next task but _before_ executing it. See the scheduler prompts for the specific implementation steps. diff --git a/docs/agents/prompts/META_PROMPTS.md b/docs/agents/prompts/META_PROMPTS.md index 7ac4d2fe..2e2f85ea 100644 --- a/docs/agents/prompts/META_PROMPTS.md +++ b/docs/agents/prompts/META_PROMPTS.md @@ -17,33 +17,24 @@ MUST 1: Set cadence config to: - branch_prefix = agents/daily/ - prompt_dir = docs/agents/prompts/daily/ -MUST 2: Run BOTH preflight commands to build the exclusion set: +MUST 2: Run preflight to get the exclusion set: -a) Nostr lock check (primary): node scripts/agent/nostr-lock.mjs check --cadence daily -b) GitHub PR check (supplemental): -curl -s "https://api.github.com/repos/PR0M3TH3AN/bitvid/pulls?state=open&per_page=100" | jq '{count: length, prs: [.[] | {number, created_at, draft, head: {ref: .head.ref}, agent: ((.head.ref | capture("^agents/daily/(?[^/]+)/")?.agent) // (.title | capture("(?[A-Za-z0-9-]+-agent)")?.agent // null))}] | sort_by(.created_at, .number)}' +Use the `locked` array from the JSON output as the exclusion set. -Exclusion set = union of Nostr locked agents + agents with open PRs. - -MUST 3: Run the exact command list in this order: +MUST 3: Run these commands in this order: 1) cat AGENTS.md CLAUDE.md 2) ls -1 docs/agents/task-logs/daily/ | sort | tail -n 1 -3) [claim] Use the Nostr lock (preferred, no tokens needed): +3) Select next roster agent not in exclusion set +4) Claim via Nostr lock: AGENT_PLATFORM=jules node scripts/agent/nostr-lock.mjs lock --agent --cadence daily - Exit 0 = lock acquired. Exit 3 = race lost, go back to step 2. - IF Nostr relays are unreachable, try git push fallback. - IF git push also fails, try: - bash scripts/agent/claim-task-api.sh --agent --cadence daily --base unstable - IF all methods fail, write `_failed.md` and stop. -4) execute selected prompt from docs/agents/prompts/daily/ -5) npm run lint -6) create `_completed.md` or `_failed.md`, commit, push (or API update) + Exit 0 = lock acquired, proceed. Exit 3 = race lost, go back to step 2. +5) Execute selected prompt from docs/agents/prompts/daily/ +6) npm run lint +7) Create `_completed.md` or `_failed.md`, commit, push -MUST 4: Race rule: the Nostr lock script handles race detection automatically (exit code 3 = lost). For git push/API fallbacks: rerun GitHub PR preflight after claiming. Earlier `created_at` wins; if tied, lower PR number wins. Print exactly one line: `RACE CHECK: won` or `RACE CHECK: lost`. - -MUST 5: If all daily agents are excluded, stop and write `_failed.md` with this exact reason: `All roster tasks currently claimed by other agents`. +MUST 4: If all daily agents are excluded, stop and write `_failed.md` with this exact reason: `All roster tasks currently claimed by other agents`. ``` ## Weekly Scheduler Meta Prompt @@ -59,31 +50,22 @@ MUST 1: Set cadence config to: - branch_prefix = agents/weekly/ - prompt_dir = docs/agents/prompts/weekly/ -MUST 2: Run BOTH preflight commands to build the exclusion set: +MUST 2: Run preflight to get the exclusion set: -a) Nostr lock check (primary): node scripts/agent/nostr-lock.mjs check --cadence weekly -b) GitHub PR check (supplemental): -curl -s "https://api.github.com/repos/PR0M3TH3AN/bitvid/pulls?state=open&per_page=100" | jq '{count: length, prs: [.[] | {number, created_at, draft, head: {ref: .head.ref}, agent: ((.head.ref | capture("^agents/weekly/(?[^/]+)/")?.agent) // (.title | capture("(?[A-Za-z0-9-]+-agent)")?.agent // null))}] | sort_by(.created_at, .number)}' +Use the `locked` array from the JSON output as the exclusion set. -Exclusion set = union of Nostr locked agents + agents with open PRs. - -MUST 3: Run the exact command list in this order: +MUST 3: Run these commands in this order: 1) cat AGENTS.md CLAUDE.md 2) ls -1 docs/agents/task-logs/weekly/ | sort | tail -n 1 -3) [claim] Use the Nostr lock (preferred, no tokens needed): +3) Select next roster agent not in exclusion set +4) Claim via Nostr lock: AGENT_PLATFORM=jules node scripts/agent/nostr-lock.mjs lock --agent --cadence weekly - Exit 0 = lock acquired. Exit 3 = race lost, go back to step 2. - IF Nostr relays are unreachable, try git push fallback. - IF git push also fails, try: - bash scripts/agent/claim-task-api.sh --agent --cadence weekly --base unstable - IF all methods fail, write `_failed.md` and stop. -4) execute selected prompt from docs/agents/prompts/weekly/ -5) npm run lint -6) create `_completed.md` or `_failed.md`, commit, push (or API update) + Exit 0 = lock acquired, proceed. Exit 3 = race lost, go back to step 2. +5) Execute selected prompt from docs/agents/prompts/weekly/ +6) npm run lint +7) Create `_completed.md` or `_failed.md`, commit, push -MUST 4: Race rule: the Nostr lock script handles race detection automatically (exit code 3 = lost). For git push/API fallbacks: rerun GitHub PR preflight after claiming. Earlier `created_at` wins; if tied, lower PR number wins. Print exactly one line: `RACE CHECK: won` or `RACE CHECK: lost`. - -MUST 5: If all weekly agents are excluded, stop and write `_failed.md` with this exact reason: `All roster tasks currently claimed by other agents`. +MUST 4: If all weekly agents are excluded, stop and write `_failed.md` with this exact reason: `All roster tasks currently claimed by other agents`. ``` diff --git a/docs/agents/prompts/scheduler-flow.md b/docs/agents/prompts/scheduler-flow.md index 67678261..96b7f4c0 100644 --- a/docs/agents/prompts/scheduler-flow.md +++ b/docs/agents/prompts/scheduler-flow.md @@ -27,96 +27,75 @@ During scheduler execution, the scheduler-specific instructions in this document - `branch_prefix` = `agents//` - `prompt_dir` = `docs/agents/prompts//` -2. **MUST** run the preflight check to build the exclusion set. Use the Nostr lock check (preferred) with GitHub PR check as supplemental: +2. **MUST** run the preflight check to build the exclusion set: - **Primary — Nostr lock check:** ```bash node scripts/agent/nostr-lock.mjs check --cadence ``` - This queries Nostr relays for active lock events and returns JSON with `locked` and `available` agent lists. - **Supplemental — GitHub PR check** (catches agents with open PRs that predate the Nostr system): - ```bash - curl -s "https://api.github.com/repos/PR0M3TH3AN/bitvid/pulls?state=open&per_page=100" | jq '{count: length, prs: [.[] | {number, created_at, draft, head: {ref: .head.ref}, agent: ((.head.ref | capture("^agents//(?[^/]+)/")?.agent) // (.title | capture("(?[A-Za-z0-9-]+-agent)")?.agent // null))}] | sort_by(.created_at, .number)}' - ``` + This queries Nostr relays for active lock events and returns JSON with `locked` and `available` agent lists. Use the `locked` array as the exclusion set. - The exclusion set is the **union** of agents locked via Nostr AND agents with open PRs. - -3. **MUST** derive excluded agents from both outputs: - - From Nostr check: use the `locked` array directly. - - From GitHub PR check: 1) parse `head.ref` as `agents///...`; 2) fallback to title regex; 3) if still unknown, stop with a global lock message. - -4. **MUST** run this command and read both policy files before selecting an agent: +3. **MUST** read both policy files before selecting an agent: ```bash cat AGENTS.md CLAUDE.md ``` -5. **MUST** run this command to identify the latest cadence log file, then choose the next roster agent not in the exclusion set: +4. **MUST** run this command to identify the latest cadence log file, then choose the next roster agent not in the exclusion set: ```bash ls -1 | sort | tail -n 1 ``` -6. **MUST** stop and write a `_failed.md` log with `All roster tasks currently claimed by other agents` when every roster agent is excluded. +5. **MUST** stop and write a `_failed.md` log with `All roster tasks currently claimed by other agents` when every roster agent is excluded. -7. **MUST** claim the selected agent using **one** of these methods (try in order): +6. **MUST** claim the selected agent: - **Method A — Nostr lock (preferred, works everywhere Node.js is available):** ```bash AGENT_PLATFORM= \ node scripts/agent/nostr-lock.mjs lock \ --agent \ --cadence ``` - This generates an ephemeral keypair, publishes a NIP-78 lock event to Nostr relays with NIP-40 auto-expiration (2 hours default), and performs a built-in race check. No tokens or secrets needed. Exit code 0 = won, exit code 3 = lost race. - **Method B — git push (when Nostr relays are unreachable):** - 1) create branch, - 2) create and commit `__started.md` in ``, - 3) push branch and commit. + The script generates an ephemeral keypair, publishes a NIP-78 lock event to Nostr relays with NIP-40 auto-expiration (2 hours), and performs a built-in race check. No tokens or secrets needed. - **Method C — GitHub API fallback (when git push is also blocked):** - ```bash - bash scripts/agent/claim-task-api.sh \ - --agent \ - --cadence \ - --base unstable - ``` - Requires `GITHUB_TOKEN` or `GH_TOKEN` in the environment. + - **Exit code 0** = lock acquired, proceed to Step 7. + - **Exit code 3** = race lost (another agent claimed first). Return to Step 2. + - **Exit code 2** = relay error. Write a `_failed.md` log with reason `Nostr relay error` and stop. - If **all three methods** fail, write a `_failed.md` log with reason `Cannot create remote claim: all locking methods failed` and stop. +7. **MUST** execute `/` end-to-end. -8. **MUST** verify the lock was acquired: - - If Method A was used: the script's built-in race check already ran. If exit code was 0, print `RACE CHECK: won`. If exit code 3, print `RACE CHECK: lost`, abandon, and return to Step 5. - - If Method B or C was used: re-run the GitHub PR preflight from Step 2. If an earlier claim exists for the same derived agent, print `RACE CHECK: lost (agent already claimed by PR #)`, abandon, and return to Step 5. +8. **MUST** create exactly one final status file (`_completed.md` or `_failed.md`), run `npm run lint`, then commit and push. -9. **MUST** execute `/` end-to-end. - -10. **MUST** create exactly one final status file (`_completed.md` or `_failed.md`), run `npm run lint`, then commit and push (or use the API to update the remote branch if push is blocked). - -## Canonical Example Run Output - -### Method A — Nostr lock (preferred) +## Canonical Example Run ```text -# Step 2: Preflight — check Nostr locks + GitHub PRs +# Step 1: Set cadence +cadence=daily + +# Step 2: Preflight — check Nostr locks $ node scripts/agent/nostr-lock.mjs check --cadence daily -{ "cadence": "daily", "date": "2026-02-14", "locked": ["audit-agent"], "available": [...], ... } +{ + "cadence": "daily", + "date": "2026-02-14", + "locked": ["audit-agent"], + "available": ["ci-health-agent", "const-refactor-agent", ...], + "lockCount": 1, + "locks": [...] +} -$ curl -s "https://api.github.com/repos/PR0M3TH3AN/bitvid/pulls?state=open&per_page=100" | jq '...' -{ "count": 2, "prs": [ ... ] } - -# Steps 3-5: Read policies, check logs, select agent +# Step 3: Read policies $ cat AGENTS.md CLAUDE.md ... +# Step 4: Check logs, select next agent $ ls -1 docs/agents/task-logs/daily/ | sort | tail -n 1 2026-02-13_18-40-00_load-test-agent_completed.md Selected agent: nip-research-agent -# Step 7: Claim via Nostr lock +# Step 6: Claim via Nostr lock $ AGENT_PLATFORM=jules node scripts/agent/nostr-lock.mjs lock --agent nip-research-agent --cadence daily Locking: agent=nip-research-agent, cadence=daily, date=2026-02-14 Step 1: Checking for existing locks... @@ -132,46 +111,12 @@ LOCK_AGENT=nip-research-agent LOCK_CADENCE=daily LOCK_EXPIRES_ISO=2026-02-14T02:00:00.000Z -# Step 9: Execute agent prompt +# Step 7: Execute agent prompt (agent runs nip-research-agent prompt...) -# Step 10: Finalize +# Step 8: Finalize $ npm run lint > lint passed Wrote: docs/agents/task-logs/daily/2026-02-14_00-12-00_nip-research-agent_completed.md ``` - -### Method B — git push (fallback if relays are unreachable) - -```text -$ node scripts/agent/nostr-lock.mjs check --cadence daily -nostr-lock failed: Relay query timed out -# Nostr relays unreachable — fall back to GitHub PR check only - -$ curl -s "https://api.github.com/repos/PR0M3TH3AN/bitvid/pulls?state=open&per_page=100" | jq '...' -{ "count": 12, "prs": [ ... ] } - -$ cat AGENTS.md CLAUDE.md -... - -Selected agent: nip-research-agent -Created: docs/agents/task-logs/daily/2026-02-14_00-00-00_nip-research-agent_started.md -Pushed branch: agents/daily/nip-research-agent/2026-02-14-run - -$ curl -s "https://api.github.com/repos/PR0M3TH3AN/bitvid/pulls?state=open&per_page=100" | jq '...' -RACE CHECK: won -``` - -### Method C — GitHub API (fallback if git push is also blocked) - -```text -# Both Nostr relays and git push failed -$ bash scripts/agent/claim-task-api.sh --agent nip-research-agent --cadence daily --base unstable -CLAIM_BRANCH=agents/daily/nip-research-agent/2026-02-14-run-1707868800 -CLAIM_PR_NUMBER= -CLAIM_PR_URL= - -$ curl -s "https://api.github.com/repos/PR0M3TH3AN/bitvid/pulls?state=open&per_page=100" | jq '...' -RACE CHECK: won -```