os-backlog: wire the triage-trigger hook + arbitrary-project test coverage (cards #1818330518584820864, #1818330528525321347)
hooks/triage_check.rb and the Servers project heuristic/board-ensure
generalization already landed in f9a81a1 but the triage hook was never
registered in hooks.json, so it could never fire. This wires it in
(second SessionStart command, 8s timeout) and adds the missing test
coverage: TriageCheck's needs-triage/note-composition logic, an
arbitrary-project (Servers) BoardEnsurer case, and the ~/servers
resolver-ambiguity fix. Also reconciles session_start.py's pull-beats-push
docstring with the new hook's sanctioned exception, and fixes a stray
comment-indent bug in board_ensurer.rb. Suite 128/282/0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PLudjwx2GWw8CFdYaBzVzK
This commit is contained in:
parent
f9a81a17c5
commit
3708c0099d
|
|
@ -8,6 +8,11 @@
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/session_start.py",
|
"command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/session_start.py",
|
||||||
"timeout": 5
|
"timeout": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "ruby ${CLAUDE_PLUGIN_ROOT}/hooks/triage_check.rb",
|
||||||
|
"timeout": 8
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,15 @@ Deliberately contains ZERO board state and no briefs (notification policy v2:
|
||||||
pull beats push — board state is fetched on demand via /os-backlog:list, never
|
pull beats push — board state is fetched on demand via /os-backlog:list, never
|
||||||
pushed into a session).
|
pushed into a session).
|
||||||
|
|
||||||
|
Sibling hook triage_check.rb (card 1818330518584820864) is a narrow, sanctioned
|
||||||
|
exception to that policy: it makes one bounded Planka read to decide whether to
|
||||||
|
push a single mechanical dispatch directive — not a brief, not board contents —
|
||||||
|
when the current project's own Backlog has unlabeled cards. The only board-
|
||||||
|
derived fact it ever surfaces is the unlabeled count that justifies dispatching
|
||||||
|
card-triage; it never lists card titles or otherwise renders board state. Kept
|
||||||
|
as a separate script precisely so this file can stay a pure, network-free
|
||||||
|
function of filesystem state.
|
||||||
|
|
||||||
git project -> additionalContext note (rules only, near-zero tokens)
|
git project -> additionalContext note (rules only, near-zero tokens)
|
||||||
not a git repo -> silent (process rules only apply inside real projects)
|
not a git repo -> silent (process rules only apply inside real projects)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,8 @@ module Backlog
|
||||||
|
|
||||||
# @param name [String] board name (never an archived---prefixed name;
|
# @param name [String] board name (never an archived---prefixed name;
|
||||||
# this method only ever targets the active board)
|
# this method only ever targets the active board)
|
||||||
# @param project_name [String] "Dev" or "Clients" (or any project name)
|
# @param project_name [String] any Planka project name — created on demand
|
||||||
|
# if it doesn't already exist
|
||||||
# @return [Result]
|
# @return [Result]
|
||||||
def ensure(name, project_name:)
|
def ensure(name, project_name:)
|
||||||
raise ArgumentError, "board name must not use the archived-- prefix" if BoardSpec.archived?(name)
|
raise ArgumentError, "board name must not use the archived-- prefix" if BoardSpec.archived?(name)
|
||||||
|
|
|
||||||
|
|
@ -120,6 +120,18 @@ class BoardEnsurerTest < Minitest::Test
|
||||||
refute_nil archived
|
refute_nil archived
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_ensure_creates_an_arbitrary_project_name_on_demand
|
||||||
|
client = FakePlankaClient.new
|
||||||
|
ensurer = Backlog::BoardEnsurer.new(client: client, human_user_id: "human-1")
|
||||||
|
|
||||||
|
result = ensurer.ensure("ovh-prod", project_name: "Servers")
|
||||||
|
|
||||||
|
assert result.created
|
||||||
|
project = client.projects_store.first
|
||||||
|
assert_equal "Servers", project.name
|
||||||
|
assert_equal "Servers", result.project.name
|
||||||
|
end
|
||||||
|
|
||||||
def test_ensure_rejects_archived_prefixed_name
|
def test_ensure_rejects_archived_prefixed_name
|
||||||
client = FakePlankaClient.new
|
client = FakePlankaClient.new
|
||||||
ensurer = Backlog::BoardEnsurer.new(client: client, human_user_id: "human-1")
|
ensurer = Backlog::BoardEnsurer.new(client: client, human_user_id: "human-1")
|
||||||
|
|
|
||||||
|
|
@ -90,4 +90,22 @@ class ResolverTest < Minitest::Test
|
||||||
|
|
||||||
assert_equal "use llf-schema", resolver.resolve_string
|
assert_equal "use llf-schema", resolver.resolve_string
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_unconfigured_servers_repo_uses_board_named_after_repo
|
||||||
|
resolver = Backlog::Resolver.new(
|
||||||
|
repo_path: "/home/jared/servers/proxmox-ubuntu",
|
||||||
|
config_contents: nil,
|
||||||
|
boards: %w[proxmox-ubuntu]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_equal "use proxmox-ubuntu", resolver.resolve_string
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_project_for_returns_dev_clients_servers_or_nil
|
||||||
|
assert_equal "Dev", Backlog::Resolver.project_for("/home/jared/dev/some-repo")
|
||||||
|
assert_equal "Clients", Backlog::Resolver.project_for("/home/jared/clients/acme-co")
|
||||||
|
assert_equal "Servers", Backlog::Resolver.project_for("/home/jared/servers/ovh-prod")
|
||||||
|
assert_equal "Servers", Backlog::Resolver.project_for("/home/jared/servers")
|
||||||
|
assert_nil Backlog::Resolver.project_for("/home/jared/scratch/some-repo")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
require_relative "test_helper"
|
||||||
|
|
||||||
|
class TriageCheckTest < Minitest::Test
|
||||||
|
def test_card_missing_both_labels_needs_triage
|
||||||
|
assert Backlog::TriageCheck.needs_triage?([])
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_card_missing_priority_label_needs_triage
|
||||||
|
assert Backlog::TriageCheck.needs_triage?(["semi"])
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_card_missing_autonomy_label_needs_triage
|
||||||
|
assert Backlog::TriageCheck.needs_triage?(["P1"])
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_fully_labeled_card_does_not_need_triage
|
||||||
|
refute Backlog::TriageCheck.needs_triage?(%w[P1 semi])
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_note_for_no_unlabeled_cards_is_nil
|
||||||
|
assert_nil Backlog::TriageCheck.note_for("cc-os", [%w[P1 semi], %w[P0 hitl]])
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_note_for_counts_only_unlabeled_cards
|
||||||
|
labels = [%w[P1 semi], [], ["P2"]]
|
||||||
|
note = Backlog::TriageCheck.note_for("cc-os", labels)
|
||||||
|
|
||||||
|
refute_nil note
|
||||||
|
assert_includes note, "2 unlabeled Backlog cards on board cc-os"
|
||||||
|
assert_includes note, "card-triage"
|
||||||
|
assert_includes note, "haiku"
|
||||||
|
assert_includes note, "background"
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_note_for_singular_card_uses_singular_wording
|
||||||
|
note = Backlog::TriageCheck.note_for("cc-os", [[]])
|
||||||
|
|
||||||
|
assert_includes note, "1 unlabeled Backlog card on board cc-os"
|
||||||
|
refute_includes note, "1 unlabeled Backlog cards"
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Reference in New Issue