feat: exec approvals store write path and coordinator side effects#505
feat: exec approvals store write path and coordinator side effects#505AlexAlves87 wants to merge 2 commits into
Conversation
…ects (PR8)
Implements the store write path (AddAllowlistEntryAsync, RecordAllowlistUseAsync)
and wires the side-effect calls into ExecApprovalsCoordinator.
Side effects fire strictly after the final allow decision is confirmed (both
pass1 pre-approved and post-pass2 branches). Best-effort: never throw; any
I/O failure degrades to a logged warning. UpdateFileAsync refuses to write
a malformed file. SemaphoreSlim serializes intra-process writes.
Pattern validation is non-empty only, matching macOS parity. New entries
carry {id, pattern} only — lastUsedAt is absent on creation and stamped by
RecordAllowlistUseAsync on first use (macOS addAllowlistEntry parity).
Rail 19 preserved: coordinator not referenced in any production src/ file.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Strip rail codes, PR numbers, research doc references, D-labels, CVE/ADR tags, and other planning labels from all ExecApprovals source files. No logic changed — comments only. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Thanks for putting this together. The useful piece here is the exec-approvals write path: persisting Allow Always decisions into The branch has drifted pretty far from current Could you open a fresh PR from current
That should make this much easier to review and land while keeping the credit and direction from your work here. |
|
Superseded by #526 — fresh branch from current master with the same focused scope. |
What
Adds the write path to
ExecApprovalsStoreand wires the two side-effect calls intoExecApprovalsCoordinator.Store — new public API:
AddAllowlistEntryAsync(agentId, pattern)— persists a new allowlist entry after an AllowAlways prompt decision. Deduplicates on write (OrdinalIgnoreCase). Returnstrueif present after the call (added or already there),falseon empty pattern or I/O failure. New entries carry{id, pattern}only —lastUsedAtis absent on creation, matching macOSaddAllowlistEntrybehavior, and is stamped byRecordAllowlistUseAsyncon first use.RecordAllowlistUseAsync(agentId, pattern, command, resolvedPath)— updateslastUsedAt,lastUsedCommand, andlastResolvedPathfor every matching entry after a final allow. Returnsfalseif pattern not found or on I/O failure.Store — private infrastructure:
UpdateFileAsync(mutate)— load → mutate → atomic save, serialized by the existingSemaphoreSlim. Never throws. Refuses to overwrite a malformed file. Handles transientIOExceptionon the atomic move as a degraded path: logsWarn, no retry.Coordinator — side effects wired:
RecordAllowlistUsageAsyncfires on both allow exit points: the pass1 pre-approved branch and the post-pass2 branch. Both are required to cover the common allowlist-satisfied case.PersistAllowlistEntriesAsyncfires only after pass2 = Allow andfollowupDecision == AllowAlways, strictly outside the_promptLockblock.Allowresult.Not wired in production yet: the coordinator is still not referenced in any production
src/file. TheProductionWiring_CoordinatorNotReferencedInSrctest remains green.Design notes
Side effects fire strictly after the final allow decision is confirmed — not before the second evaluator pass as in the macOS socket path. This is a deliberate structural safety choice: the macOS sequence is "persist before pass2" (and relies on the proof that
Evaluate(context, AllowAlways)always produces Allow); this implementation prefers safety-by-structure over proof-by-argument.Pattern validation in
AddAllowlistEntryAsyncis non-empty only, matching macOS parity. Basename-only patterns are inert at match time but not rejected at persist time.Testing
145 tests passing, 0 failures. 24 new tests added in this change (17 store, 7 coordinator).
Store tests cover: success paths, dedup, not-found, malformed-file refusal, I/O failure degradation on both mutators (using
FileShare.ReadsoLoadFilesucceeds butFile.Moveis blocked), concurrency (5 concurrent writes produce a single entry), and round-trip JSON validation.Coordinator tests cover: AllowAlways persistence, non-allowlist security guard, duplicate pattern dedup, allowlist usage recording, allowlist-not-satisfied guard, pass1 pre-approved path, and fallback path with allowlist satisfied.
Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com