Activity log as a decision log: per-poll observations and rule provenance - #36
Merged
Merged
Conversation
Medium: - Classify GitHub's startup_failure conclusion as failing so a workflow that fails to start alerts instead of pending forever (github_eval) - Allow workflow_call events as eligible required PR checks so reusable-workflow CI can complete a watch (github_client) - Map http.client.HTTPException to ProviderError so truncated model responses degrade instead of crashing watch add --intent (inference_api) - Treat a concurrent atomic token rotation as safe (stable regular file already opened) and keep the daemon alive through transient store-iteration failures (secrets_file, daemon) - Make config list survive invalid stored values and truncated JSON while keeping legacy plaintext secrets masked; guard config unset/keys against non-object config.json (cli, config) - Tokenize query and content symmetrically in relevant_to so the recorded PR identity matches remembered context (memory) Low: - Reject empty memory content; report hidden older items in memory list - Decline the watch-add confirmation on EOF stdin; report unusable secret stores cleanly across config and watch commands - Serialize config/secret read-modify-write cycles with sidecar flocks and write config.json atomically - Start the cooperative-stop window after startup-lock acquisition so queued time is not deducted from the stop budget (runner_control) - Classify truncated bodies as retryable network errors, local secret-store failures as blockers, and permanent 301/308 redirects as PR-not-found blockers instead of endless retries (github_client) - Default next_check_at at creation so an ACTIVE task is never persisted unschedulable (tasks) 535 tests pass (20 new regression tests); the first-use e2e demo passes all 8 scenarios.
Upgrade the activity log from an event log to a decision log (issue #35): - Every completed poll appends a check-observed entry (commit-pinned snapshot digest + dedup fingerprint) before the events it produced, so no-change polls are reconstructable and any decision is replayable from the log alone. - Every event's evidence names the policy rule that fired (stop:/notify:/ record:) and, for pending states, the concrete pending_reason. - Recording is all-or-nothing per run: the supersession check now runs once, after summarizing and before anything is recorded or delivered, so a pause/cancel/scope change still leaves no entries. - Activity-write failures are isolated: the run and delivery never depend on the log accepting an entry. - nanodot activity hides check-observed by default; --all includes it. watch list/show summaries always skip observations. Docs: README section and docs/design/decision-log.md. Tests: 539 passing, including new observation/replay/isolation/provenance coverage.
This was referenced Oct 3, 2026
Closed
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.
Fixes #35 — makes the activity log a decision log: it now records what the runner observed, not just what changed.
What changed
runner.py): every completed poll appends acheck-observedentry before the events it produced — the commit-pinned snapshot digest plus the dedup fingerprint (newstatemachine.observation()). No-change polls are no longer invisible, and any stop/notify decision is replayable from the log alone.statemachine.py): every event's evidence now names the policy rule that fired (stop: …/notify: …/record: …), and pending events carry the concretepending_reasonthatstep()already distinguished internally.runner.py): the supersession check now runs once, after summarizing and before anything is recorded or delivered. A pause/cancel/scope change still leaves zero activity entries, and recording is all-or-nothing per run (previously a lifecycle change mid-loop could leave earlier events recorded).runner.py): activity appends go through a_recordhelper — a log failure never fails the run or blocks delivery, mirroring the memory-observation pattern.cli.py):nanodot activityhidescheck-observedby default;--allincludes them.watch list/watch showsummaries always skip observations.ActivityLog.query()gainedexclude_kinds; same-timestamp entries now order by insertion (rowid) so the log replays as observe → decide → act.Deliberately unchanged
docs/design/egress.md), redacted at write time.activity.evidenceis free-form JSON.Verification
--allbehavior).examples/first_pr_watch.py): 8/8 scenarios pass through the real CLI.docs/design/decision-log.mdand a README section.