Skip to content

feat(trigger-watcher): add chain field for sequential injection - #27

Merged
JeanBaptisteRenard merged 1 commit into
mainfrom
feat/trigger-chain
May 31, 2026
Merged

feat(trigger-watcher): add chain field for sequential injection#27
JeanBaptisteRenard merged 1 commit into
mainfrom
feat/trigger-chain

Conversation

@JeanBaptisteRenard

@JeanBaptisteRenard JeanBaptisteRenard commented May 31, 2026

Copy link
Copy Markdown
Collaborator

Adds a chain field to the trigger JSON for sequential multi-step injection, mutually exclusive with command. Each step waits for the previous turn to complete (busy-rise → busy-fall) before the next command is injected.

Stack note (REBASED 2026-05-31 21:34 CEST): Originally stacked on #26 (timeout default + per-trigger timeout_ms), this branch has been rebased onto main now that #26 (ffbeb44) and #28 (8746466 — liveness check) are merged. The chain loop now also performs the W7 liveness check before each step's write — defense-in-depth across PRs.

Schema

{
  "sessionId": "...",
  "wait": "idle",
  "chain": [
    {"command": "/compact"},
    {"command": "verify and commit"},
    {"command": "open PR"}
  ],
  "timeout_ms": 600000
}
  • chain is a non-empty array, length ≤ 20
  • Each entry has a required command (same MAX_COMMAND_LEN and forbidden-chars rules as the single-command path)
  • Optional per-step timeout_ms overrides the global for that step only (capped at remaining global deadline)
  • chain and command are mutually exclusive — both present → ok:false, error:"command and chain are mutually exclusive"

Execution semantics

  1. After the initial idle wait (honoring wait field), inject step 0
  2. Wait for _cliBusy to flip true (rise, max 2s — TOCTOU window for instant replies), then false (turn complete) before injecting step N+1
  3. Global timeout_ms is the deadline for the whole chain. If exceeded mid-chain → abort with {ok:false, partial:true, steps_completed:N, error:"chain timeout"}
  4. Per-step timeout_ms bounds only that step's turn-completion wait (independent of remaining global, but capped by it)
  5. On any step failure (timeout, session-exit, write error, dead PTY) → stop, write partial result with steps_completed: N (fully-completed step count, not including the failing one)
  6. No turn-completion wait after the LAST step — result is written immediately after final injection

Result shape

Success:

{
  "ok": true,
  "sessionId": "...",
  "sent_at": "<step 0 sent_at>",
  "steps": [
    {"idx": 0, "command": "/compact", "sent_at": "...", "waited_ms": 0},
    {"idx": 1, "command": "verify and commit", "sent_at": "...", "waited_ms": 1234}
  ],
  "total_waited_ms": 1234
}

Partial failure: ok:false, partial:true, steps_completed:N, error:"...", steps:[...], total_waited_ms:N

Tests

36 trigger-watcher tests pass: 22 existing + 11 chain (CHAIN-1..CHAIN-11) + 3 W7 liveness (from #28).

The makeChainCtx helper now also stubs pid: process.pid and isPtyAlive: () => true so the new W7 liveness checks (pre-flight + per-step in the chain loop) don't break chain tests.

Out-of-scope follow-ups

  • Husky pre-commit hook is not marked executable — chmod +x .husky/pre-commit would re-enable auto-lint+test on commits (separate cleanup PR)
  • Taskfile.yaml test task description says "24 tests" but the suite is now at 36+

@JeanBaptisteRenard
JeanBaptisteRenard changed the base branch from feat/trigger-timeout-field to main May 31, 2026 17:56
Extends the trigger schema with a `chain` field allowing multi-step
sequential PTY injection.  Each step is waited on (busy→true→false
transition) before the next is sent.  The global timeout_ms covers the
entire chain; per-step timeout_ms overrides it for that step's turn wait.

New constants: MAX_CHAIN_LENGTH=20, BUSY_RISE_TIMEOUT_MS=2000.
New helper: waitForTurnComplete() — detects busy rising edge then idle;
handles the TOCTOU case where Claude answers so fast busy=true is never
observed (2 s grace window, then assume instant-reply).

Adds 11 new tests (chain happy path, mutual-exclusion, validation, timeout,
session-exit mid-chain, per-step timeout_ms).  All 33 tests pass.
@JeanBaptisteRenard
JeanBaptisteRenard merged commit 9dd4a66 into main May 31, 2026
7 checks passed
@JeanBaptisteRenard
JeanBaptisteRenard deleted the feat/trigger-chain branch May 31, 2026 21:08
JeanBaptisteRenard added a commit that referenced this pull request Jun 9, 2026
…-27-ipc-xss

security: validate IPC file paths, sanitize viewer HTML, add CSP (port upstream #27)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant