Skip to content

feat: add local project and kanban usage surfaces#2

Merged
the24thLetter merged 725 commits into
mainfrom
openclaw/local-main
May 28, 2026
Merged

feat: add local project and kanban usage surfaces#2
the24thLetter merged 725 commits into
mainfrom
openclaw/local-main

Conversation

@the24thLetter

@the24thLetter the24thLetter commented May 20, 2026

Copy link
Copy Markdown
Owner

Purpose

This PR is for local/fork review only. It is not intended for NousResearch/hermes-agent upstream merge.

Summary

  • Add shared project usage ledger helpers.
  • Add Project Usage dashboard plugin API/assets.
  • Surface Kanban token/tool/turn usage in CLI, native tool responses, dashboard API, and card badges.
  • Add scripts/update_local_fork.sh so local customizations can be rebased on upstream origin/main after Hermes updates.

Codex/Cursor sweep summary

Current accepted head: 8f95720bbfb85175187f83ebba28f8f739949c30

Bugs found and fixed

  • Fixed Cursor Bugbot 19cfaecf-6ed7-42d6-bda0-7fbc02f7a571: release manifests always include the release tag and only release events can move :latest; main pushes publish :main only.
  • Fixed Cursor Bugbot b9ce1492-dc9d-4442-b1f3-5586567bf412: :latest updates are guarded by the current registry org.opencontainers.image.revision plus an origin/main ancestry fallback, so backport releases cannot move :latest backwards.
  • Fixed Cursor Bugbot 85d87d13-2d27-415b-a339-8197f901ef5c: Docker label extraction reads both Image/Labels and image/labels JSON casing from docker buildx imagetools inspect.

Implemented changes in the PR

  • Added executable scripts/update_local_fork_and_restart.sh as the operator continuity wrapper around the narrow scripts/update_local_fork.sh Git helper.
  • The wrapper enforces canonical HERMES_HOME by default, clean checkout/branch switching, pre-rebase Hermes backup, editable reinstall via uv pip install --python ./venv/bin/python -e '.[all]', config migrate/check, scalar continuity settings only, YAML verification for review-agent skills, system LaunchDaemon-first gateway restart, GUI LaunchAgent-unloaded verification, no standalone Kanban daemon, dashboard restart, and post-upgrade health checks.
  • Fixed reviewer-identified wrapper blockers: removed hermes status --all, added a non-secret Telegram connection assertion, and verifies post-restart Kanban dispatcher activity from captured log offsets for both LaunchDaemon and manual-fallback paths.
  • Kanban board-scoped config now propagates through implementation fresh-base worktrees, worker environment, Review gate routing, Review merge-captain assignment, review-agent skills, and worker-context instructions.
  • Added regression tests for board-scoped Kanban overrides and Docker publish tag semantics, plus missing contributor author mappings so attribution checks pass.

Validation

  • bash -n scripts/update_local_fork_and_restart.sh — passed.
  • scripts/update_local_fork_and_restart.sh --help — passed.
  • scripts/update_local_fork_and_restart.sh --hermes-home /Users/openclaw/ai-os-migration/configs/hermes --dry-run — passed without mutating state.
  • ruff check hermes_cli/kanban_db.py hermes_cli/kanban.py tools/kanban_tools.py tests/hermes_cli/test_kanban_db.py tests/hermes_cli/test_kanban_core_functionality.py tests/test_docker_publish_workflow.py — passed.
  • env -u HERMES_KANBAN_BOARD -u HERMES_KANBAN_DB -u HERMES_KANBAN_TASK -u HERMES_KANBAN_RUN_ID -u HERMES_KANBAN_CLAIM_LOCK -u HERMES_KANBAN_WORKSPACE -u HERMES_KANBAN_WORKSPACES_ROOT -u HERMES_KANBAN_REQUIRE_REVIEW_BEFORE_DONE -u HERMES_KANBAN_MERGE_CAPTAIN_PROFILE ./venv/bin/python -m pytest tests/hermes_cli/test_kanban_db.py tests/hermes_cli/test_kanban_core_functionality.py tests/test_docker_publish_workflow.py -q — 406 passed, 1 skipped.
  • python3 /Users/openclaw/.codex/skills/cursor-bugbot-sweep/scripts/poll_cursor_review.py --repo the24thLetter/hermes-agent --pr 2 --head-sha current --once — current-head clean for 8f95720bbfb85175187f83ebba28f8f739949c30; check-run metadata still lists the three fixed prior Docker threads above as unresolved in GitHub.
  • gh pr checks 2 --repo the24thLetter/hermes-agent --watch --interval 10 — all required checks passing; Docker build/merge/save-duration jobs are skipped for this fork PR.

