Skip to content

feat(flow): hold a suspend inhibitor for the run, inside the CLI#256

Merged
eduralph merged 2 commits into
mainfrom
feat/244-flow-keepawake
Jul 5, 2026
Merged

feat(flow): hold a suspend inhibitor for the run, inside the CLI#256
eduralph merged 2 commits into
mainfrom
feat/244-flow-keepawake

Conversation

@eduralph

@eduralph eduralph commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Closes #244.

Problem

A pdca flow — a batch, or a high-difficulty bundle on a strong Do model — can run for hours, unattended. If the host auto-suspends on idle, suspend pauses every process and interrupts the cycle mid-run. Reported from getwyrd/wyrd-pdca, which was adding a local scripts/flow wrapper as a stopgap.

Change — inside the installed pdca command, not make

pdca flow is the single choke point for a long unattended run, so the inhibitor lives inside that command; every path (direct, make, script, detached) is then covered by one implementation.

In src/pdca_harness/cli.py:

  • _suspend_inhibitor_argv(argv, env) — the pure decision (unit-testable, no exec): returns the wrapper argv or None. Skips when already wrapped (PDCA_FLOW_INHIBITED), opted out (--no-inhibit / PDCA_NO_INHIBIT), or no binary is present. Linux → systemd-inhibit --what=idle:sleep --why="pdca flow"; macOS → caffeinate -s.
  • _inhibit_suspend_and_reexec()os.execvpes under the wrapper with PDCA_FLOW_INHIBITED=1; warns once on stderr only when the reason is "no inhibitor found".
  • Invoked at the flow dispatch, only on the real CLI entry (argv is None) so a programmatic main([...]) call (tests/embedding) is never replaced via sys.argv.
  • New --no-inhibit flag on the flow parser.

Advisory by design: inhibits only idle:sleep, never shutdown/handle-*, so an operator can still power off. No make flow target exists, so no Makefile change is needed.

Verification

  • tests/test_flow_inhibit.py (7 new tests) — wraps with systemd-inhibit/caffeinate; returns None when already-inhibited / opted-out (env + flag) / no-binary; prefers systemd-inhibit. make check487 tests pass.
  • Live (real python -m pdca_harness.cli flow --rehearse, with a recorder shadowing systemd-inhibit):
    1. re-execs under systemd-inhibit --what=idle:sleep --why=pdca flow, child has PDCA_FLOW_INHIBITED=1 and does not re-fire (double-wrap guard holds);
    2. --no-inhibit → 0 fires; 3. PDCA_NO_INHIBIT=1 → 0 fires; 4. empty PATH → the one-line keep-awake warning, still runs.

🤖 Generated with Claude Code


Review round 1 (Codex) — addressed in 5dbb5fb

  • python -m pdca_harness.cli flow re-exec was broken (argv[0] = non-executable cli.py) → new _reexec_command rebuilds module/script invocations as sys.executable -m pdca_harness.cli …; console scripts forwarded as-is. Verified live + 2 new tests.
  • macOS caffeinate -s is AC-only → use caffeinate -i -s so the idle assertion holds on battery.

A `pdca flow` — a batch, or a high-difficulty bundle on a strong Do model — can run for
hours unattended; if the host auto-suspends on idle, suspend pauses every process and cuts
the cycle off mid-run. Hold a suspend inhibitor for the command's lifetime from inside the
CLI: on the real `flow` entry, re-exec under systemd-inhibit (Linux) / caffeinate (macOS),
guarded against double-wrap by PDCA_FLOW_INHIBITED, so every invocation path — direct,
detached, screen — is covered by one implementation. Advisory only (idle:sleep, never
shutdown); opt out with --no-inhibit / PDCA_NO_INHIBIT for CI/containers; a one-line notice
when no inhibitor is available.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@eduralph eduralph added this to the Milestone 0.52.0 milestone Jul 5, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 270baf9921

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread template/src/pdca_harness/cli.py
Comment thread template/src/pdca_harness/cli.py Outdated
…attery (#244 review)

Two Codex findings:

1. `python -m pdca_harness.cli flow …` has argv[0] = the cli.py file path; forwarding that
   as the inhibitor's command made systemd-inhibit/caffeinate try to exec a non-executable
   .py file, so the re-exec failed before flow started on hosts with an inhibitor. Add
   _reexec_command: for a module/script invocation (argv[0] ends .py / __main__) rebuild the
   child as `sys.executable -m pdca_harness.cli …`; an installed console script is execable
   and forwarded as-is.

2. macOS `caffeinate -s` is the system-sleep assertion valid only on AC power; the idle
   assertion `-i` is what holds on battery — the common unattended-laptop case. Use
   `caffeinate -i -s` to mirror systemd's idle:sleep.

Tests: module-invocation re-exec + console-script passthrough, and the caffeinate flags.
make check → 487 tests pass. Live: `python -m pdca_harness.cli flow --rehearse` under a
recorder now re-invokes `python3 -m pdca_harness.cli …` and reaches the CLI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@eduralph eduralph merged commit cd1383f into main Jul 5, 2026
3 checks passed
@eduralph eduralph deleted the feat/244-flow-keepawake branch July 5, 2026 13:22
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.

flow: build keep-awake into the installed pdca flow command, not a make target

1 participant