Skip to content

feat: /toggle-review command for pi-hard-no runtime on/off#119

Open
royosherove wants to merge 3 commits into
mainfrom
feat/toggle-code-review
Open

feat: /toggle-review command for pi-hard-no runtime on/off#119
royosherove wants to merge 3 commits into
mainfrom
feat/toggle-code-review

Conversation

@royosherove
Copy link
Copy Markdown
Member

Summary

New slash command (/toggle-review, alias /toggle-code-review) that flips pi-hard-no's auto code review on/off persistently and without a session restart.

Pairs with pi-hard-no PR #7 which adds the runtime re-read mechanism.

How it works

  1. User sends /toggle-review in Telegram
  2. Gateway resolves the effective settings file: <agentCwd>/.hardno/settings.json if present, else ~/.pi/.hardno/settings.json (matches pi-hard-no's read order)
  3. Atomic flip of enabled boolean, preserving all other fields
  4. pi-hard-no (v1.3.0+) re-reads the field at the next agent_end \u2014 effect on the next agent turn, no restart

Safety

  • Atomic write \u2014 tmp + rename, orphan tmp cleanup on failure
  • Race-safe \u2014 mtime check + retry (up to 3x) if pi-hard-no's own Alt+R toggle writes concurrently
  • Preserves other fields \u2014 re-reads existing file, merges enabled, re-writes
  • Auth \u2014 standard allowlist check (matches /verbose, /stop)
  • UI feedback \u2014 reply indicates which file was written ("project-local" vs "global") so the user knows exactly what happened

Dispatch placement

Wired into handleOrAbort (not the command registry) so it works mid-turn, matching /verbose and /stop semantics. Registry dispatch is serialized behind the thread queue; toggle should be immediate.

Review history

2 review cycles + architect pass. Findings addressed:

  • Loop 1: lost-update race, atomic-write safety, local-vs-global routing bug (toggle silently ineffective when local settings file existed)
  • Architect: README missed the new command \u2014 added commands table row + details section

Tests

  • 21 new tests in code-review-toggle.test.ts (was 15 baseline)
  • 477 total green
  • New coverage: resolveSettingsPath with/without cwd, toggle routing (local present/absent), end-to-end (toggle writes local \u2192 read picks it up, no masking)

API notes

Slight breaking change to module internals (not public):

  • toggleEnabled(opts: ToggleOptions) \u2014 was toggleEnabled(home?: string)
  • readEnabled(opts: ToggleOptions) \u2014 was readEnabled(home?: string)
  • Adds ToggleResult.wroteLocal: boolean for UX clarity

Requires

  • pi-hard-no \u2265 1.3.0 for runtime effect (v1.3.0 unreleased \u2014 lives in PR fix: mock child_process in bundle tests for CI #7 on that repo). Command works even without the extension installed \u2014 just writes a JSON file nobody reads, no crash.

Loki FastStart added 3 commits May 11, 2026 14:20
New slash command (alias: /toggle-code-review) that flips
~/.pi/.hardno/settings.json `enabled` boolean. Takes effect on the
NEXT agent turn \u2014 no session restart needed \u2014 because pi-hard-no v1.3.0+
re-reads that field at each agent_end.

Implementation:
- src/gateway/code-review-toggle.ts
  * resolveSettingsPath() / readEnabled() / toggleEnabled()
  * Atomic write (tmp + rename), preserves other keys in the file
  * Treats missing file / missing enabled key as default `true`
  * Recovers from malformed JSON by overwriting
- src/gateway/commands.ts: handleToggleReview() posts confirmation
- src/gateway/gateway.ts: wired into handleOrAbort (snappy path,
  not thread-queue-gated, matches /verbose)
- src/gateway/code-review-toggle.test.ts: 15 unit tests

Command depends on pi-hard-no \u2265 1.3.0 being installed for runtime
effect; the write itself always succeeds (plain JSON), so the command
is safe even if the extension isn't loaded \u2014 just a no-op at the
extension end.

Tests: 15 new / 471 total green. Typecheck clean.
F6 (Medium): toggle now respects pi-hard-no's local-vs-global file
  precedence. New ToggleOptions { cwd?, home? } \u2014 when cwd is provided
  and <cwd>/.hardno/settings.json exists, writes there instead of global.
  Gateway passes agent.getInfo().cwd so the toggle hits the same file
  pi-hard-no will read on the next agent_end. Previously: roundhouse
  wrote global, pi-hard-no read local (enabled still true), toggle
  silently ineffective.

F1 Codex (High) / F2 reviewer (Medium): lost-update race on concurrent
  writers. toggleEnabled() now captures mtime at read and retries (up
  to 3x) if another writer (e.g. pi-hard-no's own Alt+R handler) updates
  the file between our read and rename. Also cleans up orphan tmp files
  on any write/rename failure.

API surface changes:
- resolveSettingsPath(opts) now returns { path, isLocal } instead of a bare string
- resolveGlobalSettingsPath() kept for the case where cwd isn't known
- toggleEnabled(opts) signature: ToggleOptions object (was home: string)
- readEnabled(opts) signature: ToggleOptions object (was home: string)
- ToggleResult.wroteLocal: boolean \u2014 surfaced to handler for UX clarity
  ("wrote to project-local config" vs "global")

User-facing: handleToggleReview now reports scope + path:
  "Wrote to project-local config (~/repos/x/.hardno/settings.json).
  Takes effect on the next agent turn \u2014 no restart needed."

Tests: 21 new (was 15) / 477 total green. New coverage:
- resolveSettingsPath with/without cwd, with/without local file
- toggleEnabled routing: local present/absent, global fallback
- end-to-end: toggle writes local, read picks it up (F6 regression)
Architect review on pi-hard-no flagged missing docs for the new toggle;
same rationale applies here \u2014 it's a user-facing slash command that
users won't discover without README mention.

- Added row to the Telegram bot commands table (+ alias)
- Added details section explaining persistence + routing semantics
Copy link
Copy Markdown

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

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: 41be16c7bb

ℹ️ 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".


// Race check: if another writer updated the file between our read and
// our rename, discard this tmp and retry so we don't clobber their edits.
if (readMtime !== null && attempt < MAX_WRITE_ATTEMPTS) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve race check on the last write attempt

The mtime conflict guard is skipped on the final retry because of attempt < MAX_WRITE_ATTEMPTS, so a concurrent writer that updates settings.json between our read and rename on attempt 3 will be silently overwritten. This reintroduces lost updates exactly under the contention this loop is meant to handle (e.g., /toggle-review racing with pi-hard-no’s own toggle), so the last attempt should still detect conflicts and fail/retry instead of renaming unconditionally.

Useful? React with 👍 / 👎.

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