Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 42 additions & 54 deletions .cursor/bin/riven-loop-tick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ function nowIso(): string {
}

function log(message: string): void {
appendFileSync(join(logDir, "runner.log"), `${nowIso()} ${message}\n`);
appendFileSync(join(logDir, "runner.log"), `${nowIso()} ${message}
`);
}

function run(command: string, args: string[], timeoutMs: number): { status: number; stdout: string; stderr: string } {
Expand All @@ -54,13 +55,17 @@ function run(command: string, args: string[], timeoutMs: number): { status: numb
}

function lines(text: string): string[] {
return text.split(/\r?\n/).map(l => l.trim()).filter(l => l.length > 0);
return text.split(/?
/).map(l => l.trim()).filter(l => l.length > 0);
}

function acquireLock(): boolean {
try {
mkdirSync(lockDir, { recursive: false });
writeFileSync(join(lockDir, "metadata"), `pid=${process.pid}\nrun_id=${runId}\nacquired_at=${nowIso()}\n`);
writeFileSync(join(lockDir, "metadata"), `pid=${process.pid}
run_id=${runId}
acquired_at=${nowIso()}
`);
return true;
} catch {
try {
Expand All @@ -77,7 +82,10 @@ function acquireLock(): boolean {
}
rmSync(lockDir, { recursive: true, force: true });
mkdirSync(lockDir, { recursive: false });
writeFileSync(join(lockDir, "metadata"), `pid=${process.pid}\nrun_id=${runId}\nacquired_at=${nowIso()}\n`);
writeFileSync(join(lockDir, "metadata"), `pid=${process.pid}
run_id=${runId}
acquired_at=${nowIso()}
`);
return true;
} catch { return false; }
}
Expand All @@ -97,7 +105,8 @@ function readBroadcasts(): void {
const path = join(broadcastDir, peer);
if (existsSync(path)) {
const content = readFileSync(path, "utf8").trim();
if (content) log(`broadcast from ${peer.replace(".md", "")}: ${content.split("\n")[0] ?? "(empty)"}`);
if (content) log(`broadcast from ${peer.replace(".md", "")}: ${content.split("
")[0] ?? "(empty)"}`);
}
}
}
Expand All @@ -109,7 +118,8 @@ function writeBroadcast(summary: string): void {
"",
"## Background tick status",
summary,
].join("\n"));
].join("
"));
}

function gh(...args: string[]): { status: number; stdout: string } {
Expand Down Expand Up @@ -146,7 +156,8 @@ function forwardTick(): void {
return;
}

const prNumbers = prsResult.stdout.trim().split("\n").filter(n => n.trim()).map(Number);
const prNumbers = prsResult.stdout.trim().split("
").filter(n => n.trim()).map(Number);
for (const pr of prNumbers) {
const gateResult = gh(
"pr", "view", String(pr), "--repo", "Lucent-Financial-Group/Zeta",
Expand Down Expand Up @@ -198,59 +209,31 @@ function heartbeat(): void {
const elapsed = Date.now() - lastTime;

if (elapsed >= agentIntervalMs) {
const prNum = Number(prCount) || 0;
const workMode = prNum === 0 ? "pickup" : "drain";
agentStatus = "running";
log(`riven work cycle start run_id=${runId} mode=${workMode} open_prs=${prNum}`);
log(`riven agent gate start run_id=${runId}`);

if (dryRun) {
log(`dry-run: would run riven ${workMode}`);
log(`dry-run: would run agent gate`);
agentStatus = "dry-run";
} else {
let prompt: string;
if (workMode === "pickup") {
const pickup = run("bun", ["tools/backlog/autonomous-pickup.ts", "--json"], 30_000);
let executionPrompt = "";
try {
const selection = JSON.parse(pickup.stdout);
executionPrompt = selection.executionPrompt ?? "";
log(`pickup selected: ${selection.selected?.id ?? "none"} action=${selection.action ?? "none"}`);
} catch { log(`pickup parse error: ${pickup.stderr.slice(0, 200)}`); }

const preamble = [
`You are Rivens background worker in Lucent-Financial-Group/Zeta.`,
`BEFORE ANY WORK: 1) Read CLAUDE.md and AGENTS.md for repo conventions.`,
`2) Run "bun tools/github/refresh-worldview.ts" to get current state.`,
`3) Read active trajectories at docs/trajectories/*/RESUME.md.`,
`4) Build gate: "dotnet build -c Release" must end with 0 warnings 0 errors.`,
`KEY RULES: TS over bash (Rule 0). Prefer F#/TS code over docs.`,
`Always re-decompose items during the build — assume decomposition has mistakes.`,
].join(" ");

prompt = executionPrompt.length > 0
? `${preamble} YOUR TASK:\n${executionPrompt}`
: `${preamble} No backlog items available. Run refresh-worldview, check for stale classifications, fix them, open a PR.`;
} else {
prompt = [
`You are Rivens background worker in Lucent-Financial-Group/Zeta.`,
`Read CLAUDE.md first. Run "bun tools/github/refresh-worldview.ts".`,
`Build gate: "dotnet build -c Release" (0 warnings).`,
`TASK: ${prNum} open PRs. Run "bun tools/github/poll-pr-gate-batch.ts --all-open".`,
`For any PR where gate=BLOCKED and nextAction=resolve-threads:`,
`check out branch, read review comments, fix code issues, push,`,
`reply to threads, resolve via GraphQL, arm auto-merge`,
`(gh pr merge NUMBER --auto --squash). Own your PRs through merge.`,
].join(" ");
}

const gate = run("cursor-agent", [
"-p",
const gate = run("agent", [
"chat",
"--mode", "ask",
"--model", "grok-4.3",
prompt,
[
"You are Riven, trajectory manager and adversarial-truth-axis reviewer.",
"This is an autonomous 15-minute cycle.",
"Read broadcasts first from ~/.local/share/zeta-broadcasts/{otto,vera,lior,riven}.md.",
"Walk assigned trajectories. Decompose only what you hit mid-stride.",
"Produce at least one concrete, actionable claim or small PR scope.",
"When blocked, create a specific research child the next pickup cannot dodge.",
"Write your status to ~/.local/share/zeta-broadcasts/riven.md at the end.",
"GitHub PR state and actual file contents are authoritative.",
].join(" "),
], agentTimeoutMs);

agentStatus = gate.status === 0 ? "ok" : `exit-${gate.status}`;
log(`riven work cycle end run_id=${runId} mode=${workMode} status=${gate.status}`);
log(`riven agent gate end run_id=${runId} status=${gate.status}`);

writeFileSync(agentStateFile, JSON.stringify({
run_id: runId,
Expand All @@ -260,10 +243,16 @@ function heartbeat(): void {
}, null, 2));

if (gate.stdout.trim().length > 0) {
appendFileSync(join(logDir, "ticks.log"), `\n--- ${runId} riven gate ---\n${gate.stdout}\n`);
appendFileSync(join(logDir, "ticks.log"), `
--- ${runId} riven gate ---
${gate.stdout}
`);
}
if (gate.stderr.trim().length > 0) {
appendFileSync(join(logDir, "ticks.err"), `\n--- ${runId} riven gate ---\n${gate.stderr}\n`);
appendFileSync(join(logDir, "ticks.err"), `
--- ${runId} riven gate ---
${gate.stderr}
`);
}
}
} else {
Expand Down Expand Up @@ -316,4 +305,3 @@ try {
} finally {
releaseLock();
}

Expand Down
Loading