mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-06 12:52:26 +00:00
update
This commit is contained in:
1 parent
40f0d9e3fb
commit
ea7cdc1b7f
15 files changed
-1798
No files matched your search
@@ -1,43 +0,0 @@
|
||||
# TORCH: Task Orchestration via Relay-Coordinated Handoff
|
||||
|
||||
TORCH is a decentralized task-locking protocol for multi-agent software development.
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
# Check active locks
|
||||
node src/nostr-lock.mjs check --cadence daily
|
||||
|
||||
# Claim a task
|
||||
AGENT_PLATFORM=codex node src/nostr-lock.mjs lock --agent docs-agent --cadence daily
|
||||
|
||||
# List all active locks
|
||||
node src/nostr-lock.mjs list
|
||||
```
|
||||
|
||||
## Defaults
|
||||
|
||||
- Kind: `30078` (NIP-33 parameterized replaceable)
|
||||
- Expiration: `NIP-40` via `expiration` tag
|
||||
- TTL: `7200s` (2h)
|
||||
- Namespace: `torch`
|
||||
- Relays:
|
||||
- `wss://relay.damus.io`
|
||||
- `wss://nos.lol`
|
||||
- `wss://relay.primal.net`
|
||||
|
||||
## Environment variables
|
||||
|
||||
- `NOSTR_LOCK_NAMESPACE`
|
||||
- `NOSTR_LOCK_RELAYS`
|
||||
- `NOSTR_LOCK_TTL`
|
||||
- `NOSTR_LOCK_DAILY_ROSTER`
|
||||
- `NOSTR_LOCK_WEEKLY_ROSTER`
|
||||
- `AGENT_PLATFORM`
|
||||
|
||||
## Exit codes
|
||||
|
||||
- `0`: success
|
||||
- `1`: usage error
|
||||
- `2`: relay/network error
|
||||
- `3`: lock denied (already locked or race lost)
|
||||
@@ -1,454 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// TORCH — Task Orchestration via Relay-Coordinated Handoff
|
||||
// Generic Nostr-based task locking for multi-agent development.
|
||||
|
||||
import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure';
|
||||
import { SimplePool } from 'nostr-tools/pool';
|
||||
import { useWebSocketImplementation } from 'nostr-tools/relay';
|
||||
import WebSocket from 'ws';
|
||||
|
||||
useWebSocketImplementation(WebSocket);
|
||||
|
||||
const DEFAULT_RELAYS = [
|
||||
'wss://relay.damus.io',
|
||||
'wss://nos.lol',
|
||||
'wss://relay.primal.net',
|
||||
];
|
||||
|
||||
const DEFAULT_TTL = 7200;
|
||||
const DEFAULT_NAMESPACE = 'torch';
|
||||
const QUERY_TIMEOUT_MS = 15_000;
|
||||
const VALID_CADENCES = new Set(['daily', 'weekly']);
|
||||
|
||||
const DEFAULT_DAILY_ROSTER = [
|
||||
'documentation-agent',
|
||||
'quality-agent',
|
||||
'security-agent',
|
||||
'performance-agent',
|
||||
'refactor-agent',
|
||||
];
|
||||
|
||||
const DEFAULT_WEEKLY_ROSTER = [
|
||||
'bug-reproducer-agent',
|
||||
'integration-agent',
|
||||
'release-agent',
|
||||
'test-coverage-agent',
|
||||
'weekly-synthesis-agent',
|
||||
];
|
||||
|
||||
function getRelays() {
|
||||
const envRelays = process.env.NOSTR_LOCK_RELAYS;
|
||||
if (envRelays) {
|
||||
return envRelays.split(',').map((r) => r.trim()).filter(Boolean);
|
||||
}
|
||||
return DEFAULT_RELAYS;
|
||||
}
|
||||
|
||||
function getNamespace() {
|
||||
const namespace = (process.env.NOSTR_LOCK_NAMESPACE || DEFAULT_NAMESPACE).trim();
|
||||
return namespace || DEFAULT_NAMESPACE;
|
||||
}
|
||||
|
||||
function getTtl() {
|
||||
const envTtl = process.env.NOSTR_LOCK_TTL;
|
||||
if (envTtl) {
|
||||
const parsed = parseInt(envTtl, 10);
|
||||
if (!Number.isNaN(parsed) && parsed > 0) return parsed;
|
||||
}
|
||||
return DEFAULT_TTL;
|
||||
}
|
||||
|
||||
function parseEnvRoster(value) {
|
||||
if (!value) return null;
|
||||
return value
|
||||
.split(',')
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function getRoster(cadence) {
|
||||
const dailyFromEnv = parseEnvRoster(process.env.NOSTR_LOCK_DAILY_ROSTER);
|
||||
const weeklyFromEnv = parseEnvRoster(process.env.NOSTR_LOCK_WEEKLY_ROSTER);
|
||||
|
||||
if (cadence === 'daily') {
|
||||
return dailyFromEnv && dailyFromEnv.length ? dailyFromEnv : DEFAULT_DAILY_ROSTER;
|
||||
}
|
||||
|
||||
return weeklyFromEnv && weeklyFromEnv.length ? weeklyFromEnv : DEFAULT_WEEKLY_ROSTER;
|
||||
}
|
||||
|
||||
function todayDateStr() {
|
||||
return new Date().toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
function nowUnix() {
|
||||
return Math.floor(Date.now() / 1000);
|
||||
}
|
||||
|
||||
function parseLockEvent(event) {
|
||||
const dTag = event.tags.find((t) => t[0] === 'd')?.[1] ?? '';
|
||||
const expTag = event.tags.find((t) => t[0] === 'expiration')?.[1];
|
||||
const expiresAt = expTag ? parseInt(expTag, 10) : null;
|
||||
|
||||
let content = {};
|
||||
try {
|
||||
content = JSON.parse(event.content);
|
||||
} catch {
|
||||
// Ignore malformed JSON content
|
||||
}
|
||||
|
||||
return {
|
||||
eventId: event.id,
|
||||
pubkey: event.pubkey,
|
||||
createdAt: event.created_at,
|
||||
createdAtIso: new Date(event.created_at * 1000).toISOString(),
|
||||
expiresAt,
|
||||
expiresAtIso: expiresAt ? new Date(expiresAt * 1000).toISOString() : null,
|
||||
dTag,
|
||||
agent: content.agent ?? null,
|
||||
cadence: content.cadence ?? null,
|
||||
status: content.status ?? null,
|
||||
date: content.date ?? null,
|
||||
platform: content.platform ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
function filterActiveLocks(locks) {
|
||||
const now = nowUnix();
|
||||
return locks.filter((lock) => !lock.expiresAt || lock.expiresAt > now);
|
||||
}
|
||||
|
||||
async function queryLocks(relays, cadence, dateStr, namespace) {
|
||||
const pool = new SimplePool();
|
||||
const tagFilter = `${namespace}-lock-${cadence}-${dateStr}`;
|
||||
|
||||
try {
|
||||
const events = await Promise.race([
|
||||
pool.querySync(relays, {
|
||||
kinds: [30078],
|
||||
'#t': [tagFilter],
|
||||
}),
|
||||
new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('Relay query timed out')), QUERY_TIMEOUT_MS),
|
||||
),
|
||||
]);
|
||||
|
||||
return filterActiveLocks(events.map(parseLockEvent));
|
||||
} finally {
|
||||
pool.close(relays);
|
||||
}
|
||||
}
|
||||
|
||||
async function publishLock(relays, event) {
|
||||
const pool = new SimplePool();
|
||||
|
||||
try {
|
||||
const publishPromises = pool.publish(relays, event);
|
||||
const results = await Promise.allSettled(publishPromises);
|
||||
const successes = results.filter((r) => r.status === 'fulfilled');
|
||||
|
||||
if (successes.length === 0) {
|
||||
const errors = results.map((r, i) => {
|
||||
if (r.status === 'rejected') {
|
||||
const reason = r.reason;
|
||||
const message = reason instanceof Error ? reason.message : String(reason ?? 'unknown');
|
||||
return `${relays[i]}: ${message}`;
|
||||
}
|
||||
return `${relays[i]}: unknown`;
|
||||
});
|
||||
throw new Error(`Failed to publish to any relay:\n ${errors.join('\n ')}`);
|
||||
}
|
||||
|
||||
console.error(` Published to ${successes.length}/${relays.length} relays`);
|
||||
return event;
|
||||
} finally {
|
||||
pool.close(relays);
|
||||
}
|
||||
}
|
||||
|
||||
async function cmdCheck(cadence) {
|
||||
const relays = getRelays();
|
||||
const namespace = getNamespace();
|
||||
const dateStr = todayDateStr();
|
||||
|
||||
console.error(`Checking locks: namespace=${namespace}, cadence=${cadence}, date=${dateStr}`);
|
||||
console.error(`Relays: ${relays.join(', ')}`);
|
||||
|
||||
const locks = await queryLocks(relays, cadence, dateStr, namespace);
|
||||
const lockedAgents = [...new Set(locks.map((l) => l.agent).filter(Boolean))];
|
||||
const roster = getRoster(cadence);
|
||||
const available = roster.filter((a) => !lockedAgents.includes(a));
|
||||
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
namespace,
|
||||
cadence,
|
||||
date: dateStr,
|
||||
locked: lockedAgents.sort(),
|
||||
available: available.sort(),
|
||||
lockCount: locks.length,
|
||||
locks: locks.map((l) => ({
|
||||
agent: l.agent,
|
||||
eventId: l.eventId,
|
||||
createdAt: l.createdAtIso,
|
||||
expiresAt: l.expiresAtIso,
|
||||
platform: l.platform,
|
||||
})),
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
async function cmdLock(agent, cadence, dryRun = false) {
|
||||
const relays = getRelays();
|
||||
const namespace = getNamespace();
|
||||
const dateStr = todayDateStr();
|
||||
const ttl = getTtl();
|
||||
const now = nowUnix();
|
||||
const expiresAt = now + ttl;
|
||||
|
||||
console.error(`Locking: namespace=${namespace}, agent=${agent}, cadence=${cadence}, date=${dateStr}`);
|
||||
console.error(`TTL: ${ttl}s, expires: ${new Date(expiresAt * 1000).toISOString()}`);
|
||||
console.error(`Relays: ${relays.join(', ')}`);
|
||||
|
||||
console.error('Step 1: Checking for existing locks...');
|
||||
const existingLocks = await queryLocks(relays, cadence, dateStr, namespace);
|
||||
const conflicting = existingLocks.filter((l) => l.agent === agent);
|
||||
|
||||
if (conflicting.length > 0) {
|
||||
const earliest = conflicting.sort((a, b) => a.createdAt - b.createdAt)[0];
|
||||
console.error(
|
||||
`LOCK DENIED: ${agent} already locked by event ${earliest.eventId} ` +
|
||||
`(created ${earliest.createdAtIso}, platform: ${earliest.platform})`,
|
||||
);
|
||||
console.log('LOCK_STATUS=denied');
|
||||
console.log('LOCK_REASON=already_locked');
|
||||
console.log(`LOCK_EXISTING_EVENT=${earliest.eventId}`);
|
||||
process.exit(3);
|
||||
}
|
||||
|
||||
console.error('Step 2: Generating ephemeral keypair...');
|
||||
const sk = generateSecretKey();
|
||||
const pk = getPublicKey(sk);
|
||||
console.error(` Ephemeral pubkey: ${pk.slice(0, 16)}...`);
|
||||
|
||||
console.error('Step 3: Building lock event...');
|
||||
const event = finalizeEvent(
|
||||
{
|
||||
kind: 30078,
|
||||
created_at: now,
|
||||
tags: [
|
||||
['d', `${namespace}-lock/${cadence}/${agent}/${dateStr}`],
|
||||
['t', `${namespace}-agent-lock`],
|
||||
['t', `${namespace}-lock-${cadence}`],
|
||||
['t', `${namespace}-lock-${cadence}-${dateStr}`],
|
||||
['expiration', String(expiresAt)],
|
||||
],
|
||||
content: JSON.stringify({
|
||||
agent,
|
||||
cadence,
|
||||
status: 'started',
|
||||
namespace,
|
||||
date: dateStr,
|
||||
platform: process.env.AGENT_PLATFORM || 'unknown',
|
||||
lockedAt: new Date(now * 1000).toISOString(),
|
||||
expiresAt: new Date(expiresAt * 1000).toISOString(),
|
||||
}),
|
||||
},
|
||||
sk,
|
||||
);
|
||||
|
||||
console.error(` Event ID: ${event.id}`);
|
||||
|
||||
if (dryRun) {
|
||||
console.error('Step 4: [DRY RUN] Skipping publish — event built but not sent');
|
||||
console.error('RACE CHECK: won (dry run — no real contention possible)');
|
||||
} else {
|
||||
console.error('Step 4: Publishing to relays...');
|
||||
await publishLock(relays, event);
|
||||
|
||||
console.error('Step 5: Race check...');
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500));
|
||||
|
||||
const postLocks = await queryLocks(relays, cadence, dateStr, namespace);
|
||||
const racingLocks = postLocks
|
||||
.filter((l) => l.agent === agent)
|
||||
.sort((a, b) => a.createdAt - b.createdAt);
|
||||
|
||||
if (racingLocks.length > 1 && racingLocks[0].eventId !== event.id) {
|
||||
const winner = racingLocks[0];
|
||||
console.error(
|
||||
`RACE CHECK: lost (earlier lock by event ${winner.eventId}, created ${winner.createdAtIso})`,
|
||||
);
|
||||
console.log('LOCK_STATUS=race_lost');
|
||||
console.log('LOCK_REASON=earlier_claim_exists');
|
||||
console.log(`LOCK_WINNER_EVENT=${winner.eventId}`);
|
||||
process.exit(3);
|
||||
}
|
||||
|
||||
console.error('RACE CHECK: won');
|
||||
}
|
||||
|
||||
console.log('LOCK_STATUS=ok');
|
||||
console.log(`LOCK_EVENT_ID=${event.id}`);
|
||||
console.log(`LOCK_PUBKEY=${pk}`);
|
||||
console.log(`LOCK_AGENT=${agent}`);
|
||||
console.log(`LOCK_CADENCE=${cadence}`);
|
||||
console.log(`LOCK_DATE=${dateStr}`);
|
||||
console.log(`LOCK_EXPIRES=${expiresAt}`);
|
||||
console.log(`LOCK_EXPIRES_ISO=${new Date(expiresAt * 1000).toISOString()}`);
|
||||
}
|
||||
|
||||
async function cmdList(cadence) {
|
||||
const relays = getRelays();
|
||||
const namespace = getNamespace();
|
||||
const dateStr = todayDateStr();
|
||||
const cadences = cadence ? [cadence] : ['daily', 'weekly'];
|
||||
|
||||
console.error(`Listing active locks: namespace=${namespace}, cadences=${cadences.join(', ')}`);
|
||||
|
||||
for (const c of cadences) {
|
||||
const locks = await queryLocks(relays, c, dateStr, namespace);
|
||||
|
||||
console.log(`\n${'='.repeat(72)}`);
|
||||
console.log(`Active ${namespace} ${c} locks (${dateStr})`);
|
||||
console.log('='.repeat(72));
|
||||
|
||||
if (locks.length === 0) {
|
||||
console.log(' (no active locks)');
|
||||
continue;
|
||||
}
|
||||
|
||||
const sorted = locks.sort((a, b) => a.createdAt - b.createdAt);
|
||||
for (const lock of sorted) {
|
||||
const age = nowUnix() - lock.createdAt;
|
||||
const ageMin = Math.round(age / 60);
|
||||
const remaining = lock.expiresAt ? lock.expiresAt - nowUnix() : null;
|
||||
const remainMin = remaining ? Math.round(remaining / 60) : '?';
|
||||
|
||||
console.log(
|
||||
` ${(lock.agent ?? 'unknown').padEnd(30)} ` +
|
||||
`age: ${String(ageMin).padStart(4)}m ` +
|
||||
`ttl: ${String(remainMin).padStart(4)}m ` +
|
||||
`platform: ${lock.platform ?? '?'} ` +
|
||||
`event: ${lock.eventId?.slice(0, 12)}...`,
|
||||
);
|
||||
}
|
||||
|
||||
const roster = getRoster(c);
|
||||
const lockedAgents = new Set(locks.map((l) => l.agent).filter(Boolean));
|
||||
const available = roster.filter((a) => !lockedAgents.has(a));
|
||||
|
||||
console.log(`\n Locked: ${lockedAgents.size}/${roster.length}`);
|
||||
console.log(` Available: ${available.join(', ') || '(none)'}`);
|
||||
}
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
const args = { command: null, agent: null, cadence: null, dryRun: false };
|
||||
let i = 0;
|
||||
|
||||
if (argv.length > 0 && !argv[0].startsWith('-')) {
|
||||
args.command = argv[0];
|
||||
i = 1;
|
||||
}
|
||||
|
||||
for (; i < argv.length; i++) {
|
||||
const arg = argv[i];
|
||||
if (arg === '--agent' || arg === '-a') {
|
||||
args.agent = argv[++i];
|
||||
} else if (arg === '--cadence' || arg === '-c') {
|
||||
args.cadence = argv[++i];
|
||||
} else if (arg.startsWith('--agent=')) {
|
||||
args.agent = arg.split('=')[1];
|
||||
} else if (arg.startsWith('--cadence=')) {
|
||||
args.cadence = arg.split('=')[1];
|
||||
} else if (arg === '--dry-run') {
|
||||
args.dryRun = true;
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
function usage() {
|
||||
console.error(`Usage: node src/nostr-lock.mjs <command> [options]
|
||||
|
||||
Commands:
|
||||
check --cadence <daily|weekly> Check locked agents (JSON)
|
||||
lock --agent <name> --cadence <daily|weekly> Claim a lock
|
||||
list [--cadence <daily|weekly>] Print active lock table
|
||||
|
||||
Options:
|
||||
--dry-run Build and sign the event but do not publish
|
||||
|
||||
Environment:
|
||||
NOSTR_LOCK_NAMESPACE Namespace prefix for lock tags (default: torch)
|
||||
NOSTR_LOCK_RELAYS Comma-separated relay WSS URLs
|
||||
NOSTR_LOCK_TTL Lock TTL in seconds (default: 7200)
|
||||
NOSTR_LOCK_DAILY_ROSTER Comma-separated daily roster (optional)
|
||||
NOSTR_LOCK_WEEKLY_ROSTER Comma-separated weekly roster (optional)
|
||||
AGENT_PLATFORM Platform identifier (e.g., codex)
|
||||
|
||||
Exit codes:
|
||||
0 Success
|
||||
1 Usage error
|
||||
2 Relay/network error
|
||||
3 Lock denied (already locked or race lost)`);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = parseArgs(process.argv.slice(2));
|
||||
|
||||
if (!args.command) {
|
||||
usage();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
switch (args.command) {
|
||||
case 'check': {
|
||||
if (!args.cadence || !VALID_CADENCES.has(args.cadence)) {
|
||||
console.error('ERROR: --cadence <daily|weekly> is required for check');
|
||||
process.exit(1);
|
||||
}
|
||||
await cmdCheck(args.cadence);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'lock': {
|
||||
if (!args.agent) {
|
||||
console.error('ERROR: --agent <name> is required for lock');
|
||||
process.exit(1);
|
||||
}
|
||||
if (!args.cadence || !VALID_CADENCES.has(args.cadence)) {
|
||||
console.error('ERROR: --cadence <daily|weekly> is required for lock');
|
||||
process.exit(1);
|
||||
}
|
||||
await cmdLock(args.agent, args.cadence, args.dryRun);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'list': {
|
||||
if (args.cadence && !VALID_CADENCES.has(args.cadence)) {
|
||||
console.error('ERROR: --cadence must be daily or weekly');
|
||||
process.exit(1);
|
||||
}
|
||||
await cmdList(args.cadence || null);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
console.error(`ERROR: Unknown command: ${args.command}`);
|
||||
usage();
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(`nostr-lock failed: ${err.message}`);
|
||||
process.exit(2);
|
||||
});
|
||||
@@ -1,71 +0,0 @@
|
||||
# Agent Scheduler Meta Prompts (Generic)
|
||||
|
||||
Copy one block below into the scheduler agent session.
|
||||
|
||||
---
|
||||
|
||||
## Daily Scheduler Meta Prompt
|
||||
|
||||
```text
|
||||
You are the daily agent scheduler for this repository.
|
||||
|
||||
Follow `src/prompts/scheduler-flow.md` exactly.
|
||||
|
||||
MUST 1: Set cadence config to:
|
||||
- cadence = daily
|
||||
- log_dir = task-logs/daily/
|
||||
- branch_prefix = agents/daily/
|
||||
- prompt_dir = src/prompts/daily/
|
||||
|
||||
MUST 2: Run preflight to get the exclusion set:
|
||||
|
||||
node src/nostr-lock.mjs check --cadence daily
|
||||
|
||||
Use the `locked` array from the JSON output as the exclusion set.
|
||||
|
||||
MUST 3: Run these commands in this order:
|
||||
1) cat AGENTS.md
|
||||
2) ls -1 task-logs/daily/ | sort | tail -n 1
|
||||
3) Select next roster agent not in exclusion set
|
||||
4) Claim via Nostr lock:
|
||||
AGENT_PLATFORM=<platform> node src/nostr-lock.mjs lock --agent <agent-name> --cadence daily
|
||||
Exit 0 = lock acquired, proceed. Exit 3 = race lost, go back to step 2.
|
||||
5) Execute selected prompt from src/prompts/daily/
|
||||
6) Run repository checks (for example: npm run lint)
|
||||
7) Create `_completed.md` or `_failed.md`, commit, push
|
||||
|
||||
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
|
||||
|
||||
```text
|
||||
You are the weekly agent scheduler for this repository.
|
||||
|
||||
Follow `src/prompts/scheduler-flow.md` exactly.
|
||||
|
||||
MUST 1: Set cadence config to:
|
||||
- cadence = weekly
|
||||
- log_dir = task-logs/weekly/
|
||||
- branch_prefix = agents/weekly/
|
||||
- prompt_dir = src/prompts/weekly/
|
||||
|
||||
MUST 2: Run preflight to get the exclusion set:
|
||||
|
||||
node src/nostr-lock.mjs check --cadence weekly
|
||||
|
||||
Use the `locked` array from the JSON output as the exclusion set.
|
||||
|
||||
MUST 3: Run these commands in this order:
|
||||
1) cat AGENTS.md
|
||||
2) ls -1 task-logs/weekly/ | sort | tail -n 1
|
||||
3) Select next roster agent not in exclusion set
|
||||
4) Claim via Nostr lock:
|
||||
AGENT_PLATFORM=<platform> node src/nostr-lock.mjs lock --agent <agent-name> --cadence weekly
|
||||
Exit 0 = lock acquired, proceed. Exit 3 = race lost, go back to step 2.
|
||||
5) Execute selected prompt from src/prompts/weekly/
|
||||
6) Run repository checks (for example: npm run lint)
|
||||
7) Create `_completed.md` or `_failed.md`, commit, push
|
||||
|
||||
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`.
|
||||
```
|
||||
@@ -1,22 +0,0 @@
|
||||
# Daily Agent Scheduler Prompt (Generic)
|
||||
|
||||
Use `src/prompts/scheduler-flow.md` as the authoritative scheduler procedure.
|
||||
|
||||
## Daily Cadence Configuration
|
||||
|
||||
- `cadence`: `daily`
|
||||
- `log_dir`: `task-logs/daily/`
|
||||
- `branch_prefix`: `agents/daily/`
|
||||
- `prompt_dir`: `src/prompts/daily/`
|
||||
|
||||
## Example Daily Roster
|
||||
|
||||
| # | Agent Name | Prompt File |
|
||||
|---|------------|-------------|
|
||||
| 1 | documentation-agent | `documentation-agent.md` |
|
||||
| 2 | quality-agent | `quality-agent.md` |
|
||||
| 3 | security-agent | `security-agent.md` |
|
||||
| 4 | performance-agent | `performance-agent.md` |
|
||||
| 5 | refactor-agent | `refactor-agent.md` |
|
||||
|
||||
Replace this roster with your project-specific task agents.
|
||||
@@ -1,47 +0,0 @@
|
||||
# Scheduler Flow (Single Source of Truth)
|
||||
|
||||
Use this document for all scheduler runs.
|
||||
|
||||
## Numbered MUST Procedure
|
||||
|
||||
1. Set cadence variables before any command:
|
||||
- `cadence` = `daily` or `weekly`
|
||||
- `log_dir` = `task-logs/<cadence>/`
|
||||
- `branch_prefix` = `agents/<cadence>/`
|
||||
- `prompt_dir` = `src/prompts/<cadence>/`
|
||||
|
||||
2. Run preflight to build the exclusion set:
|
||||
|
||||
```bash
|
||||
node src/nostr-lock.mjs check --cadence <cadence>
|
||||
```
|
||||
|
||||
3. Read policy file(s):
|
||||
|
||||
```bash
|
||||
cat AGENTS.md
|
||||
```
|
||||
|
||||
4. Find latest cadence log file, then choose next roster agent not in exclusion set:
|
||||
|
||||
```bash
|
||||
ls -1 <log_dir> | sort | tail -n 1
|
||||
```
|
||||
|
||||
5. If every roster agent is excluded, write a `_failed.md` log with:
|
||||
`All roster tasks currently claimed by other agents` and stop.
|
||||
|
||||
6. Claim selected agent:
|
||||
|
||||
```bash
|
||||
AGENT_PLATFORM=<platform> \
|
||||
node src/nostr-lock.mjs lock --agent <agent-name> --cadence <cadence>
|
||||
```
|
||||
|
||||
- Exit `0`: lock acquired, continue.
|
||||
- Exit `3`: race lost/already locked, return to step 2.
|
||||
- Exit `2`: relay error, write `_failed.md` with reason `Nostr relay error`, stop.
|
||||
|
||||
7. Execute `<prompt_dir>/<prompt-file>` end-to-end.
|
||||
|
||||
8. Create exactly one final status file (`_completed.md` or `_failed.md`), run repo checks, commit, and push.
|
||||
@@ -1,22 +0,0 @@
|
||||
# Weekly Agent Scheduler Prompt (Generic)
|
||||
|
||||
Use `src/prompts/scheduler-flow.md` as the authoritative scheduler procedure.
|
||||
|
||||
## Weekly Cadence Configuration
|
||||
|
||||
- `cadence`: `weekly`
|
||||
- `log_dir`: `task-logs/weekly/`
|
||||
- `branch_prefix`: `agents/weekly/`
|
||||
- `prompt_dir`: `src/prompts/weekly/`
|
||||
|
||||
## Example Weekly Roster
|
||||
|
||||
| # | Agent Name | Prompt File |
|
||||
|---|------------|-------------|
|
||||
| 1 | bug-reproducer-agent | `bug-reproducer-agent.md` |
|
||||
| 2 | integration-agent | `integration-agent.md` |
|
||||
| 3 | release-agent | `release-agent.md` |
|
||||
| 4 | test-coverage-agent | `test-coverage-agent.md` |
|
||||
| 5 | weekly-synthesis-agent | `weekly-synthesis-agent.md` |
|
||||
|
||||
Replace this roster with your project-specific task agents.
|
||||
Reference in new issue
Block a user