Local fork update/restart wrapper

  • Added executable scripts/update_local_fork_and_restart.sh as the operator continuity wrapper around the narrow scripts/update_local_fork.sh Git helper.
  • Encodes safeguards for canonical HERMES_HOME, clean checkout/branch switching, Hermes quick backup, editable reinstall via uv pip install --python ./venv/bin/python -e '.[all]', config migrate/check, scalar continuity settings, YAML-only review skill verification, system LaunchDaemon-first gateway restart, GUI LaunchAgent-unloaded verification, no standalone Kanban daemon, dashboard restart, and post-upgrade health checks.
  • Documents and reports the manual gateway fallback as live but unsupervised: no launchd KeepAlive, no boot/login restart, and no automatic crash recovery.

Validation:

  • bash -n scripts/update_local_fork_and_restart.sh
  • scripts/update_local_fork_and_restart.sh --help
  • scripts/update_local_fork_and_restart.sh --dry-run
  • git diff --cached --check
  • shellcheck not installed locally; bash -n used as shellcheck-equivalent validation.

Note

High Risk
Changes PID 1, image entrypoint, Docker Hub tag semantics, and credential/API client behavior—any regression affects every container user and long-running sessions.

Overview
This changeset is dominated by container runtime and CI delivery, not the Kanban/local-fork items named in the PR title (those do not appear in the provided diff).

Docker image moves from tini + entrypoint.sh to s6-overlay (/init, stage2-hook, main-wrapper, supervised services), ships Node 22 from an upstream stage, bakes HERMES_GIT_SHA for supportability, adds a docker exec privilege-drop shim, extra Python provider extras, and tighter build-context ignores. Smoke tests now hit the real image ENTRYPOINT (fixing a no-op after s6 migration).

Docker publish simplifies tagging: :main on main pushes only; releases tag the version and move :latest only when the release commit is not behind current :latest/origin/main (removes separate move-main / move-sha jobs). Docker integration tests run in the amd64 publish job against the already-loaded :test image. New hadolint/shellcheck workflow lints the Dockerfile and docker/ scripts.

CI tests switch from single-job pytest -n auto to a 6-shard matrix driven by scripts/run_tests_parallel.py with merged duration caching. Docs deploy always rebuilds the skills index; scheduled skills-index runs trigger deploy-site instead of duplicating deploy; a 4-hour freshness watchdog opens GitHub issues on stale live index.

