mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-31 09:52:24 +00:00
feat(agents): add Nostr-based distributed task locking via NIP-78
Replace GitHub draft PR as the primary locking mechanism with Nostr relay-based locks. Each agent generates an ephemeral keypair, publishes a NIP-78 (Kind 30078) lock event with NIP-40 auto-expiration, and discards the key. No tokens, no secrets, no git push required. New script: scripts/agent/nostr-lock.mjs - check: query relays for active locks, return JSON exclusion set - lock: claim a task with built-in race detection (exit 3 = lost) - list: human-readable table of all active locks The locking fallback chain is now: 1. Nostr lock (no credentials needed, works in any sandbox) 2. git push + draft PR (traditional method) 3. GitHub API via curl (needs GITHUB_TOKEN) Updated scheduler-flow.md, META_PROMPTS.md, and AGENTS.md to document all three methods with Nostr as the preferred primary. https://claude.ai/code/session_01BpDsVSAkVWtJLnfKxsB9ib
This commit is contained in:
4 files changed
+662
-89
No files matched your search
@@ -17,25 +17,31 @@ MUST 1: Set cadence config to:
|
||||
- branch_prefix = agents/daily/
|
||||
- prompt_dir = docs/agents/prompts/daily/
|
||||
|
||||
MUST 2: Run this exact preflight command first:
|
||||
MUST 2: Run BOTH preflight commands to build 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>[^/]+)/")?.agent) // (.title | capture("(?<agent>[A-Za-z0-9-]+-agent)")?.agent // null))}] | sort_by(.created_at, .number)}'
|
||||
|
||||
Exclusion set = union of Nostr locked agents + agents with open PRs.
|
||||
|
||||
MUST 3: Run the exact command list in this order:
|
||||
1) cat AGENTS.md CLAUDE.md
|
||||
2) ls -1 docs/agents/task-logs/daily/ | sort | tail -n 1
|
||||
3) [claim] Try: create branch, create+commit `_started.md`, push.
|
||||
IF git push fails or is blocked, use the API fallback instead:
|
||||
3) [claim] Use the Nostr lock (preferred, no tokens needed):
|
||||
AGENT_PLATFORM=jules node scripts/agent/nostr-lock.mjs lock --agent <agent-name> --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 <agent-name> --cadence daily --base unstable
|
||||
This creates the remote branch, started.md, and draft PR via curl.
|
||||
Requires GITHUB_TOKEN or GH_TOKEN in environment.
|
||||
IF both methods fail, write `_failed.md` and stop.
|
||||
4) rerun preflight command from MUST 2
|
||||
5) execute selected prompt from docs/agents/prompts/daily/
|
||||
6) npm run lint
|
||||
7) create `_completed.md` or `_failed.md`, commit, push (or API update)
|
||||
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)
|
||||
|
||||
MUST 4: Race rule: after step 3, compare only matching derived `agent` claims from the rerun. Earlier `created_at` wins; if tied, lower PR number wins. Print exactly one line: `RACE CHECK: won` or `RACE CHECK: lost (agent already claimed by PR #<number>)`.
|
||||
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`.
|
||||
```
|
||||
@@ -53,25 +59,31 @@ MUST 1: Set cadence config to:
|
||||
- branch_prefix = agents/weekly/
|
||||
- prompt_dir = docs/agents/prompts/weekly/
|
||||
|
||||
MUST 2: Run this exact preflight command first:
|
||||
MUST 2: Run BOTH preflight commands to build 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>[^/]+)/")?.agent) // (.title | capture("(?<agent>[A-Za-z0-9-]+-agent)")?.agent // null))}] | sort_by(.created_at, .number)}'
|
||||
|
||||
Exclusion set = union of Nostr locked agents + agents with open PRs.
|
||||
|
||||
MUST 3: Run the exact command list in this order:
|
||||
1) cat AGENTS.md CLAUDE.md
|
||||
2) ls -1 docs/agents/task-logs/weekly/ | sort | tail -n 1
|
||||
3) [claim] Try: create branch, create+commit `_started.md`, push.
|
||||
IF git push fails or is blocked, use the API fallback instead:
|
||||
3) [claim] Use the Nostr lock (preferred, no tokens needed):
|
||||
AGENT_PLATFORM=jules node scripts/agent/nostr-lock.mjs lock --agent <agent-name> --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 <agent-name> --cadence weekly --base unstable
|
||||
This creates the remote branch, started.md, and draft PR via curl.
|
||||
Requires GITHUB_TOKEN or GH_TOKEN in environment.
|
||||
IF both methods fail, write `_failed.md` and stop.
|
||||
4) rerun preflight command from MUST 2
|
||||
5) execute selected prompt from docs/agents/prompts/weekly/
|
||||
6) npm run lint
|
||||
7) create `_completed.md` or `_failed.md`, commit, push (or API update)
|
||||
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)
|
||||
|
||||
MUST 4: Race rule: after step 3, compare only matching derived `agent` claims from the rerun. Earlier `created_at` wins; if tied, lower PR number wins. Print exactly one line: `RACE CHECK: won` or `RACE CHECK: lost (agent already claimed by PR #<number>)`.
|
||||
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`.
|
||||
```
|
||||
@@ -27,14 +27,24 @@ During scheduler execution, the scheduler-specific instructions in this document
|
||||
- `branch_prefix` = `agents/<cadence>/`
|
||||
- `prompt_dir` = `docs/agents/prompts/<cadence>/`
|
||||
|
||||
2. **MUST** run this command first and save the JSON output as the preflight exclusion set:
|
||||
2. **MUST** run the preflight check to build the exclusion set. Use the Nostr lock check (preferred) with GitHub PR check as supplemental:
|
||||
|
||||
**Primary — Nostr lock check:**
|
||||
```bash
|
||||
node scripts/agent/nostr-lock.mjs check --cadence <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/<cadence>/(?<agent>[^/]+)/")?.agent) // (.title | capture("(?<agent>[A-Za-z0-9-]+-agent)")?.agent // null))}] | sort_by(.created_at, .number)}'
|
||||
```
|
||||
|
||||
3. **MUST** derive excluded agents from command output using this order only:
|
||||
1) parse `head.ref` as `agents/<cadence>/<agent-name>/...`; 2) fallback to title regex; 3) if still unknown, stop with a global lock message.
|
||||
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/<cadence>/<agent-name>/...`; 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:
|
||||
|
||||
@@ -42,7 +52,7 @@ During scheduler execution, the scheduler-specific instructions in this document
|
||||
cat AGENTS.md CLAUDE.md
|
||||
```
|
||||
|
||||
5. **MUST** run this command to identify the latest cadence log file, then choose the next roster agent not excluded by Step 2:
|
||||
5. **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 <log_dir> | sort | tail -n 1
|
||||
@@ -52,24 +62,34 @@ During scheduler execution, the scheduler-specific instructions in this document
|
||||
|
||||
7. **MUST** claim the selected agent using **one** of these methods (try in order):
|
||||
|
||||
**Method A — git push (preferred):**
|
||||
**Method A — Nostr lock (preferred, works everywhere Node.js is available):**
|
||||
```bash
|
||||
AGENT_PLATFORM=<jules|claude-code|codex> \
|
||||
node scripts/agent/nostr-lock.mjs lock \
|
||||
--agent <agent-name> \
|
||||
--cadence <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 `<timestamp>_<agent>_started.md` in `<log_dir>`,
|
||||
3) push branch and commit.
|
||||
|
||||
**Method B — GitHub API fallback (when git push is blocked):**
|
||||
If `git push` fails or is unavailable (e.g., Jules sandbox), use the API claim script:
|
||||
**Method C — GitHub API fallback (when git push is also blocked):**
|
||||
```bash
|
||||
bash scripts/agent/claim-task-api.sh \
|
||||
--agent <agent-name> \
|
||||
--cadence <cadence> \
|
||||
--base unstable
|
||||
```
|
||||
This creates the remote branch, `_started.md` log file, and draft PR entirely via the GitHub REST API using `curl`. Requires `GITHUB_TOKEN` or `GH_TOKEN` in the environment.
|
||||
Requires `GITHUB_TOKEN` or `GH_TOKEN` in the environment.
|
||||
|
||||
If **both** methods fail (no push access AND no API token), write a `_failed.md` log with reason `Cannot create remote claim: git push blocked and no GITHUB_TOKEN available` and stop.
|
||||
If **all three methods** fail, write a `_failed.md` log with reason `Cannot create remote claim: all locking methods failed` and stop.
|
||||
|
||||
8. **MUST** re-run the same PR preflight command from Step 2 immediately after the claim is visible remotely. If an earlier claim exists for the same derived agent, print `RACE CHECK: lost (agent already claimed by PR #<number>)`, abandon the run, and return to Step 5. Otherwise print `RACE CHECK: won`.
|
||||
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 #<number>)`, abandon, and return to Step 5.
|
||||
|
||||
9. **MUST** execute `<prompt_dir>/<prompt-file>` end-to-end.
|
||||
|
||||
@@ -77,71 +97,81 @@ During scheduler execution, the scheduler-specific instructions in this document
|
||||
|
||||
## Canonical Example Run Output
|
||||
|
||||
### Method A (git push)
|
||||
### Method A — Nostr lock (preferred)
|
||||
|
||||
```text
|
||||
$ 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>[^/]+)/")?.agent) // (.title | capture("(?<agent>[A-Za-z0-9-]+-agent)")?.agent // null))}] | sort_by(.created_at, .number)}'
|
||||
{ "count": 12, "prs": [ ... ] }
|
||||
# Step 2: Preflight — check Nostr locks + GitHub PRs
|
||||
$ node scripts/agent/nostr-lock.mjs check --cadence daily
|
||||
{ "cadence": "daily", "date": "2026-02-14", "locked": ["audit-agent"], "available": [...], ... }
|
||||
|
||||
$ 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
|
||||
$ cat AGENTS.md CLAUDE.md
|
||||
# bitvid — AI Agent Guide
|
||||
...
|
||||
|
||||
$ 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
|
||||
$ 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...
|
||||
Step 2: Generating ephemeral keypair...
|
||||
Step 3: Building lock event...
|
||||
Step 4: Publishing to relays...
|
||||
Published to 3/3 relays
|
||||
Step 5: Race check...
|
||||
RACE CHECK: won
|
||||
LOCK_STATUS=ok
|
||||
LOCK_EVENT_ID=ea2724c76d2bd707...
|
||||
LOCK_AGENT=nip-research-agent
|
||||
LOCK_CADENCE=daily
|
||||
LOCK_EXPIRES_ISO=2026-02-14T02:00:00.000Z
|
||||
|
||||
# Step 9: Execute agent prompt
|
||||
(agent runs nip-research-agent prompt...)
|
||||
|
||||
# Step 10: 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 '...'
|
||||
{ "count": 13, "prs": [ ... ] }
|
||||
RACE CHECK: won
|
||||
|
||||
$ npm run lint
|
||||
> lint passed
|
||||
|
||||
Wrote: docs/agents/task-logs/daily/2026-02-14_00-12-00_nip-research-agent_completed.md
|
||||
```
|
||||
|
||||
### Method B (API fallback — for Jules or other restricted environments)
|
||||
### Method C — GitHub API (fallback if git push is also blocked)
|
||||
|
||||
```text
|
||||
$ curl -s "https://api.github.com/repos/PR0M3TH3AN/bitvid/pulls?state=open&per_page=100" | jq '...'
|
||||
{ "count": 12, "prs": [ ... ] }
|
||||
|
||||
$ cat AGENTS.md CLAUDE.md
|
||||
...
|
||||
|
||||
$ 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
|
||||
|
||||
# git push is blocked — use API fallback:
|
||||
# Both Nostr relays and git push failed
|
||||
$ bash scripts/agent/claim-task-api.sh --agent nip-research-agent --cadence daily --base unstable
|
||||
Claiming task: nip-research-agent (daily)
|
||||
Branch: agents/daily/nip-research-agent/2026-02-14-run-1707868800
|
||||
Step 1: Fetching base branch SHA (unstable)...
|
||||
Base SHA: abc123...
|
||||
Step 2: Creating remote branch...
|
||||
Created: refs/heads/agents/daily/nip-research-agent/2026-02-14-run-1707868800
|
||||
Step 3: Creating started.md log file...
|
||||
Created: docs/agents/task-logs/daily/2026-02-14_00-00-00_nip-research-agent_started.md
|
||||
Step 4: Creating draft PR...
|
||||
PR <number>: <url>
|
||||
CLAIM_BRANCH=agents/daily/nip-research-agent/2026-02-14-run-1707868800
|
||||
CLAIM_PR_NUMBER=<number>
|
||||
CLAIM_PR_URL=<url>
|
||||
CLAIM_LOG_FILE=docs/agents/task-logs/daily/2026-02-14_00-00-00_nip-research-agent_started.md
|
||||
|
||||
# Re-run preflight to check for race:
|
||||
$ curl -s "https://api.github.com/repos/PR0M3TH3AN/bitvid/pulls?state=open&per_page=100" | jq '...'
|
||||
{ "count": 13, "prs": [ ... ] }
|
||||
RACE CHECK: won
|
||||
|
||||
$ npm run lint
|
||||
> lint passed
|
||||
|
||||
Wrote: docs/agents/task-logs/daily/2026-02-14_00-12-00_nip-research-agent_completed.md
|
||||
```
|
||||
Reference in new issue
Block a user