Skip to content

feat: add dashboard observability viewer (logs + health)#1

Closed
Koan-Bot wants to merge 639 commits into
mainfrom
koan.atoomic/implement-807
Closed

feat: add dashboard observability viewer (logs + health)#1
Koan-Bot wants to merge 639 commits into
mainfrom
koan.atoomic/implement-807

Conversation

@Koan-Bot

Copy link
Copy Markdown
Owner

What

Adds a log viewer and health dashboard for real-time observability.

Why

Resolves Anantys-oss#807. Debugging requires manually grepping through run.log and awake.log. This adds structured log access and system health monitoring to the dashboard.

How

  • GET /api/logs — tails run.log/awake.log with source, limit, and substring filter. Deque-based read, 2000 char truncation.
  • GET /logs — viewer page with source selector, text filter, auto-scroll, error/warn highlighting.
  • GET /api/health — disk usage thresholds + process liveness via PID files.
  • Health card on main dashboard — polls /api/health every 60s with colored status dots.

Testing

12 new unit tests. Full dashboard suite passes (117 tests).

Koan-Bot pushed a commit that referenced this pull request Apr 2, 2026
e-check on the next iteration when CI completes. This eliminates the risk of fixing something that's already being re-tested.
- **Simplified status guard logic**: Separated the 'pending', non-failure, and no-logs cases into distinct early returns with clear messages, replacing the combined `status not in ("failure",) and not ci_logs` condition.
- **Added test for pending early return**: New `test_ci_pending_returns_early` verifies the early-return behavior when CI is still running.
- **No changes to print statements** (Important #1): The 4 flagged `print(..., file=sys.stderr)` calls with `[ci_check]` prefixes are intentional structured logging, consistent with the codebase convention (e.g., `[ci_queue]` in `check_ci_status`). Not debug leftovers.
- **Test timeout** (Blocking #2): Investigated — all blocking calls (`time.sleep`, network) are properly mocked in the test file. The 120s timeout in the quality report is from running the full 11000+ test suite, not from these specific tests.
Koan-Bot added a commit that referenced this pull request Apr 2, 2026
The decompose prompt let Claude use #1, #2 etc. to cross-reference
sub-issues in their Dependencies section. After creation on GitHub,
these became links to unrelated issues. Now:

- Prompt uses SUB-N placeholders instead of #N syntax
- After all sub-issues are created, a post-creation pass replaces
  SUB-N with the real #<number> via gh issue edit
- New issue_edit() helper in github.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Koan-Bot added a commit that referenced this pull request Apr 3, 2026
The decompose prompt let Claude use #1, #2 etc. to cross-reference
sub-issues in their Dependencies section. After creation on GitHub,
these became links to unrelated issues. Now:

- Prompt uses SUB-N placeholders instead of #N syntax
- After all sub-issues are created, a post-creation pass replaces
  SUB-N with the real #<number> via gh issue edit
- New issue_edit() helper in github.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Koan-Bot added a commit that referenced this pull request Apr 5, 2026
 on `recover_missions()`** (`recover.py:171`): Changed `-> int` to `-> tuple` to match the new `(count, escalated_list)` return value, per reviewer's Important #1.
- **Eliminated TOCTOU race in `recover_missions()`** (`recover.py:190-192`): Replaced `if not missions_path.exists()` with `try/except FileNotFoundError` pattern, consistent with the same fix applied to `pick_mission.py` in this PR, per reviewer's Important #2.
- **Replaced `print(..., file=sys.stderr)` with `log.warning()` in `_reset_failure_count()`** (`pr_review_learning.py:343-344`): Uses the module's existing logger for consistency with the `log.warning()` calls added in `github_notifications.py` in this same PR, per reviewer's Suggestion #1. Also resolves the quality report's "debug print statement" flag.
- **Added return type annotation to `fallback_extract()`** (`pick_mission.py:21`): Changed bare `-> tuple` to `-> tuple[str | None, str | None]` for clarity on the nullable return values, per reviewer's Suggestion #2.
Koan-Bot added a commit that referenced this pull request Apr 6, 2026
nable change is adding the stale-CI caveat to the prompt.

Here's my summary of changes:

- **Added stale-CI caveat to `koan/skills/core/rebase/prompts/ci_fix.md`** per reviewer request: the pre-push CI check inspects results from *before* the rebase, so failures may already be resolved. Added an "Important Context" section warning Claude that the logs are from before the rebase, and added step 2 ("Cross-check against the current diff") and step 7 ("If all failures appear to be already resolved, make no changes") to prevent unnecessary or harmful fixes based on stale CI results.
- **No changes needed for missing mocks** (reviewer concern #1 and #3): verified that all existing `run_rebase` integration tests already include `@patch('app.rebase_pr._fix_existing_ci_failures', return_value=False)`, and the 10 new test classes (`TestCheckExistingCi` with 5 tests, `TestFixExistingCiFailures` with 5 tests) are complete and present in the test file — the truncation was only in the PR diff view.
Koan-Bot added a commit that referenced this pull request Apr 7, 2026
nable change is adding the stale-CI caveat to the prompt.

Here's my summary of changes:

- **Added stale-CI caveat to `koan/skills/core/rebase/prompts/ci_fix.md`** per reviewer request: the pre-push CI check inspects results from *before* the rebase, so failures may already be resolved. Added an "Important Context" section warning Claude that the logs are from before the rebase, and added step 2 ("Cross-check against the current diff") and step 7 ("If all failures appear to be already resolved, make no changes") to prevent unnecessary or harmful fixes based on stale CI results.
- **No changes needed for missing mocks** (reviewer concern #1 and #3): verified that all existing `run_rebase` integration tests already include `@patch('app.rebase_pr._fix_existing_ci_failures', return_value=False)`, and the 10 new test classes (`TestCheckExistingCi` with 5 tests, `TestFixExistingCiFailures` with 5 tests) are complete and present in the test file — the truncation was only in the PR diff view.
Koan-Bot added a commit that referenced this pull request Apr 7, 2026
 on `recover_missions()`** (`recover.py:171`): Changed `-> int` to `-> tuple` to match the new `(count, escalated_list)` return value, per reviewer's Important #1.
- **Eliminated TOCTOU race in `recover_missions()`** (`recover.py:190-192`): Replaced `if not missions_path.exists()` with `try/except FileNotFoundError` pattern, consistent with the same fix applied to `pick_mission.py` in this PR, per reviewer's Important #2.
- **Replaced `print(..., file=sys.stderr)` with `log.warning()` in `_reset_failure_count()`** (`pr_review_learning.py:343-344`): Uses the module's existing logger for consistency with the `log.warning()` calls added in `github_notifications.py` in this same PR, per reviewer's Suggestion #1. Also resolves the quality report's "debug print statement" flag.
- **Added return type annotation to `fallback_extract()`** (`pick_mission.py:21`): Changed bare `-> tuple` to `-> tuple[str | None, str | None]` for clarity on the nullable return values, per reviewer's Suggestion #2.
Koan-Bot added a commit that referenced this pull request May 13, 2026
…vider is enabled

g outside the lock in `loop_manager.py`. Let me re-read to see the full truncated suggestion.

Looking at the code in `loop_manager.py`, the `if force:` log at line 693-694 is outside the lock but only logs — it's a minor suggestion and marked 🟢 (not actionable change request). The TOCTOU race (suggestion #1) is also noted as consistent with the codebase and not a change request.

The only actionable review comment was the 🟡 Important one about the signal being silently lost when both providers are disabled. Here's the summary:

---

- **Guard signal consumption behind provider check** (`run.py`): Only consume the `/check_notifications` signal file in `_run_iteration()` when at least one notification provider (GitHub or Jira) is enabled. Previously the signal was consumed unconditionally, meaning it would be silently lost if both providers were disabled — the force check would never actually run. Now the signal file is left in place for the next iteration where config may have changed.
Koan-Bot added a commit that referenced this pull request May 14, 2026
ocess.TimeoutExpired` as a retryable exception in `retry_with_backoff`, and after max retries it re-raises. So the `subprocess` import and the `except` branch are **not** dead code — they're correct. The import stays.

Here's the summary:

- **Moved `json` import to module level** — was lazily imported as `import json as _json` inside `_is_subject_closed()`. The underscore alias was unnecessary since there's no name collision. Replaced all `_json` references with `json`. (Review comment #1)
- **Kept `import subprocess` at module level** — verified that `subprocess.TimeoutExpired` can indeed propagate from `gh_api()` → `run_gh()` → `retry_with_backoff()` (it's listed as a retryable exception, re-raised after max retries). The import and except branch are not dead code. (Review comment #2 — investigated, no change needed)
- **Skipped comment #3** (comment_id empty-string fallback) — reviewer noted this is a pre-existing pattern not introduced by this PR, not an actionable change request.
Koan-Bot added a commit that referenced this pull request May 14, 2026
ocess.TimeoutExpired` as a retryable exception in `retry_with_backoff`, and after max retries it re-raises. So the `subprocess` import and the `except` branch are **not** dead code — they're correct. The import stays.

Here's the summary:

- **Moved `json` import to module level** — was lazily imported as `import json as _json` inside `_is_subject_closed()`. The underscore alias was unnecessary since there's no name collision. Replaced all `_json` references with `json`. (Review comment #1)
- **Kept `import subprocess` at module level** — verified that `subprocess.TimeoutExpired` can indeed propagate from `gh_api()` → `run_gh()` → `retry_with_backoff()` (it's listed as a retryable exception, re-raised after max retries). The import and except branch are not dead code. (Review comment #2 — investigated, no change needed)
- **Skipped comment #3** (comment_id empty-string fallback) — reviewer noted this is a pre-existing pattern not introduced by this PR, not an actionable change request.
Koan-Bot added a commit that referenced this pull request May 14, 2026
…vider is enabled

g outside the lock in `loop_manager.py`. Let me re-read to see the full truncated suggestion.

Looking at the code in `loop_manager.py`, the `if force:` log at line 693-694 is outside the lock but only logs — it's a minor suggestion and marked 🟢 (not actionable change request). The TOCTOU race (suggestion #1) is also noted as consistent with the codebase and not a change request.

The only actionable review comment was the 🟡 Important one about the signal being silently lost when both providers are disabled. Here's the summary:

---

- **Guard signal consumption behind provider check** (`run.py`): Only consume the `/check_notifications` signal file in `_run_iteration()` when at least one notification provider (GitHub or Jira) is enabled. Previously the signal was consumed unconditionally, meaning it would be silently lost if both providers were disabled — the force check would never actually run. Now the signal file is left in place for the next iteration where config may have changed.
bluetoothbot and others added 18 commits May 16, 2026 13:18
The warning "Claude hit the max turns limit (5). To increase: set
skill_max_turns in instance/config.yaml (current: 5)" was misleading
when fired from chat-style callers (ask, github_reply, github_intent,
spec_generator, deepplan/plan reviewers, implement commit-subject
helper). These callers hardcode max_turns=1/3/5; skill_max_turns
(default 200) does not affect them, so the suggested remedy did
nothing.

Threads a max_turns_source argument through run_command()/
run_command_streaming(). Skill runners keep the default
("skill_max_turns") and are unchanged. Hardcoded-limit callers
pass max_turns_source=None, which produces a clearer message
("This call uses a hardcoded limit and is not configurable.")
instead of pointing the user at an unrelated config key.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cold-start notification processing was serial: each notification triggered
several sequential `gh` API calls (fetch comment, find_mention_in_thread,
check subject state, mark read, react). With a 24h lookback returning 10+
notifications, this added 5-20s of wall-clock latency before the first
iteration could plan.

- New `github.parallel_workers` config (default 4, range 1-16) controls
  the worker pool used by `_process_notifications_concurrent`.
- Per-notification work is I/O bound (subprocess + HTTP) so threads scale
  near-linearly. workers=1 keeps the original serial path.
- Existing thread-safe primitives cover the shared state: cache lock,
  atomic mission writes, lock-guarded backoff counters.
classify_cli_error uses loose quota patterns (e.g. "rate limit",
"too many requests") on combined stdout+stderr, causing false QUOTA
classification when Claude's response discusses API rate limiting.

These tests demonstrate the bug before the fix.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
classify_cli_error() used loose quota patterns (e.g. "rate limit",
"too many requests", "usage limit") on combined stdout+stderr. When
Claude's response discussed API rate limiting, this falsely triggered
QUOTA classification, causing spurious mission requeueing and pauses.

Apply the same split-detection strategy already used by
handle_quota_exhaustion in quota_handler.py: strict patterns only
for stdout, all patterns for stderr.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary of changes:

- **Fixed CI regression in `classify_cli_error()`** (`koan/app/cli_errors.py`): The split stdout/stderr quota check broke `test_failure_raises_runtime_error`, which passes a `MagicMock` for stdout. The original combined f-string implicitly coerced non-string values; the new direct `re.search(stdout)` call did not. Added explicit `str()` coercion for both `stdout` and `stderr` before regex matching, restoring the defensive behavior while keeping the false-positive fix intact. Addresses @atoomic's request to view and fix the CI failure.
Skill runners (rebase_pr, recreate_pr, squash_pr) produced zero stdout
during execution — all prints went to stderr. The liveness watchdog
(600s timeout) only resets on stdout lines, so long-running Claude CLI
calls caused the subprocess to be killed before completion.

Add print("[skill] ...", flush=True) heartbeat statements before every
blocking operation (Claude invocations, API calls, git operations) to
keep the watchdog satisfied.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nantys-oss#1307)

Adds a rolling burn-rate estimator that tracks the percentage of session
quota consumed per minute, computes time-to-exhaustion, and lets Koan act
on the projection before the wall.

- `burn_rate.py` maintains a 20-sample circular buffer in
  `instance/.burn-rate.json` plus a `last_warned_at` cursor so warnings
  fire at most once per quota cycle.
- `mission_runner.update_usage` records a sample after every run; the
  cost is the percentage of `session_token_limit` consumed by that run.
- `UsageTracker.decide_mode()` consults the buffer through a new
  `instance_dir` argument; if projected exhaustion is < 30 min, it drops
  one tier (deep→implement→review) and surfaces the downgrade in the
  decision reason.
- `iteration_manager` checks each iteration whether projected exhaustion
  is < 60 min while the next reset is still > 2 h away, and if so emits
  a one-shot Telegram alert via the outbox.
- `/quota` now prints the live burn rate (%/h) and estimated time to
  exhaustion when there is enough history.

Tests cover buffer trimming, persistence, edge cases (no history, zero
span, invalid values), mode multipliers, and the tracker downgrade
integration. Full suite passes (12364 tests).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Skills can now declare Python package dependencies via a `requirements:`
field in their SKILL.md frontmatter. Missing packages are auto-installed
via pip before the handler's first execution in a session.

- Parse `requirements: [pkg1, pkg2]` in frontmatter (inline list or single string)
- Check importability before installing (fast path for satisfied deps)
- Support version specifiers (>=, ==, <)
- Cache per-skill per-session to avoid repeated checks
- Return SkillError on install failure (surfaced to Telegram)
- Document the field in koan/skills/README.md

Closes Anantys-oss#1245

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… cleanup

All three review items have been addressed. Here's the summary:

- **Pip flag injection validation** (blocking): Added early validation in `ensure_requirements()` that rejects any requirement entry starting with `-`, preventing pip CLI flag injection (e.g. `--index-url`) via crafted SKILL.md files. Added `test_ensure_requirements_rejects_flag_injection` test.
- **Incomplete version specifier stripping** (important): Replaced chained `.split(">=")[0].split("==")[0].split("<")[0]` with `re.split(r'[><=!~]', pkg)[0]` to handle all PEP 440 operators (`~=`, `<=`, `!=`, `===`, etc.). Added `test_ensure_requirements_handles_tilde_specifier` test.
- **Fragile test state management** (important): Added `_reset_requirements_cache()` helper in `skills.py` and an `autouse` pytest fixture in `TestSkillRequirements` that clears the cache before/after each test. Removed all manual `try/finally` blocks and direct `_requirements_satisfied` manipulation from tests.
Adds ruff lint config with the PERF ruleset, excludes tests/ from PERF
checks (fixture loops add little perf value), and rewrites the production
+ skill violations: list append in for-loop → list.extend / comprehension,
dict iterator key→value, dict-build loop → dict comprehension, slice copy
→ list.copy.

All 12633 tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`gh run list --branch X --limit 1` was returning the "Dependabot
auto-merge" workflow (conclusion=skipped) on non-Dependabot PRs,
which check_ci_status, wait_for_ci, and check_existing_ci all
treated as a failure. drain_one then kept injecting /ci_check fix
missions against a PR whose real CI was green — see
aio-libs/yarl#1681 for the field report.

Add aggregate_ci_runs(runs) and fetch_branch_ci_runs(branch, repo)
helpers in claude_step. The aggregator filters out skipped,
cancelled, neutral, and action_required conclusions, then reduces
the remaining runs to (status, run_id) with failure > pending >
success priority. All three CI status callers now share the same
filtering, fetching up to 20 runs per branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When GitHub's Private Vulnerability Reporting is enabled on a target
repo, /security_audit now submits critical/high findings as private
security advisories instead of public issues. Lower-severity findings
remain public. Configurable per-project via projects.yaml security
section (pvrs mode + threshold). Graceful fallback to public issues
on any PVRS API failure.

Implements: Anantys-oss#1341
Plan: PR Anantys-oss#1269 (docs/plan-pvrs-security-audit.md)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ensure all remote tracking refs for the base branch are fresh before
any mission work begins — both new branches and rebases.

Two complementary changes:
- git_prep: after primary sync, fetch base from all secondary remotes
- claude_step: pre-fetch all relevant remotes before the rebase loop

Addresses review feedback on PR Anantys-oss#1333 about keeping main up to date
regardless of its name before working on branches.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a Linting section to CLAUDE.md documenting ruff as the project's
linter, and add a `make lint` Makefile target so contributors can
check compliance before pushing. Addresses review feedback on Anantys-oss#1345.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
toddr-bot and others added 22 commits May 24, 2026 19:00
tag_complexity_in_pending() inserts [complexity:X] between the mission
text and the ⏳ timestamp marker. The needle used by start_mission(),
complete_mission(), and fail_mission() was captured before this tag was
added, so the substring match in _remove_item_by_text() silently fails.
Result: missions never move to In Progress or Done — they stay stuck in
Pending while the agent runs them, and /list shows nothing running.

Fix: strip [complexity:X] tags (and normalize whitespace) from the line
before comparing against the needle in _remove_item_by_text().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The /add_project skill normalized every input URL (including SSH form)
to HTTPS, then ran a bare `git clone`. Over HTTPS git has no credential
helper, doesn't read GH_TOKEN, and can't prompt (stdin closed), so
private repos failed with:

    fatal: could not read Username for 'https://github.com': Device not configured

Clone via `gh repo clone` instead, which authenticates with the same
session gh credentials (GH_TOKEN) already used to confirm push access.
No reliance on SSH config or a git credential helper.

Tests updated to target the run_gh clone path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…n_repos

@mentions from repos cloned into workspace/ after the process started were
dropped as "unregistered repo". known_repos resolves aliases by git remote,
but only from an in-memory cache populated once at startup, so a later clone
(under any alias directory name) was invisible until a full restart.

Refresh the cache lazily in _get_known_repos_from_projects() via the
idempotent populate_workspace_github_urls(). The git remote — not the
directory name — is now the source of truth, and the "only registered repos"
filter still holds. New clones are picked up on the next poll without a
restart.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…view

PR Anantys-oss#1517 review surfaced a second location with the same class of bug and two
polish items:

- attention.py:_collect_github_mention_items() built known_repos from
  projects.yaml only — ignoring workspace projects and using bare .lower()
  instead of normalization. Now reuses _get_known_repos_from_projects() so
  workspace repos cloned under any alias dir are matched by git remote, with
  consistent owner/repo normalization (reviewer's Option 1).
- Broad-except refresh failure now logs at warning, not debug, so a persistent
  failure to recognize new repos is visible without debug logging.
- Trimmed the over-long explanatory comment to the non-obvious WHY.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
QUOTA_CHECK_UNRELIABLE path logged to stderr only — the human got no
Telegram notification that quota protection was disabled for the session.
Now sends a dedicated outbox warning and sets quota_check_unreliable flag
in the result dict for downstream visibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Silent drops of negative/NaN/inf values made stale burn rates
undetectable. Now logs at WARNING level so operators can diagnose
upstream parse failures feeding bad cost_pct values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Token parse failures on failing missions were invisible because the
cost_tracking_failed flag was gated behind exit_code == 0. Failing
missions can still consume tokens — the gap should be flagged regardless.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
subject.url from the GitHub notifications API returns API-format URLs
(api.github.com/repos/.../pulls/N) instead of browser-navigable links.
Adds _api_url_to_web() to convert pulls→pull and issues→issues.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The api() function was wrapping input_data in `-F body=@-` (a form field),
but the PVRS endpoint expects the full JSON payload as the raw HTTP body.
This caused 422 errors on every submission, triggering the fallback path
that created dummy "PVRS unavailable" public issues instead of private
security advisories.

Add `raw_body` parameter to api() that uses `--input -` for endpoints
needing raw JSON payloads. Closes Anantys-oss#1490, Anantys-oss#1487, Anantys-oss#1392, Anantys-oss#1389.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Notification is now tag-based: Telegram messages fire only when a new
release tag appears on upstream, not on every new commit to main.
The update mechanism (pull from upstream main) is unchanged.

- Add _get_latest_tag(), check_for_new_release_tag(), _notify_new_release_tag()
- Track last-notified tag in instance/.last-notified-tag (plain text)
- Fetch tags alongside refs in check_for_updates() (--tags flag)
- Write tag file only after successful notification to avoid missed alerts
- Update all existing tests + add new test classes for tag logic (45 total)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The /implement review gate no longer blocks and waits for human fixes when
plan quality issues are found. Instead it spawns a codebase-grounded
improver subagent that resolves the issues autonomously (up to 3 rounds),
posts the improved plan to the GitHub issue, and proceeds with implementation.
Falls open after exhausting retries.

Closes Anantys-oss#757

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The gate now returns a _GateImproved object carrying both the improved
plan and the issues that were fixed. This context is appended to the
implementation prompt so the agent understands what was refined and can
pay extra attention to the corrected areas.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ment prompt

Add guiding principles that bias the plan improver toward minimal changes,
reuse of existing code, fewer abstractions, and deletion over addition.
The agent should fix what was flagged without expanding scope or introducing
unnecessary complexity.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… redacted issues

When PVRS submission fails or is unavailable, findings are now saved to
`instance/security/<project>/<date>.<severity>.<slug>.md` with full details
instead of creating useless redacted public GitHub issues. The operator gets
a Telegram notification with the file path and a suggested /fix command.

High+ severity findings always produce a local file (dual-write when PVRS
succeeds). Medium/low findings still go to public GitHub issues as before.

- Add _write_local_finding() + _slugify_finding_title() helpers
- Add instance_dir param to create_issues(), IssueCreationResult.local_files
- Remove _submit_redacted_fallback_issue() (dead code)
- Add instance.example/security/.gitkeep

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… collisions

Blocking:
- High-severity findings no longer fall through to public issues when
  instance_dir is empty. They are skipped with a stderr warning instead
  of leaking full exploit details publicly.

Important:
- datetime.now() captured once in _write_local_finding() to prevent
  midnight-boundary date mismatch between filename and content.
- Filename now includes a 6-char title hash suffix to prevent silent
  overwrites when two findings share the same slug prefix.

Silent failure analysis:
- pvrs_status only set to "submitted" when advisory_url is non-empty,
  preventing findings from silently disappearing.
- _write_local_finding() wrapped in try/except so a filesystem error
  on one finding doesn't crash the loop for remaining findings.
- PVRS exception logging uses repr(e) to preserve exception type.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…head_tracker

Fix Docker + Codex provider end-to-end operation by:
- Dispatch auth checks by configured provider (claude vs codex)
- Add codex binary verification in startup checks
- Remove phantom missions.docker.md volume mount
- Update provider-agnostic language in format_outbox

Also removes deprecated head_tracker module and rescan skill (now handled
by remote_rename_detector). Removes enable_multiple_instances config
(GitHub API now silently skips unregistered repos).

Fixes Anantys-oss#1404.
- Add GET /api/logs endpoint: tails run.log and awake.log with
  source, limit, and q (substring filter) params; deque-based read
  avoids loading full files; lines truncated at 2000 chars
- Add GET /logs page: source selector, text filter with debounce,
  line count badge, auto-scroll toggle; error/warn keyword highlight
- Add GET /api/health endpoint: disk usage (ok/warn/error thresholds
  at 85%/95%), run and awake process liveness via PID files
- Add health card to main dashboard: polls /api/health every 60s,
  shows colored dots for disk, run, and awake status
- Add Logs nav link to base.html
- Add 12 unit tests covering all new endpoints (117 total, all pass)

Co-Authored-By: Claude <noreply@anthropic.com>
@Koan-Bot
Koan-Bot force-pushed the koan.atoomic/implement-807 branch from 84b6996 to bb3694f Compare May 25, 2026 11:52
@Koan-Bot Koan-Bot closed this May 25, 2026
@Koan-Bot
Koan-Bot deleted the koan.atoomic/implement-807 branch May 25, 2026 17:15
Koan-Bot pushed a commit that referenced this pull request Jun 29, 2026
Address review feedback on PR Anantys-oss#2200:

- Wait for the user bus socket before the first `systemctl --user` call in
  install-user-service.sh. On a fresh no-session host (sudo -niu, linger just
  enabled) logind brings up user@<uid>.service asynchronously, so the prior
  immediate daemon-reload could fail with "Failed to connect to bus" and abort
  under set -e (warning #1).
- Document the systemd-user deployment mode in docs/setup/systemd-user.md
  (env var, linger, PATH preservation, uninstall, troubleshooting) and link it
  from docs/README.md (warning #2).
- Drop the no-op After/Wants=network-online.target directives — that target
  does not exist in the --user manager scope (suggestion #1).
- Add uninstall-user-service.sh + matching Makefile target (with optional
  disable-linger=1) to mirror the system/launchd uninstall paths (suggestion #2).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Koan-Bot pushed a commit that referenced this pull request Jun 30, 2026
Address review feedback on PR Anantys-oss#2200:

- Wait for the user bus socket before the first `systemctl --user` call in
  install-user-service.sh. On a fresh no-session host (sudo -niu, linger just
  enabled) logind brings up user@<uid>.service asynchronously, so the prior
  immediate daemon-reload could fail with "Failed to connect to bus" and abort
  under set -e (warning #1).
- Document the systemd-user deployment mode in docs/setup/systemd-user.md
  (env var, linger, PATH preservation, uninstall, troubleshooting) and link it
  from docs/README.md (warning #2).
- Drop the no-op After/Wants=network-online.target directives — that target
  does not exist in the --user manager scope (suggestion #1).
- Add uninstall-user-service.sh + matching Makefile target (with optional
  disable-linger=1) to mirror the system/launchd uninstall paths (suggestion #2).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Koan-Bot added a commit that referenced this pull request Jul 3, 2026
The /review skill is prompt-driven, so its output quality can't be unit-tested
directly, and CI has no model token to run a live review. This adds a two-tier
eval design (PLAN.md) and ships Tier 1 — a contract eval that runs as plain
pytest in the existing fast CI group, no API key needed:

- Prompt-contract: every review prompt keeps its load-bearing structure when
  rendered (all {@include} partials resolve; JSON-output prompts keep their
  'valid JSON' directive + full severity vocabulary). Catches prompt drift —
  the #1 unprotected regression for a prompt-driven skill.
- Golden-output anchors: curated fixtures that must stay schema-valid and
  semantically consistent.
- Semantic invariants: app/review_eval.evaluate_review() layers cross-field
  rules the JSON schema can't express (blocking finding => lgtm:false,
  finding_refs in range, empty comments + not-LGTM => warning), proven against
  an adversarial corpus of schema-valid-but-broken reviews.

evaluate_review() is the shared scoring core; the future Tier-2 model-driven
quality eval (golden diffs with planted bugs, API-gated, never in CI) reuses it.
Koan-Bot pushed a commit that referenced this pull request Jul 18, 2026
…nantys-oss#2439)

* feat(rebase): split /rebase into rebase-only default + /rebase --fix

Previously /rebase always rebased AND applied PR review feedback. The
feedback leg now lives behind an explicit --fix (implied by any trailing
text after the URL, e.g. a focus area or severity keyword); a bare
/rebase performs a pure rebase.

- Thread --fix end to end: rebase/handler.py parses/preserves it,
  skill_dispatch._build_rebase_cmd is the single decision point, and
  rebase_pr (run_rebase -> _run_rebase_impl) gates the feedback leg on
  apply_feedback = fix or _FEEDBACK_ON_BY_DEFAULT.
- Migrate feedback-dependent callers to --fix so they don't regress at
  the flip: /fix on a PR, /rr (handler + review_rebase sub_commands),
  and autoreview. pr_checkup (conflict-only), ci_dispatch, and
  review_comment_dispatch are intentionally left as plain /rebase.
- Flip the default to rebase-only and surface a temporary transition
  notice (chat reply + PR comment) on the bare-rebase path, date-gated
  by rebase_transition.FIX_NOTICE_DEADLINE (2026-08-17). The behavior
  change is permanent; only the notice is time-limited.
- Update durable specs (specs/skills/rebase.md, specs/skills/fix.md) and
  user docs (docs/users/skills.md, docs/users/user-manual.md);
  regenerate OKF indexes.

Eval harness untouched: the scored already-solved decision runs
independently of the feedback leg.

Architectural change: durable specs (specs/skills/**) updated
contract-first.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(review): severity-hint catch-all points at /rebase --fix

The multi-severity review footer told users a bare `@bot rebase` /
`/rebase <url>` fixes "all" feedback. After the /rebase split that path
only rebases onto the base branch and applies no review feedback, so the
catch-all now advertises `--fix` and spells out that a bare rebase skips
feedback. Severity variants (critical/important) already imply --fix via
trailing text and are unchanged.

Addresses review warning #1 on PR Anantys-oss#2439.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(rebase): point feedback-retry CTAs at /rebase --fix

When the feedback leg fails, is dropped, or hits provider quota, the
recovery guidance told the human to re-run a bare `/rebase`. After the
/rebase split that only rebases onto the base branch and does NOT
re-apply review feedback — the exact recovery these paths need. Point
the feedback-failed WARNING alert and the feedback_quota retry string at
`/rebase --fix`, update the two feedback-context code comments, and keep
the docs/messaging/github-alerts.md mirror in sync.

The generic rebase-failure recovery helper still says `/rebase` on
purpose: there the rebase itself failed, so a bare re-run is correct.

Addresses review warning #2 on PR Anantys-oss#2439.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(rebase): align skill metadata/text with the --fix contract

The /rr combo and its GitHub expansion already queue /rebase --fix
(covered by existing tests), and the user manual documents it, but the
skills' own descriptive surfaces still read as a bare rebase:

- review_rebase handler docstring + usage message now say the rebase leg
  runs /rebase --fix and applies the review feedback.
- review_rebase SKILL.md command description mentions --fix.
- rebase SKILL.md frontmatter + command description note that a bare
  rebase only rebases onto the base branch and --fix applies feedback
  (review suggestion #1).

Add a usage-message test asserting the /rr help text surfaces --fix.
Addresses the /rr improvement request and skill-metadata suggestion on
PR Anantys-oss#2439.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(rebase): strip system metadata before the --fix decision

A requeued bare `/rebase <url>` could silently enable the feedback leg.
Dispatch stripped lifecycle timestamps and the 📬 marker before builders
saw the args, but not the `[complexity:X]` classifier tag, the `[r:N]`
crash-recovery counter, or the 🎫 origin marker. Those survive requeue
(quota/auth/stagnation), so a later pick yielded `/rebase <url>
[complexity:medium]`; `_build_rebase_cmd` treats any post-URL remainder
as user focus and added `--fix` — re-applying review feedback that was
intentionally skipped, and burning quota.

Fix at the dispatch chokepoint (root cause, covers every skill): add
`missions.strip_system_metadata()` and apply it alongside the lifecycle
strip, so no builder ever sees queue-appended metadata as an argument.
Also align the rebase handler docstring with the rebase-only default.

Addresses review warning #1 and suggestion #1 on PR Anantys-oss#2439.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Koan-Bot added a commit that referenced this pull request Jul 18, 2026
…] tag

The docstring claimed enabled=False fully disables decomposition ("the
tag is ignored"), contradicting _maybe_decompose_mission which honors the
[decompose] tag regardless of enabled. Rewrite the docstring to match
actual behavior and add a regression test asserting the tag triggers
decomposition when enabled=False.

Closes the last outstanding review item (Koan-Bot warning #1).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

Dashboard: Observability and structured logging viewer

8 participants