|
| 1 | +# Default Verdict CLI and Telegram Commands |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +Add the ability to view and set the default policy verdict from the CLI and Telegram bot. The REST API already supports this via `GET/PATCH /api/config`. The CLI and Telegram are missing dedicated commands, and the policy listing does not display the current default. |
| 6 | + |
| 7 | +## Context |
| 8 | + |
| 9 | +- Store: `internal/store/store.go` (GetConfig, UpdateConfig with DefaultVerdict field) |
| 10 | +- Policy engine: `internal/policy/engine_store.go` (loads default from store) |
| 11 | +- API: `internal/api/server.go` (GET/PATCH /api/config already works) |
| 12 | +- CLI: `cmd/sluice/policy.go` (list/add/remove/import/export subcommands) |
| 13 | +- Telegram: `internal/telegram/commands.go` (/policy show/allow/deny/remove) |
| 14 | +- Default is stored in `config` table singleton, validated to allow/deny/ask |
| 15 | +- Existing Telegram handlers use `recompileAndSwap()` under `reloadMu` for engine updates (not SIGHUP) |
| 16 | +- `/policy show` already displays default in header text as "Current policy (default: X)". Replace with `[default]` row for consistency with CLI. |
| 17 | + |
| 18 | +## Development Approach |
| 19 | + |
| 20 | +- **Testing approach**: Regular (code first, then tests) |
| 21 | +- Complete each task fully before moving to the next |
| 22 | +- CRITICAL: every task MUST include new/updated tests |
| 23 | +- CRITICAL: all tests must pass before starting next task |
| 24 | +- Uses gofumpt for Go formatting |
| 25 | + |
| 26 | +## Testing Strategy |
| 27 | + |
| 28 | +- **Unit tests**: test CLI command output and store interaction (success + error cases) |
| 29 | +- **Unit tests**: test Telegram command handler for /policy default (success + error cases) |
| 30 | + |
| 31 | +## Solution Overview |
| 32 | + |
| 33 | +Four changes that share the same store API (GetConfig/UpdateConfig): |
| 34 | + |
| 35 | +1. **CLI `sluice policy default [allow|deny|ask]`**: no args shows current default, with arg sets it. Requires `--db` flag like other policy commands. |
| 36 | +2. **CLI `sluice policy list`**: display default verdict as first row `[default] <verdict>`. |
| 37 | +3. **Telegram `/policy default [allow|deny|ask]`**: no args shows current, with arg sets it. Recompiles engine inline via `recompileAndSwap()` under `reloadMu`. |
| 38 | +4. **Telegram `/policy show`**: replace header "Current policy (default: X)" with `[default] <verdict>` as first row for consistency with CLI. |
| 39 | + |
| 40 | +After setting the default via CLI, the running sluice process needs a SIGHUP to reload (same as `policy add`/`remove`). After setting via Telegram or API, the engine is recompiled inline. |
| 41 | + |
| 42 | +## Implementation Steps |
| 43 | + |
| 44 | +### Task 1: Add `sluice policy default` CLI command |
| 45 | + |
| 46 | +**Files:** |
| 47 | +- Modify: `cmd/sluice/policy.go` |
| 48 | +- Modify: `cmd/sluice/policy_test.go` |
| 49 | + |
| 50 | +- [ ] Add `policyDefaultCmd` subcommand under `policyCmd` with `--db` flag |
| 51 | +- [ ] No args: read store via `GetConfig`, print current default verdict |
| 52 | +- [ ] With arg: validate against allow/deny/ask (same pattern as `handlePolicyAdd`), call `store.UpdateConfig`, print confirmation |
| 53 | +- [ ] Update usage string in `handlePolicyCommand` to include `default` |
| 54 | +- [ ] Write tests: get current default, set valid default, reject invalid default |
| 55 | +- [ ] Run tests |
| 56 | + |
| 57 | +### Task 2: Show default verdict in `sluice policy list` output |
| 58 | + |
| 59 | +**Files:** |
| 60 | +- Modify: `cmd/sluice/policy.go` |
| 61 | +- Modify: `cmd/sluice/policy_test.go` |
| 62 | + |
| 63 | +- [ ] In `policyListCmd` handler, read config and prepend default verdict as first row: `[default] <verdict>` |
| 64 | +- [ ] Show before numbered rules. When no rules exist, still show default row (skip "no rules found" message since default is always present) |
| 65 | +- [ ] Write test for list output including default row |
| 66 | +- [ ] Run tests |
| 67 | + |
| 68 | +### Task 3: Add `/policy default` Telegram command |
| 69 | + |
| 70 | +**Files:** |
| 71 | +- Modify: `internal/telegram/commands.go` |
| 72 | +- Modify: `internal/telegram/commands_test.go` |
| 73 | + |
| 74 | +- [ ] Add `default` case to the `/policy` command dispatcher |
| 75 | +- [ ] No args: read config from store via `GetConfig`, reply with current default |
| 76 | +- [ ] With arg: validate against allow/deny/ask, call `store.UpdateConfig` under `reloadMu`, then `recompileAndSwap()`, reply with confirmation |
| 77 | +- [ ] Update usage string in `handlePolicy` and help text in `handleHelp` to include `default` |
| 78 | +- [ ] Write tests: get default, set valid default, reject invalid default, store-not-configured fallback |
| 79 | +- [ ] Run tests |
| 80 | + |
| 81 | +### Task 4: Show default verdict in `/policy show` Telegram output |
| 82 | + |
| 83 | +**Files:** |
| 84 | +- Modify: `internal/telegram/commands.go` |
| 85 | +- Modify: `internal/telegram/commands_test.go` |
| 86 | + |
| 87 | +- [ ] In `policyShowFromStore`, replace header "Current policy (default: X)" with `[default] <verdict>` as first row before numbered rules |
| 88 | +- [ ] Apply same change to engine-backed `policyShow` fallback |
| 89 | +- [ ] Write test for show output with default row |
| 90 | +- [ ] Run tests |
| 91 | + |
| 92 | +### Task 5: Verify acceptance criteria |
| 93 | + |
| 94 | +- [ ] `sluice policy default` shows current default |
| 95 | +- [ ] `sluice policy default ask` sets default to ask |
| 96 | +- [ ] `sluice policy default invalid` returns error |
| 97 | +- [ ] `sluice policy list` shows default as first row |
| 98 | +- [ ] Telegram `/policy default` shows current default |
| 99 | +- [ ] Telegram `/policy default deny` sets and confirms |
| 100 | +- [ ] Telegram `/policy show` shows default as first row |
| 101 | +- [ ] `sluice policy export` still shows default verdict correctly |
| 102 | +- [ ] Run full test suite: `go test ./... -v -timeout 30s` |
| 103 | + |
| 104 | +### Task 6: [Final] Update documentation |
| 105 | + |
| 106 | +- [ ] Update CLAUDE.md CLI subcommands section with `sluice policy default` |
| 107 | +- [ ] Move this plan to `docs/plans/completed/` |
| 108 | + |
| 109 | +## Post-Completion |
| 110 | + |
| 111 | +**Manual verification:** |
| 112 | +- Deploy to knuth and test via Telegram /policy default |
| 113 | +- Verify sluice policy list output on the server |
0 commit comments