Agent/runtime fixes include credential-pool provider mismatch guards and faster rotation on exhausted keys, custom-provider extra_body merge, platform enabled_toolsets gating for memory/context tools, MiniMax OAuth per-request tokens, atomic rollback on in-place model switch, safer TCP shutdown without cross-thread close (NousResearch#29507), and ACP delivery when transform_llm_output changes the final text after streaming. Anthropic adapter is refactored with safer OAuth credential writes and MCP name double-prefix avoidance.

Supply-chain audit uses three-dot diffs and narrows install-hook file detection to repo-root paths to cut false positives.

Reviewed by Cursor Bugbot for commit 8f95720. Bugbot is set up for automated code reviews on this repo. Configure here.

@the24thLetter

Copy link
Copy Markdown
Owner Author

bugbot run

Comment thread plugins/kanban/dashboard/plugin_api.py Outdated
Comment thread hermes_cli/kanban.py Outdated
@the24thLetter

Copy link
Copy Markdown
Owner Author

bugbot run

Comment thread hermes_cli/project_usage_ledger.py Outdated
@the24thLetter

Copy link
Copy Markdown
Owner Author

bugbot run

Comment thread hermes_cli/kanban.py
@the24thLetter

Copy link
Copy Markdown
Owner Author

bugbot run

Comment thread plugins/kanban/dashboard/plugin_api.py Outdated
@the24thLetter

Copy link
Copy Markdown
Owner Author

bugbot run

Comment thread hermes_cli/project_usage_ledger.py Outdated
@the24thLetter

Copy link
Copy Markdown
Owner Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit aaddb2f. Configure here.

Comment thread hermes_cli/kanban.py
@the24thLetter

Copy link
Copy Markdown
Owner Author

Merge-captain review: changes requested

I found a blocking Review-gate state-machine regression on the current head aa8871d80ad1650324dd96582cd6bd3d5d32dd4d.

Blocking finding:

  • hermes_cli/kanban_db.py: review-column spawn failures still restore the card to ready instead of review. Repro: create a task in review, run dispatch_once(..., spawn_fn=lambda ...: raise RuntimeError(...), failure_limit=3), and the task ends as status=ready, assignee=mergecaptain, with the run closed as spawn_failed. That means the next dispatcher tick treats the mergecaptain as an implementation worker, bypassing the Review-column protocol and potentially causing the exact review crash/respawn confusion this PR is meant to eliminate.

Required fix:

  • Teach the spawn-failure path to use _status_after_failed_run(...) / equivalent for review-origin runs, not unconditional ready.
  • Cover both below-threshold and breaker-tripped paths for review-origin spawn failures. Below threshold should restore review; if the breaker trips, make the terminal state intentional and documented (likely blocked with review metadata), not accidentally ready.
  • Add a regression test mirroring test_detect_crashed_review_worker_returns_to_review but with a review dispatch_once spawn function that raises before _set_worker_pid.

Validation I ran:

  • ./venv/bin/python -m py_compile hermes_cli/kanban_db.py tools/kanban_tools.py
  • ./venv/bin/python -m ruff check hermes_cli/kanban_db.py tools/kanban_tools.py tests/hermes_cli/test_kanban_db.py tests/tools/test_kanban_tools.py
  • ./venv/bin/python -m pytest -q tests/hermes_cli/test_kanban_db.py tests/tools/test_kanban_tools.py => 245 passed
  • Manual repro above => failed with status ready assignee mergecaptain run_status spawn_failed run_outcome spawn_failed

I stopped the local Bugbot polling after finding this blocker; Cursor Bugbot was still in progress at that point.

Comment thread hermes_cli/kanban_db.py
@the24thLetter

Copy link
Copy Markdown
Owner Author

Fixed the mergecaptain blocker on current head 4dc5853179f635c74cddd85d9d2c1773e9912a06: Review-origin dispatch spawn failures now restore to Review below the breaker threshold, and breaker-tripped failures intentionally block with restore_status=review recorded in run metadata/events. Validation: git diff --check, py_compile, ruff, pytest tests/hermes_cli/test_kanban_db.py tests/tools/test_kanban_tools.py => 247 passed.

Comment thread hermes_cli/project_usage_ledger.py Outdated
@the24thLetter

Copy link
Copy Markdown
Owner Author

Fixed Cursor Bugbot issue 1a1032a0 on current head b8ed1a9f2bf1f0f2f5a2d63652e1a023b868d080: unroutable Review rejections now record explicit review_rejection, missing_implementation_assignee, and explanatory metadata/event payloads instead of silently falling through to a generic block. Validation: git diff --check; py_compile; ruff; focused pytest => 248 passed; synthetic repro confirms the explicit metadata/event fields. Current-head Cursor Bugbot is still pending.

@the24thLetter

Copy link
Copy Markdown
Owner Author

Merge-captain blocker on current head b8ed1a9f2bf1f0f2f5a2d63652e1a023b868d080:

The Review gate still does not work end-to-end for real mergecaptain workers because _default_spawn switches the child to the profile-scoped HERMES_HOME, but does not propagate the root Review-gate settings. In this checkout, root config has kanban.require_review_before_done: true and merge_captain_profile: mergecaptain, while /profiles/mergecaptain/config.yaml does not. A dispatched mergecaptain worker calling kanban_block therefore evaluates review_gate_enabled() as false and leaves the task blocked/mergecaptain with a generic blocked event instead of returning it to the original implementer.

Repro from /Users/openclaw/ai-os-migration/configs/hermes/hermes-agent:

TMP=$(mktemp -d)
DB="$TMP/kanban.db"
HERMES_HOME=/Users/openclaw/ai-os-migration/configs/hermes \
HERMES_KANBAN_DB="$DB" \
./venv/bin/python - <<'PY' > "$TMP/task.txt"
from hermes_cli import kanban_db as kb
kb.init_db()
with kb.connect() as conn:
    t=kb.create_task(conn,title='profile review config gap',assignee='default')
    impl=kb.claim_task(conn,t)
    assert kb.complete_task(conn,t,summary='ready',expected_run_id=impl.current_run_id)
    task=kb.get_task(conn,t)
    print(t, task.status, task.assignee)
PY
TID=$(awk '{print $1}' "$TMP/task.txt")
HERMES_HOME=/Users/openclaw/ai-os-migration/configs/hermes/profiles/mergecaptain \
HERMES_PROFILE=mergecaptain \
HERMES_KANBAN_DB="$DB" \
./venv/bin/python - <<PY
from hermes_cli import kanban_db as kb
with kb.connect() as conn:
    review=kb.claim_review_task(conn,'$TID')
    assert kb.block_task(conn,'$TID',reason='review rejected',expected_run_id=review.current_run_id)
    task=kb.get_task(conn,'$TID')
    run=kb.latest_run(conn,'$TID')
    print('after_block', task.status, task.assignee, run.status, run.metadata)
    print('last_event', kb.list_events(conn,'$TID')[-1].kind, kb.list_events(conn,'$TID')[-1].payload)
PY

Observed:

t_3374280f review mergecaptain
claimed running mergecaptain
after_block blocked mergecaptain blocked None
last_event blocked {'reason': 'review rejected'}

Required fix:

  • Ensure review workers spawned by _default_spawn carry the Review-gate control-plane settings they need after profile activation. Either propagate HERMES_KANBAN_REQUIRE_REVIEW_BEFORE_DONE / HERMES_KANBAN_MERGE_CAPTAIN_PROFILE into the child env from the dispatcher/root config, or make the Review-gate decision derive from durable board/run metadata instead of the worker profile config.
  • Add a regression test that simulates a root dispatcher config with Review enabled plus a mergecaptain profile without that config, then verifies a Review-origin block_task still returns to the original implementation assignee and emits review_rejected metadata/event.

Validation run on current head before blocking:

  • ./venv/bin/python -m py_compile hermes_cli/kanban_db.py tools/kanban_tools.py
  • ./venv/bin/python -m ruff check hermes_cli/kanban_db.py tests/hermes_cli/test_kanban_db.py tools/kanban_tools.py tests/tools/test_kanban_tools.py
  • ./venv/bin/python -m pytest -q tests/hermes_cli/test_kanban_db.py tests/tools/test_kanban_tools.py => 248 passed
  • Broader focused suite: ./venv/bin/python -m pytest -q tests/hermes_cli/test_kanban_db.py tests/tools/test_kanban_tools.py tests/hermes_cli/test_kanban_cli.py tests/hermes_cli/test_project_usage_ledger.py tests/plugins/test_kanban_dashboard_plugin.py tests/plugins/test_project_usage_dashboard_plugin.py => 409 passed

Comment thread hermes_cli/kanban_db.py Outdated
Comment thread plugins/kanban/dashboard/plugin_api.py Outdated
Comment thread hermes_cli/kanban_db.py
Comment thread hermes_cli/kanban_db.py
Comment thread hermes_cli/kanban_db.py Outdated
Comment thread hermes_cli/kanban_db.py
Comment thread hermes_cli/kanban_db.py
Comment thread hermes_cli/kanban_db.py
Comment thread tools/kanban_tools.py
Comment thread plugins/kanban/dashboard/plugin_api.py Outdated
Comment thread plugins/kanban/dashboard/plugin_api.py Outdated
Comment thread hermes_cli/kanban_db.py
@the24thLetter

Copy link
Copy Markdown
Owner Author

Cursor/Bugbot sweep final status for 0a9ba08c67cc1eaf4d3fcfa62e3c5ef1c38ce50b:

  • Cursor Bugbot completed successfully on the exact current head (check_run 77237494677): no new issues found.
  • GitHub reviewThreads enumeration confirms 0 unresolved non-outdated Cursor Bugbot threads, including previous-review threads.
  • Focused validation passed: py_compile, ruff, git diff --check, and 376 focused pytest tests.
  • PR intentionally left open / not merged per fork-local no-merge instruction on Kanban card t_b9ad5294.

…, ClawHub, browse.sh, OpenAI, …) (NousResearch#32336)

The Skills Hub page was stuck on a stale Feb 25 snapshot, showing only Built-in
+ Optional + Anthropic + LobeHub. The unified index already has 2078 skills
from skills.sh / ClawHub / LobeHub / GitHub taps / Claude Marketplace, and
BrowseShSource adds another ~330 — none of it was reaching the page.

Changes:

- website/scripts/extract-skills.py: read website/static/api/skills-index.json
  (the unified multi-source catalog, rebuilt twice daily) as the canonical
  external source. Keep the legacy skills/index-cache/ fallback for offline
  builds. Add friendly per-source labels (skills.sh, ClawHub, browse.sh,
  OpenAI, HuggingFace, Anthropic, LobeHub, etc.) and per-entry installCmd.
- website/src/pages/skills/index.tsx: add source pills + ordering for the 11
  new sources; render installCmd from the index entry.
- website/scripts/prebuild.mjs: when no local skills-index.json exists, fetch
  the live one from hermes-agent.nousresearch.com so local 'npm run build'
  matches production without burning GitHub API quota.
- scripts/build_skills_index.py: crawl BrowseShSource so browse.sh entries
  land in the unified index. Adjust source_order.
- tools/skills_hub.py: GitHubSource.DEFAULT_TAPS — openai/skills moved its
  skills into skills/.curated/ and skills/.system/, so add both as explicit
  taps (the listing code skips dotted dirs by design). Drop
  VoltAgent/awesome-agent-skills (README-only, no SKILL.md files) and
  MiniMax-AI/cli (singular skill, not a tap directory). Net effect: github
  source jumps from 83 → 143 skills, with OpenAI properly included.
- .github/workflows/deploy-site.yml: build the unified index BEFORE running
  extract-skills.py — previous order meant extract-skills always fell back
  to the legacy cache. Drop the 'skip if file exists' guard; the file is
  gitignored and must be rebuilt every deploy.
- .github/workflows/skills-index.yml: drop the broken 'deploy-with-index'
  job (it cp'd 'landingpage/\*' which no longer exists, failing every cron
  run since the landingpage move). Replace it with a workflow_dispatch
  trigger of deploy-site.yml so the index refresh still reaches production
  on schedule.
- website/docs/user-guide/features/skills.md: drop VoltAgent from the
  default-taps doc list to match the code.

Before: 695 skills (Built-in 90, Optional 84, Anthropic 16, LobeHub 505).
After:  2168 skills across 9 source pills, including the 1212 skills.sh
        entries the user expected to see.
@the24thLetter the24thLetter force-pushed the openclaw/local-main branch from bd876a8 to 7976c00 Compare May 28, 2026 14:51
-t "${IMAGE_NAME}:main" \
-t "${IMAGE_NAME}:latest" \
"${args[@]}"
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:latest tag races and regresses on backport releases

Medium Severity

Both main pushes and releases unconditionally tag :latest, removing the ancestor check that previously prevented backport releases from dragging :latest backwards. A patch release on an older branch (e.g., v1.1.6 after v1.2.3) will overwrite :latest with older code. Additionally, every commit to main now overwrites :latest, making it track unstable dev code rather than the latest stable release—conflating the purpose of :main and :latest.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 7c0bd37. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7ed5aa8. Configure here.

Comment thread .github/workflows/docker-publish.yml Outdated
tag_commit="$(git -C "$GITHUB_WORKSPACE" rev-parse "${TAG}^{commit}")"
latest_revision=""
if latest_metadata="$(docker buildx imagetools inspect "${IMAGE_NAME}:latest" --format '{{json .}}' 2>/dev/null)"; then
latest_revision="$(python3 -c 'import json,sys; data=json.load(sys.stdin); print((data.get("image") or {}).get("labels", {}).get("org.opencontainers.image.revision", ""))' <<<"$latest_metadata")"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Label extraction uses wrong JSON key casing

Medium Severity

The Python one-liner extracting the OCI revision label from docker buildx imagetools inspect --format '{{json .}}' uses data.get("image"), but the Go template field is .Image (uppercase). Go's default JSON serialization preserves the uppercase field name, so data.get("image") always returns None, making latest_revision always empty. This causes the :latest backport guard to silently fall through to the weaker origin/main ancestry check instead of comparing against the actual current :latest commit.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 7ed5aa8. Configure here.

@the24thLetter the24thLetter merged commit f35e01f into main May 28, 2026
26 checks passed
@the24thLetter the24thLetter deleted the openclaw/local-main branch May 28, 2026 18:41
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.