feat: add dashboard observability viewer (logs + health)#1
Closed
Koan-Bot wants to merge 639 commits into
Closed
Conversation
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.
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>
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>
…ons with auto_update
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
force-pushed
the
koan.atoomic/implement-807
branch
from
May 25, 2026 11:52
84b6996 to
bb3694f
Compare
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>
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.
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
Testing
12 new unit tests. Full dashboard suite passes (117 tests).