claude-skills/engineering/skillopt-sleep/skillopt_sleep
Claude 7eb198ff42
fix(engineering): require --yes for schedule + close mkdir/chmod race (round-10)
A tenth review pass, after confirming all nine prior rounds of fixes
hold up under independent re-reading, found two more low-severity
gaps and offered to accept a follow-up -- fixed both now for
consistency with how every prior round's findings were handled:

1. schedule had no confirmation gate at the CLI layer. The "confirm
   with the user before schedule" safeguard (deviation #15) lived only
   in commands/skillopt-sleep.md's agent-facing instructions --
   cmd_schedule() called scheduler.schedule() directly and installed a
   real crontab entry immediately. Fine for the documented Claude Code
   agent workflow (which confirms in chat first), but anyone invoking
   `python -m skillopt_sleep schedule` directly bypassed it entirely.
   Fixed: schedule now requires --yes; an interactive terminal without
   it gets a [y/N] prompt, a non-interactive one refuses outright
   (exit 2) pointing at --yes. commands/skillopt-sleep.md updated so
   the driving agent passes --yes once it has confirmed with the user
   in chat -- that's what --yes records, not a redundant re-prompt
   that would hang forever with no TTY inside a non-interactive Bash
   tool call.

2. mkdir-then-chmod wasn't atomic in write_staging()/SleepState.save(),
   leaving a brief window where a freshly-created sensitive directory
   sat at the process's default umask. Fixed: the os.makedirs() calls
   creating the state dir, staging leaf dir, and backup dir now pass
   mode=0o700 directly, on top of (not instead of) the existing
   post-creation chmod calls, which still matter for intermediate
   parent dirs and pre-existing directories that mode= doesn't cover.
   The equivalent race for individual files was judged a larger
   rewrite (every open() call site would need os.open() with an
   explicit mode) than this specific low-severity finding warranted --
   documented as a known, narrower residual gap rather than silently
   claimed as fully closed.

Verified: non-interactive schedule without --yes refuses with exit 2,
with --yes it proceeds to the same scheduler.schedule() call as
before; a synthetic run confirms state dir/state.json/staging leaf
still land at 0700/0600/0700 after the mode= change.

Added as README deviations #22-23 and reconciled the count across all
three documents to 23 (6 cosmetic, 17 safety/hardening) across ten
review rounds -- cross-checked with grep.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TX374i2YGrjNV4Yi3AmaKS
2026-07-11 19:43:30 +00:00
..
experiments feat(engineering): vendor SkillOpt-Sleep from microsoft/SkillOpt 2026-07-08 05:42:44 +00:00
__init__.py feat(engineering): vendor SkillOpt-Sleep from microsoft/SkillOpt 2026-07-08 05:42:44 +00:00
__main__.py fix(engineering): require --yes for schedule + close mkdir/chmod race (round-10) 2026-07-11 19:43:30 +00:00
backend.py fix(engineering): close round-3 review findings on vendored skillopt-sleep 2026-07-11 13:36:00 +00:00
budget.py feat(engineering): vendor SkillOpt-Sleep from microsoft/SkillOpt 2026-07-08 05:42:44 +00:00
config.py feat(engineering): vendor SkillOpt-Sleep from microsoft/SkillOpt 2026-07-08 05:42:44 +00:00
consolidate.py feat(engineering): vendor SkillOpt-Sleep from microsoft/SkillOpt 2026-07-08 05:42:44 +00:00
cycle.py fix(engineering): close round-4 review findings on vendored skillopt-sleep 2026-07-11 13:47:56 +00:00
dream.py feat(engineering): vendor SkillOpt-Sleep from microsoft/SkillOpt 2026-07-08 05:42:44 +00:00
gate.py feat(engineering): vendor SkillOpt-Sleep from microsoft/SkillOpt 2026-07-08 05:42:44 +00:00
harvest.py feat(engineering): vendor SkillOpt-Sleep from microsoft/SkillOpt 2026-07-08 05:42:44 +00:00
harvest_codex.py feat(engineering): vendor SkillOpt-Sleep from microsoft/SkillOpt 2026-07-08 05:42:44 +00:00
harvest_sources.py feat(engineering): vendor SkillOpt-Sleep from microsoft/SkillOpt 2026-07-08 05:42:44 +00:00
judges.py feat(engineering): vendor SkillOpt-Sleep from microsoft/SkillOpt 2026-07-08 05:42:44 +00:00
llm_miner.py feat(engineering): vendor SkillOpt-Sleep from microsoft/SkillOpt 2026-07-08 05:42:44 +00:00
memory.py feat(engineering): vendor SkillOpt-Sleep from microsoft/SkillOpt 2026-07-08 05:42:44 +00:00
mine.py feat(engineering): vendor SkillOpt-Sleep from microsoft/SkillOpt 2026-07-08 05:42:44 +00:00
replay.py feat(engineering): vendor SkillOpt-Sleep from microsoft/SkillOpt 2026-07-08 05:42:44 +00:00
rollout.py feat(engineering): vendor SkillOpt-Sleep from microsoft/SkillOpt 2026-07-08 05:42:44 +00:00
scheduler.py fix(engineering): anchor scheduler.py's project marker match (round-9 bug) 2026-07-11 19:32:33 +00:00
slow_update.py feat(engineering): vendor SkillOpt-Sleep from microsoft/SkillOpt 2026-07-08 05:42:44 +00:00
staging.py fix(engineering): require --yes for schedule + close mkdir/chmod race (round-10) 2026-07-11 19:43:30 +00:00
state.py fix(engineering): require --yes for schedule + close mkdir/chmod race (round-10) 2026-07-11 19:43:30 +00:00
tasks_file.py feat(engineering): vendor SkillOpt-Sleep from microsoft/SkillOpt 2026-07-08 05:42:44 +00:00
types.py feat(engineering): vendor SkillOpt-Sleep from microsoft/SkillOpt 2026-07-08 05:42:44 +00:00