Conversation
Reorganizes the config into symmetric [negative] and [positive] sections, parameterizes the safety layer by ChannelSpec, and adds an IntifaceProvider with a three-layer disconnect watchdog (explicit stop, atexit, signal handler) so the device cannot run past max_duration_s when the controller process dies cleanly. The 1.x [auth]/[provider]/ [device]/[safety]/[rate_limit]/[tool] schemas are rejected at load with a migration message; rlaif init writes the 2.0 shape. 297 tests passing, dry-run green on both channels including the watchdog no-refund invariant. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Revised project description and tool functionalities for clarity.
Replace the single-client snippet prompt with a multi-pick that accepts comma- or space-separated indices/names plus 'a'/'all' and 's'/'skip'. For each pick, auto-install via installer.install() when the client is in installer.SUPPORTED; fall back to snippet_run() for vscode/zed (the two clients we still won't auto-mutate). Failures on one client don't take out the rest. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- dry_run: extract typed MockProvider/MockRewardProvider locals so `.calls` accesses don't go through the Provider/RewardProvider ABCs (which legitimately don't expose it). Knocks out 16 pyright errors and the downstream "Scenario.ok partially unknown" cascade. - rewards/intiface: drop unused `import buttplug` (the `from buttplug import (...)` form is what's actually used). Bind the freshly connected client to a typed local in get_client() before stashing on self, so the return is `ButtplugClient` not `... | None`. - server: delete the dead `_build_default_safety` helper (no callers in src/ or tests/). Cleans up the now-orphan NEGATIVE_CHANNEL, POSITIVE_CHANNEL, SafetyConfig imports. - test_intiface: extend the file-level `# pyright:` disables with `reportArgumentType=false` so the deliberately-structural _FakeCore injection passes. Matches the four disables already on that line. - test_rewards, test_safety, test_intiface: ruff --fix import-order (I001). uv run pyright: 0 errors. uv run ruff check: clean. uv run pytest: 297 passed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
README and CLAUDE.md still described `rlaif init` as offering a single MCP snippet at the end. Update them to reflect the new multi-select auto-install flow (with vscode/zed falling back to snippet). Also fix some pre-existing doc drift in cli.py: the install/uninstall help text and module docstring still said "7 clients" / "seven supported configs" and the docstring's enumerated auto-install list was missing opencode. No code changes; pyright + ruff + pytest all still clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`argparse.ArgumentParser.error` is typed `NoReturn` (it calls sys.exit), so the trailing `return 2` was unreachable — pyright was flagging it as dead code. The defensive parser.error() catch-all stays as a guard against someone adding a sub.add_parser() without a dispatch arm. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- inline compose_negative_description / compose_positive_description into
_compose_description at the two call sites; drop one-line wrappers.
- promote server.{_build_negative_runtime,_build_positive_runtime} to
build_negative_runtime / build_positive_runtime; doctor and live_smoke
reuse them instead of reimplementing.
- add rlaif._util.pretty_json; replace identical _pretty copies in dry_run,
live_smoke, log.
- live_smoke: load() once in run(), pass Config into _run_negative /
_run_positive.
- config: factor missing-field reporting into _require_fields.
- installer: merge _print_unsupported_install / _print_unsupported_uninstall
into _print_unsupported(action=...).
- doctor: collapse _negative_issues / _positive_issues into _channel_issues
with an _offline_hint(channel, kind) helper.
- rewards/__init__: drop "(planned)" from IntifaceProvider line; it shipped.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop the "shock" default — it was a footgun left over from before the positive channel landed. Every real construction site (safety.py x2) already passes channel=spec.name; the only other caller was a test stub. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- TokenBucket.snapshot(now) returns (tokens, next_refill_at) in one _refill; info_snapshot uses it instead of two separate calls. - OpsLog.recent: list(islice(reversed(deque), limit)) — O(limit) instead of materializing the entire ring just to slice the head off. - log._run_stats: single-pass split of fired vs refused with inline intensity/duration/refusal-reason accumulation. - intiface._select_device: collect visible names while scanning for the match instead of iterating devices.values() twice. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Default rlaif log --tail 10 was slurping the entire ops.jsonl into memory. Replace with a backward seek that reads 8K chunks from EOF and stops once it has the requested number of complete non-empty lines. Output is byte-for-byte identical to the previous implementation; the multi-MB synthetic test in test_cli.py guards the equivalence. tail <= 0 still reads the whole file (it must — it shows everything). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
build_file_sink already paid the cost of re-opening the file per write; without flush+fsync, a crash mid-line could lose entries the safety story claims are persisted. SSD latency is invisible. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pulls the duplicated authorize -> fire -> commit|rollback shape out of handle_rlaif_negative and handle_rlaif_positive into a private _fire_channel helper. Each handler still owns its own typed-error dispatch table, so Provider and RewardProvider namespaces stay disjoint at the call site (CLAUDE.md hard rule #8). The watchdog no-refund contract (hard rule #6) is encoded as the single refund=False entry in the positive-channel table. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three parallel dicts (BUILDERS in snippet, _PATHS + _ADAPTERS in installer) all keyed on the same client names had no structural guarantee against drift. Consolidate into a frozen ClientRecord dataclass + CLIENTS_REGISTRY in a new leaf module rlaif._clients; snippet.py and installer.py defer their registry imports to break the cycle, leaving tomlkit / ruamel.yaml confined to installer.py as before. Adding a client now means one record entry plus the matching adapter / path-fn / builder, not three coordinated edits. CLI behavior is unchanged: same client list, same argparse choices, same snippet output, same install/uninstall paths and exit codes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CLAUDE.md architecture block now lists `_clients.py` (the single MCP-client registry) and `_util.py`, describes `_fire_channel`'s dispatch-table shape, documents the canonical `build_*_runtime` constructors, and replaces the dead `compose_*_description` references with `_compose_description`. The "things that look like bugs but are not" section gains entries for the fsync-on-every-append durability guarantee and the backward-scan `--tail` implementation, and the pyright-cache note is generalized past `rlaif.rewards` to cover any newly-added internal module. README architecture tree picks up `_clients.py` and `_util.py`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Updated README to clarify feedback channels and configuration instructions.
Removed unnecessary repetition about safety checks and MCP tools.
Updated comments for clarity and simplified instructions for the configuration file.
Updated the description in pyproject.toml to remove specific references to feedback channels.
Clarified the description of the MCP server and its reinforcement channels.
Apply ruff format to files that had drifted from the project formatter (multi-line string-arg splits collapsed to one line where they fit). Pure formatting; no behavior change. Gets the file format-clean before the next round of edits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…defaults PiShock and OpenShock now share field names — PiShock's api_key → api_token, sharecode → shocker_id. The pishock SDK still sees its legacy parameter names internally; only the rlaif config/CLI surface changes. Env vars renamed to match: RLAIF_PISHOCK_API_TOKEN, RLAIF_PISHOCK_SHOCKER_ID, RLAIF_OPENSHOCK_API_TOKEN. [positive.intiface] simplified: ws_url → base_url for parity with OpenShock; client_name dropped (always "rlaif"); device_index removed in favor of device_name (exact name or display name) since intiface's index ordering was never stable across reconnects. Env var renamed to RLAIF_INTIFACE_BASE_URL. ChannelSpec gained five default_* fields and SafetyConfig.for_spec fills missing kwargs from the spec, so a partial [positive.safety] block now picks up the README's positive defaults (75/5/5/30) instead of inheriting the negative dataclass-level ones (25/2/3/600). The positive-channel description frame advertises the actual positive defaults. README example reconciled with its own defaults table on positive max_intensity (70 → 75). Pre-fuse keys (api_key, sharecode, ws_url, client_name, device_index) are rejected at load time with messages pointing at their replacements rather than silently dropped. PiShock's shocker_id is treated as capability-bearing in the redacted config dump (prefix-only); OpenShock's UUID-shaped shocker_id stays plain — it shows up in the dashboard and isn't a secret on its own. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
2.0: add positive feedback