Skip to content

feat: data-safety fixes, strict parsing, --once, help, and scheduling semantics (0.4.0) - #9

Merged
jkrandom-sudo merged 6 commits into
mainfrom
feat/0.4.0-fixes
Jul 19, 2026
Merged

feat: data-safety fixes, strict parsing, --once, help, and scheduling semantics (0.4.0)#9
jkrandom-sudo merged 6 commits into
mainfrom
feat/0.4.0-fixes

Conversation

@jkrandom-sudo

Copy link
Copy Markdown
Owner

Summary

Data safety (P0)

  • B1: single-leader InstanceLock (atomic mkdir + instanceId ownership + heartbeat + stale takeover) — only one instance fires when several share a tasks.json (case-variant plugin paths, per-command opencode run instances); LoopStore.persist now merge-writes (tombstones + dirtyIds) so concurrent instances can't lose or resurrect tasks; instanceLock: false opts out
  • B3: history.log uses O(1) append + 1MB rotation instead of read-rewrite per fire

Parsing & commands (P1)

  • --all/--jitter stripped from prompts everywhere (B2); surrounding quotes removed (B10)
  • Cron expressions, bare intervals (/loop 5m), malformed intervals (0s/999x) now return explicit errors instead of silently creating Adaptive tasks (B9)
  • Claude Code-style flags --cancel/--list/--status/--pause/--resume/--stop/--stop-all map to subcommands (P-1); new /loop help; runtime errors now in English

Scheduling semantics (P1)

  • B4: TTL by last activity (active tasks never expire); B5: loop_schedule honors configured adaptive bounds; B6: resume re-arms per mode; B7: set_fixed jitters the first cycle; bare /loop (maintenance) runs immediately instead of after 1h; fixed tasks anchor to fire start (no interval drift); B8: maintenance prompt uses delayMs

Features & engineering

  • --once one-shot tasks: fire once, auto-cancel (P-4), also via loop_schedule(once: true)
  • README fully in English + "Migrating from Claude Code" table + upgrade self-check (opencode plugin cache workaround)
  • GitHub Actions CI (Node 18/20/22) + badge

BREAKING CHANGES: scheduling-like input that used to silently create Adaptive tasks (cron syntax, bare/malformed intervals, unknown flags) now errors with a pointer to /loop help.

Test plan

  • npm test — 185/185 (27 new: instance-lock, merge-write, history rotation, parsing/errors/help, TTL/resume/jitter/maintenance/drift, --once)
  • Real opencode E2E (isolated HOME, file:// build, live model): /loop help dialog renders; invalid inputs create no tasks; --once fires once and auto-cancels; bare /loop executes maintenance immediately; restart drops tasks (ephemeral regression); instance-lock acquisition logged

🤖 Generated with Claude Code

jkrandom added 6 commits July 19, 2026 15:07
…tation (B1, B3)

B1: opencode can load the same plugin through case-variant paths and every
opencode run spawns another in-process instance, each firing tasks on its
own ticker over a last-writer-wins tasks.json (duplicate fires, lost tasks).

- New InstanceLock (src/instance-lock.ts): atomic mkdir acquisition keyed
  by instanceId (same-process instances share a pid), utimes heartbeat,
  stale takeover via atomic rename race; only the leader ticker fires.
  instanceLock: false opts out.
- LoopStore.persist now merge-writes: disk tasks untouched by this instance
  are preserved, ids it cancelled are tombstoned, its own dirty tasks win.
  Load-time deletions (ephemeral cleanup, TTL/orphan filter) are tombstoned
  so they cannot be resurrected by the merge.
- logFire uses O(1) appendFileSync with 1MB rotation to history.1.log
  instead of read-rewriting the whole history on every fire (B3).
- --all/--jitter flags are stripped from prompt text everywhere (B2);
  one layer of surrounding quotes is removed before parsing (B10)
- Scheduling-like input is rejected instead of silently becoming an
  Adaptive task: cron expressions (B9), bare intervals like `/loop 5m`,
  and malformed intervals like 0s/999x now return explicit errors
- Claude Code-style flags --cancel/--list/--status/--pause/--resume/
  --stop/--stop-all map to the matching subcommand (P-1); unknown
  leading --flags are rejected. A leading --all also works (`/loop --all
  list`)
- New `/loop help` prints full usage; runtime error messages are now in
  English

BREAKING CHANGE: inputs that used to create an Adaptive task silently
(cron syntax, bare/malformed intervals, unknown flags) now fail with an
error pointing at /loop help.
…ce, drift

- B4: task TTL now expires by last activity (max(createdAt, lastFiredAt))
  so long-lived active loops are not dropped after 7 days
- B5: loop_schedule create honors configured adaptive bounds instead of
  hardcoding 60s/1h
- B6: resume re-arms per mode (fixed/adaptive/maintenance) — no more
  instant catch-up fire from a stale nextDueAt
- B7: set_fixed applies the jitter policy to the first cycle too
- Maintenance (bare /loop) now runs immediately in the current turn,
  then re-arms hourly — previously it did nothing for the first hour
- Fixed tasks re-arm from fire START, not completion: model-turn duration
  no longer inflates the effective interval (wall-clock anchoring)
- B8: maintenance prompt and examples/loop.md.example use delayMs
  (relative) instead of the legacy nextDueAtMs (epoch) guidance
/loop 30s --once <prompt> creates a fixed task that auto-cancels after
its first successful fire — covering "remind me in 30 minutes"-style
requests without simulating them with Adaptive mode. Failed fires keep
the task for the next cycle. Also available as loop_schedule create's
once argument (fixed mode only); list output and the creation receipt
mark one-shot tasks.
- README and commands/loop.md are now fully in English
- New "Migrating from Claude Code" table (flag mapping, --once,
  ephemeral default, active-session semantics, ticker granularity)
- Upgrade self-check: work around opencode's stale plugin package cache
  (~/.cache/opencode/packages is not refreshed by --force)
- commands/loop.md documents help/--once/--jitter/--all and CC flags
- Node 18/20/22: npm ci && npm test; README CI badge
- version 0.4.0 (package-exports assertion kept in sync)
@jkrandom-sudo
jkrandom-sudo merged commit da73483 into main Jul 19, 2026
3 checks passed
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