hook-protocol
1
总安装量
1
周安装量
#52949
全站排名
安装命令
npx skills add https://github.com/jtapias92672/onedrive_1_1-19-2026-2 --skill hook-protocol
Agent 安装分布
openclaw
1
Skill 文档
Hook Protocol
Git-backed task persistence for reliable multi-agent workflows.
When to Use
- Agents need to survive crashes/restarts
- Work state must persist beyond session
- Multiple agents coordinate on shared tasks
- Audit trail required for all work
Core Concept
A Hook is a git worktree-based persistent storage:
- Work “hangs” on the hook until completed
- Survives agent restarts, crashes, context resets
- Versioned via git (rollback capable)
- Shared across machines via git push/pull
âââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â HOOK â
â Git Worktree: /project/.forge/hooks/worker-1/ â
â ââââââââââââââââââââââââââââââââââââââââââââââââ â
â â task.json - Current assigned work â â
â â context.json - Minimum viable context â â
â â result.json - Output when complete â â
â â evidence/ - Proof of work â â
â ââââââââââââââââââââââââââââââââââââââââââââââââ â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââ
GUPP: The Universal Protocol
Gas Town Universal Polecat Protocol “If there is work on your hook, YOU MUST RUN IT.”
Worker Startup Behavior
async function workerStartup() {
// 1. Check hook for pending work
const hook = await getHook();
if (hook.hasWork()) {
// 2. Work present â EXECUTE immediately
// No announcement, no waiting, no asking permission
await execute(hook.getTask());
} else {
// 3. No work â Wait for dispatch
await waitForMail();
}
}
Why GUPP Matters
Without GUPP:
â Mayor: "Ready to work on task X?"
â Worker: "Sure, whenever you're ready"
â Mayor: "OK, let me know when you start"
â Worker: "Waiting for your signal"
â Infinite politeness loop, no work done
With GUPP:
â
Mayor slings task to hook
â
Worker starts, sees hook has work
â
Worker executes immediately
â
No waiting, no asking, no deadlock
Hook Structure
Directory Layout
.forge/hooks/
âââ translator-1/
â âââ task.json # Assigned work
â âââ context.json # MVC (minimum viable context)
â âââ result.json # Output (when complete)
â âââ status.json # PENDING | IN_PROGRESS | COMPLETE | FAILED
â âââ evidence/
â âââ input-hash.txt
â âââ output-hash.txt
âââ translator-2/
â âââ ...
âââ validator-1/
âââ ...
Task Schema
interface HookTask {
id: string; // "convoy-a1b2.1"
type: string; // "translate" | "validate" | "remediate"
input: {
componentId?: string; // For translator
mpkPath?: string; // For validator
validationReport?: string; // For remediator
};
assigned: string; // ISO timestamp
timeout?: string; // ISO timestamp for deadline
// Coordination
convoyId?: string; // Parent convoy
blockedBy?: string[]; // Dependencies
discoveredFrom?: string; // Parent task if found during work
}
Context Schema (MVC)
interface HookContext {
// ONLY what worker needs for THIS task
// NOT the full conversation history
// NOT knowledge of other workers
task: HookTask;
// Task-specific context
figmaComponent?: FigmaComponent; // For translator
expectedOutput?: Schema; // For validator
previousAttempts?: Attempt[]; // For remediator (max 3)
// Tooling (3-5 max)
tools: string[]; // ["figma-api", "mendix-sdk", "file-system"]
}
Result Schema
interface HookResult {
taskId: string;
status: "COMPLETE" | "FAILED";
output?: {
mpkPath?: string; // Translator output
validationReport?: string; // Validator output
remediationPatch?: string; // Remediator output
};
error?: {
code: string;
message: string;
recoverable: boolean;
};
evidence: {
inputHash: string; // SHA-256 of input
outputHash: string; // SHA-256 of output
timestamp: string; // ISO completion time
};
// Discovered work
discoveredTasks?: NewTask[];
}
Hook Operations
Sling (Assign Work)
// Mayor slings task to worker's hook
async function sling(taskId: string, workerId: string) {
const task = await ledger.getTask(taskId);
const context = await buildMVC(task);
// Write to worker's hook
await writeHook(workerId, {
task,
context,
status: "PENDING"
});
// Git commit for persistence
await git.commit(`Sling ${taskId} to ${workerId}`);
}
Execute (Worker Side)
// Worker checks and executes hook
async function checkHook() {
const hook = await readHook(MY_WORKER_ID);
if (hook.status === "PENDING") {
// Update status
await updateHookStatus("IN_PROGRESS");
// Execute task
try {
const result = await executeTask(hook.task, hook.context);
await writeResult(result);
await updateHookStatus("COMPLETE");
} catch (error) {
await writeError(error);
await updateHookStatus("FAILED");
}
// Git commit result
await git.commit(`Complete ${hook.task.id}`);
// Terminate (fresh worker for next task)
process.exit(0);
}
}
Handoff (Context Reset)
// Worker hands off before context pollution
async function handoff() {
// 1. Commit current work
await git.commit("Handoff checkpoint");
// 2. Write continuation state to hook
await writeHook(MY_WORKER_ID, {
...currentHook,
handoffReason: "context_limit",
resumePoint: currentStep
});
// 3. Terminate session
// New worker will pick up from hook
process.exit(0);
}
Git Integration
Worktree Structure
# Main repo
~/project/
âââ .git/
âââ .forge/
â âââ hooks/ # Hook storage (committed)
âââ src/
# Each worker can use git worktree for isolation
~/project/.git/worktrees/
âââ translator-1/ # Isolated working copy
âââ validator-1/ # Isolated working copy
Persistence Flow
1. Mayor slings task â writes to hook â git commit
2. Worker reads hook â executes â writes result â git commit
3. Mayor reads result â dispatches next â git commit
4. All state persisted, survives any crash
Crash Recovery
// On worker startup after crash
async function recoverFromCrash() {
const hook = await readHook(MY_WORKER_ID);
switch (hook.status) {
case "PENDING":
// Never started, execute fresh
await executeTask(hook.task, hook.context);
break;
case "IN_PROGRESS":
// Was running, check for partial result
if (await hasPartialResult()) {
await resumeTask(hook.task);
} else {
await executeTask(hook.task, hook.context); // Restart
}
break;
case "COMPLETE":
case "FAILED":
// Already done, wait for new work
await waitForMail();
break;
}
}
FORGE Hook Implementation
Directory Structure
.forge/
âââ hooks/
â âââ translator-pool/
â â âââ worker-1/
â â â âââ task.json
â â â âââ context.json
â â â âââ result.json
â â â âââ evidence/
â â âââ worker-2/
â âââ validator-pool/
â â âââ worker-1/
â âââ remediator-pool/
â âââ worker-1/
âââ ledger/
â âââ tasks.jsonl # All tasks (Beads format)
â âââ convoys.jsonl # Convoy definitions
â âââ evidence.jsonl # Evidence packs
âââ config.yaml
Hook CLI (Proposed)
# Check hook status
forge hook status worker-1
# Read hook contents
forge hook show worker-1
# Clear completed hook
forge hook clear worker-1
# List all hooks
forge hook list
# Repair orphaned hooks
forge hook repair
Anti-Patterns
â Work in Memory Only
// Work lost on crash
const currentTask = await getTask();
await execute(currentTask); // Crash here = lost
â Shared Mutable State
// Workers fighting over shared hook
await sharedHook.update({ worker1Result });
await sharedHook.update({ worker2Result }); // Conflict
â Long-Running Sessions
// Context accumulates, work not persisted
while (true) {
const task = await getNextTask();
await execute(task); // Hours of work in volatile memory
}
Correct Pattern
// â
Persistent, isolated, ephemeral
async function workerLifecycle() {
// 1. Read from MY hook (isolated)
const myHook = await readHook(MY_WORKER_ID);
// 2. Execute single task
const result = await execute(myHook.task);
// 3. Write result to MY hook
await writeResult(MY_WORKER_ID, result);
// 4. Git commit (persistent)
await git.commit(`${MY_WORKER_ID}: ${myHook.task.id}`);
// 5. Terminate (ephemeral)
process.exit(0);
// New worker spawns for next task
}
References
- Gas Town Hooks: github.com/steveyegge/gastown
- Beads JSONL: github.com/steveyegge/beads
- FORGE Evidence Packs: Epic 08
- Git Worktrees: git-scm.com/docs/git-worktree