← 2.1.16 Primitive inconclusive · runtime-test

Combo Test: Hook-Triggered Task Creation

Hooks can create tasks by writing JSON to the task directory.

Hooks can create tasks by writing JSON directly to the task directory, but ID collisions with TaskCreate can overwrite tasks if IDs are not coordinated.

How It Works

A hook receives the session_id via JSON stdin, then writes a task file to ~/.claude/tasks/{session_id}/{id}.json with task metadata. The TaskList runtime reads from this directory and picks up externally-created tasks in the same polling cycle. This enables event-driven task creation outside the TaskCreate API: a CI webhook, deployment event, or external workflow can trigger a shell hook that instantiates a task without client library changes.

What the Test Found

The test created a hook that received session_id, wrote task ID 27 to the task directory, and verified TaskList picked it up. The task appeared correctly after fixing JSON formatting issues. However, three problems emerged:

  1. ID collisions: When TaskCreate later assigned ID 27, it overwrote the hook task, silently destroying the first task.
  2. JSON escaping failures: Nested quotes in metadata caused parsing failures when not escaped or processed through jq.
  3. No validation: Malformed JSON was silently ignored by TaskList with no error signal.

The hook received session context correctly and filesystem writes succeeded, validating the core mechanism.

Recommended Mitigation

Hooks should use high ID ranges (1000+) or UUID-derived IDs (hook-$(date +%s)) to avoid collision with TaskCreate's auto-increment counter. JSON metadata must be escaped carefully or generated by jq to prevent quote nesting errors.

Why It Matters

This pattern enables event interposition: external systems (CI, webhooks, alerts) can inject work into a Claude Code session without library changes, providing a system-level alternative to in-process task APIs.

Evidence & receipt
  • file2.1.16/tests/combo-05-hook-task-creation/TEST-RESULTS.md
◇ ed25519 receipt
idprimitive_9012f4b0aea948a8b98e134c
alged25519
pubkey9b87705613b1e2fd064d57fa75a6b679d2856ceafad6b1daa8f982493871b6dd
sigd4038283a46ac12916b549e966ff7a59429dda676a0b72374b56b9e3322757a2a8a9c056ac6fc60b6370f87caca34f1313f45ffb0f952c498d6be1a799bcb508

Signed with an ed25519 key held off the repo. Anyone can verify against the published public key; nobody without the secret key can forge it. Click verify: it recomputes the signature in your browser. The signature proves integrity and authorship of this exact content — not a third-party timestamp or that the underlying claim is objectively true. signedAt is when the @f3/attest pipeline ran, not when the work happened; the evidence refs carry the source dates.

Connected