feat: data-safety fixes, strict parsing, --once, help, and scheduling semantics (0.4.0) - #9
Merged
Merged
Conversation
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Data safety (P0)
InstanceLock(atomic mkdir + instanceId ownership + heartbeat + stale takeover) — only one instance fires when several share atasks.json(case-variant plugin paths, per-commandopencode runinstances);LoopStore.persistnow merge-writes (tombstones + dirtyIds) so concurrent instances can't lose or resurrect tasks;instanceLock: falseopts outhistory.loguses O(1) append + 1MB rotation instead of read-rewrite per fireParsing & commands (P1)
--all/--jitterstripped from prompts everywhere (B2); surrounding quotes removed (B10)/loop 5m), malformed intervals (0s/999x) now return explicit errors instead of silently creating Adaptive tasks (B9)--cancel/--list/--status/--pause/--resume/--stop/--stop-allmap to subcommands (P-1); new/loop help; runtime errors now in EnglishScheduling semantics (P1)
loop_schedulehonors configured adaptive bounds; B6: resume re-arms per mode; B7:set_fixedjitters the first cycle; bare/loop(maintenance) runs immediately instead of after 1h; fixed tasks anchor to fire start (no interval drift); B8: maintenance prompt usesdelayMsFeatures & engineering
--onceone-shot tasks: fire once, auto-cancel (P-4), also vialoop_schedule(once: true)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)file://build, live model):/loop helpdialog renders; invalid inputs create no tasks;--oncefires once and auto-cancels; bare/loopexecutes maintenance immediately; restart drops tasks (ephemeral regression); instance-lock acquisition logged🤖 Generated with Claude Code