Repository navigation
permission: report blocked state to herdr while prompting - #85
Conversation
Emit herdr:blocked before each confirmation prompt and release it in a finally block, so a Herdr pane shows blocked with a permission: <command> label while Pi waits for a human. Signed-off-by: Pat Losoponkul <[email protected]>
Drop playBell, the PI_NO_BELL escape hatch, and the vendored sound now that Herdr reports the blocked state. Remove the agent_end handler that existed only to ring the finish bell, and update the nix file list. Signed-off-by: Pat Losoponkul <[email protected]>
Review escalated — F-001 needs product intentDecision: Approve intentional removal of all permission audio—prompt bells and natural, non-aborted completion bells, outside and inside Herdr—or retain existing audio + Findings: 0 blockers · 1 significant supported attention-behavior regression awaiting input (F-001) · 0 report-only · 0 unresolved technical investigations · 1 product decision. No blocked-event correctness defect established. Validation: Fresh local format/lint/typecheck passed; tests 410 passed, 1 skipped. Nix succeeded with 0 newly executed checks (cached); source-equivalent CI passed (401 passed, 10 skipped). Skips/provenance below; no outstanding required automated gate. Coverage: No live Herdr/TUI/audio end-to-end testing. Reviewed: Complete original axis reports, full review handoff, judgment ledger, and auditPublication audit and operation state
Prepublication history guard receipts{
"at": "2026-10-21T15:23:28.257330+00:00",
"history": {
"pr-comments": {
"endpoint": "issues/85/comments?per_page=100",
"pages": 1,
"count": 0,
"sha256_raw": "cf1cbb66a638b4860a516671fb74850e6ccf787fe6c4c8d29e9c04efe880bd05",
"exit": 0,
"complete_body": [
[]
]
},
"review-comments": {
"endpoint": "pulls/85/comments?per_page=100",
"pages": 1,
"count": 0,
"sha256_raw": "cf1cbb66a638b4860a516671fb74850e6ccf787fe6c4c8d29e9c04efe880bd05",
"exit": 0,
"complete_body": [
[]
]
},
"reviews": {
"endpoint": "pulls/85/reviews?per_page=100",
"pages": 1,
"count": 0,
"sha256_raw": "cf1cbb66a638b4860a516671fb74850e6ccf787fe6c4c8d29e9c04efe880bd05",
"exit": 0,
"complete_body": [
[]
]
},
"pr-timeline": {
"endpoint": "issues/85/timeline?per_page=100",
"pages": 1,
"count": 2,
"sha256_raw": "ce6f07be7ad1c681253aa16ff0c84fb2cc0bae05b8844739e28d7e538a44ffed",
"exit": 0,
"events": [
"committed",
"committed"
],
"commits": [
"739bb1eed4cbf9e2665f80188afa6b7d27553966",
"aecf8dd0a83d2a29b248a6d95a63c534b7bfeed3"
]
},
"issue84-comments": {
"endpoint": "issues/84/comments?per_page=100",
"pages": 1,
"count": 0,
"sha256_raw": "cf1cbb66a638b4860a516671fb74850e6ccf787fe6c4c8d29e9c04efe880bd05",
"exit": 0,
"complete_body": [
[]
]
},
"issue84-timeline": {
"endpoint": "issues/84/timeline?per_page=100",
"pages": 1,
"count": 2,
"sha256_raw": "d25a6b83214b680786cd3a6624e8dd58abcfe0f262a9d0b8032872f287790879",
"exit": 0,
"events": [
"labeled",
"cross-referenced"
]
}
}
}Complete original review handoff (verbatim; includes both original axis files and appendices)Fresh read-only PR #85 review — round 1Boundary and reviewed identityOnly clean input /tmp/pr85-review-inputs.md, its cited sources and necessary implementation/SDK/check-source dependencies were used. No contract file, previous review reports/conversations, recovery records, comment/review history, or historical planning archives were read. No repository edits, comments, checkouts, commits, pushes or ref changes. Only requested handoff files under /tmp were written. Nix/npm checks can use external caches and temporary directories; the review worktree remained clean. Installed skill: /home/pat/.pi/agent/skills/code-review/SKILL.md. Two general-purpose Standards and Spec agents ran together in one parallel foreground tool message. Standards received all twelve Fowler heuristics in full, overriding/smell-judgment rules, standards paths, full diff command and both commits; Spec received the clean issue/current regression requirements and same full range. Their complete original outputs follow without reranking.
All seven files reviewed, 405 insertions / 64 deletions:
Initial guard used status --short, branch --show-current, rev-parse HEAD/base, merge-base, ls-remote origin refs/heads/issue-84 refs/pull/85/head, diff --stat and log. Every identity matched and diff was nonempty. Completion guards repeated clean status, HEAD/merge-base, remote refs and diff hash. Original axis reportsOriginal Standards response (verbatim)Standards0 documented-standard breaches; 0 actionable baseline smells. Reviewed all seven changed files against the pinned baseline instructions, not merely the PR’s revised
All twelve smell heuristics were considered; none warrants a finding. Tool-enforced formatting/type rules were excluded from manual findings. Audit appendixReviewed identity: HEAD Actual sources read
Read-only commands/results
Skips/uncertainties
Original Spec response (verbatim)Spec1 finding — P2: Bell/audio removal exceeds issue #84’s requested behavior. Issue #84 asks: “bracket both prompts with an emit pair.” It separately says the existing
Missing/partial requirements: None identified in blocked signaling. Incorrect implementations: None identified. Both selectors emit active before invocation and inactive in API floor: Verified against registry-authenticated Pi 1.1.0, not host 1.3.0: Summary: One scope finding; no demonstrated blocked-event correctness defect. Audit appendixReviewed range: Sources actually read
Commands/results
Coverage/skips: No full gates or Nix builds run; parent owns those checks. No live Herdr pane, interactive TUI, or RPC end-to-end test. Historical planning artifacts were unnecessary. No forbidden reports/history read, mutations, comments, or report files written. Coordinator validation auditAll commands below used /home/pat/Desktop/workspace/personal/issue-84 as cwd unless an explicit -C/absolute path or gh --repo was supplied. No gates are accepted solely from PR prose. Fresh commands and results
Accepted existing check evidence and provenance
Coverage, API compatibility and uncertainties
Sources actually read (union; portions noted)Absolute review root R=/home/pat/Desktop/workspace/personal/issue-84. The original axis appendices identify which reviewer read which source; parent additionally inspected production/tests, Nix, README, specs, SDK, CI and registry evidence as recorded above. Clean/source files:
Live sources:
Checks executed the broader discovered gates but their unrelated source files were not manually reviewed. Historical permission archive files and semantic-evidence ADR were not needed/read. No prior judgments/history/recovery artifacts were used. Exact supplemental probe sourcesRun JavaScript from R as the -e argument described above: import assert from "node:assert/strict";
import { createEventBus } from "./node_modules/@earendil-works/pi-coding-agent/dist/core/event-bus.js";
import * as mod from "./extensions/permission/index.ts";
const permission = typeof mod.default === "function" ? mod.default : mod.default.default;
for (const command of ["git push origin main", "echo hello"]) {
for (const outcome of ["cancel", "reject"]) {
const events = createEventBus(), seen = [], handlers = new Map();
events.on("herdr:blocked", data => { seen.push(data); });
let resolve, reject;
const selection = new Promise((yes, no) => { resolve=yes; reject=no; });
const pi = {events, registerFlag(){}, getFlag(){return false;},
on(name,handler){handlers.set(name,handler);}, registerCommand(){}};
const ctx = {hasUI:true, ui:{select(){assert.equal(seen.length,1); return selection;}}};
permission(pi);
const pending=handlers.get("tool_call")({toolName:"bash", input:{command}}, ctx);
await Promise.resolve();
assert.deepEqual(seen,[{active:true,label:"permission: "+command}]);
if(outcome==="cancel"){resolve(undefined); assert.equal((await pending).block,true);}
else {reject(new Error("asynchronous abort")); await assert.rejects(pending,/asynchronous abort/);}
assert.deepEqual(seen,[{active:true,label:"permission: "+command},{active:false}]);
console.log("PASS",command,outcome,"active during wait; exact release");
}
}
const empty=createEventBus(); empty.emit("herdr:blocked",{active:true}); empty.emit("herdr:blocked",{active:false});
console.log("PASS no-subscriber emits");Run Python as python3 -c with this script: import base64,hashlib,io,json,pathlib,tarfile,urllib.request
root=pathlib.Path("/home/pat/Desktop/workspace/personal/issue-84")
key="node_modules/@earendil-works/pi-coding-agent"
lock=json.loads((root/"package-lock.json").read_text())["packages"][key]
assert lock["version"]=="1.1.0"
blob=urllib.request.urlopen(lock["resolved"],timeout=30).read()
algorithm,digest=lock["integrity"].split("-",1)
assert base64.b64encode(hashlib.new(algorithm,blob).digest()).decode()==digest
print("Registry tarball matches package-lock SRI:",lock["resolved"])
sdk=root/key
assert json.loads((sdk/"package.json").read_text())["version"]=="1.1.0"
files=["dist/core/extensions/types.d.ts","dist/core/extensions/loader.js","dist/core/event-bus.js","dist/core/event-bus.d.ts","dist/core/agent-session.js","dist/core/agent-session-runtime.js"]
with tarfile.open(fileobj=io.BytesIO(blob),mode="r:gz") as tar:
for name in files:
published=tar.extractfile("package/"+name).read()
installed=(sdk/name).read_bytes()
assert published==installed,name
print(hashlib.sha256(published).hexdigest(),name)
print("PASS: six installed SDK source files match SRI-authenticated Pi 1.1.0 tarball")Artifacts and axis-only summary
Standards: 0 findings, no worst issue. Spec: 1 finding, worst within axis P2 audio-removal scope/authorization gap. No cross-axis reranking. Complete original independent judgment (verbatim; full finding ledger and validation)PR #85 — independent judgment, round 1DecisionEscalate for one precise product-intent decision; do not dispatch an autonomous repair. The blocked-event implementation has no evidenced correctness defect. The audio removal has a concrete compatibility consequence, not merely extra scope: a supported non-Herdr user loses the existing prompt and natural-completion attention notifications. Herdr blocked events do not replace either notification outside Herdr and do not themselves replace the completion bell anywhere. F-001: significant bug / supported-behavior regression relative to the established baseline; disposition: awaiting human input. It is not classified as a blocker simply because issue #84 did not request it. Restoration would override an explicitly intentional, related product change in the PR. Neither restoring audio nor accepting its removal is an autonomous repair decision here. Required question: Do you approve PR #85 intentionally removing all permission-extension audio, including both permission-prompt bells and the natural, non-aborted Counts: 0 blockers; 1 significant bug awaiting human input; 0 report-only; 0 refuted; 0 unresolved technical investigations. There is 1 unresolved product-intent/acceptance question, so the run cannot be called verified. Autonomous actionable queue: empty. Decision-gated queue: F-001. Repairs used: 0/10. Identity, guards, and operation state
Review acceptance and independent inspectionThe original axes remain separate: Standards reported 0 breaches/actionable smells; Spec reported one P2 audio-removal scope concern and no missing/incorrect blocked-event requirement. No axis was skipped. The reports are complete for the agreed full diff; preserved originals are Independently inspected the actual seven-file diff, full current permission implementation and new 353-line tests, pinned-base permission implementation, base instructions, relevant current README and specs, SDK EventBus implementation/declaration, workflow, actual CI-log excerpts and permission derivation. Current code at No additional finding is established for the local helper, normalization/truncation/fallback label choices, unconditional EventBus calls, or packaging cleanup. These are legitimate implementation choices, not automatic scope blockers. The Pi 1.1.0 floor evidence supports EventBus availability; optional chaining or a dependency-floor bump is not necessary on the accepted evidence. Missing live Herdr/TUI execution remains a disclosed coverage limitation, not a newly invented mandatory gate. Full finding ledgerF-001 — intentional audio removal changes supported attention behaviorProvenance: Spec only, original finding “P2: Bell/audio removal exceeds issue #84’s requested behavior,” Category: Significant bug / supported-contract deviation relative to the established baseline, with acceptance dependent on product intent. Not a security issue, permission bypass, build failure, or automatically a blocker. Not a mere smell or cosmetic preference. Established evidence:
Concrete trigger and impact: In a normal supported Pi UI session without Herdr's integration, with Threshold rationale: The material issue is removal of a documented user-facing behavior in an otherwise supported configuration, proved by complete before/after code paths and packaging—not simply “not requested by the issue.” A label-formatting improvement would not meet this threshold. Nor is there an explicit blanket prohibition on related implementation changes. The deliberate removal may be acceptable product evolution, but the evidence does not settle acceptance of silent operation outside Herdr or loss of completion audio. That consequential public-behavior choice needs the user's answer; it must not be downgraded merely because reversal is risky, nor repaired on the assumption that every extra change is wrong. Correction / dependency / risk: No autonomous correction authorized. If preservation is selected, retain the new Disposition: Awaiting human input. Priority: first and only decision-gated item. Not safe to reserve/dispatch a repair until the answer supplies its scope. Current resolution evidence: At reviewed head Queues and reconciliation
Checks and acceptanceRequired automated evidence is accepted as passing for the reviewed head. No full rerun by this judge was necessary or performed. Fresh executions below belong to the review coordinator/Spec worker, not to this judge; CI and Nix realization are distinctly cached/source-bound evidence.
No failed, pending, unknown or unavailable required automated gate remains on accepted evidence. A legacy pending status with zero statuses is not an additional required check. The only required branch check shown by policy evidence is successful CI. The missing product answer is not repairable by rerunning automation. Limits, publication and next transition
Final judge result: escalated — F-001 awaits explicit product-intent acceptance; automated validation accepted, blocked-event feature has no demonstrated bug, no autonomous repair authorized, 0/10 attempts used. |
Verified — no repair requiredF-001 is refuted, not repaired. The user's explicit answer, “yes that is the intention,” approves removing all permission-extension audio: prompt bells and natural, non-aborted completion bells, inside and outside Herdr. No further product decision remains. Findings: 0 actionable (0 blockers, 0 significant bugs) · 0 report-only · 0 investigations · 1 refuted. Validation: accepted evidence reused for unchanged sources: local format/lint/typecheck passed; 410 tests passed, 1 skipped. Nix acceptance uses cached/source-equivalent required CI: 401 tests passed, 10 skipped. CI remains successful; identity/status were freshly checked, not full gates rerun. Audit below explains complementary skips. Scope: full seven-file PR diff reviewed by both axes. No live Herdr, interactive TUI/RPC, or real audio end-to-end testing was performed. Reviewed/current head: Full current ledger, accepted resume judgment, original reports, and publication auditComplete original two-axis audit — before the later intent answerThe complete original Standards and Spec reports, review handoff, original independent judgment/ledger, and validation evidence are preserved verbatim in the prior append-only escalation: This durable link is used under the report contract's explicit permission for previously published complete originals. Both originals covered the full PR diff; neither axis was skipped. Standards originally reported zero findings; Spec originally raised the one audio-removal concern. Those are historical reports before the user's later answer, not a current unresolved finding. Publication rechecked full-content containment of all four original artifacts in that comment; all matched. The original escalation remains unchanged. Complete accepted CURRENT resume judgment and ledger (verbatim)The following is the full accepted read-only-stage artifact, including every F-001 ledger field, validation provenance, identity, and limitations. Its stage-time statements that final publication was forbidden/outstanding describe that earlier read-only task. Only those publication-state statements are superseded by the user's subsequent final-publication authorization and the final publication audit below. The finding is refuted through changed accepted intent, never repaired; no new review round or repair budget is created. PR #85 — read-only resume reconciliation and final judgmentDecisionTechnical review/judgment: verified for the agreed full PR diff, with the previously disclosed runtime limits. No repair is required. F-001 is refuted by explicitly accepted changed requirements/product intent, not repaired. The user's answer, “yes that is the intention”, explicitly accepts retiring ALL permission-extension audio: permission-prompt bells and natural, non-aborted completion bells, inside AND outside Herdr. This is not permission for narrower review coverage, not an instruction to restore audio, and not authorization of a new repair. End-to-end orchestration completion: not yet recorded. All substantive review/judgment/automated-check gates are satisfied; the policy's final append-only PR decision/publication receipt is still outstanding. This request expressly forbids commenting, so this judge has not published the newly resolved decision. The existing verified comment is the earlier escalation, NOT a final verified decision: #85 (comment) . No publication failure occurred; publication was deliberately not attempted. Do not present that escalation link as a receipt for this new judgment. Current counts: 0 blockers; 0 significant bugs; 0 report-only; 1 refuted (F-001); 0 unresolved technical investigations; 0 unresolved product decisions; 0 repaired-and-verified findings. Autonomous and decision-gated repair queues are empty. Repairs used 0/10. No further product question remains. Sticky identity and source reconciliation
Completion boundary recheck at 2026-10-21T15:31:36Z again confirmed clean porcelain, expected HEAD/merge-base/live main/head/pull refs and CI pass. A subsequent full paginated PR-comment recheck compared byte-for-byte after JSON parsing with the initial resume history: identical complete contents. An initial attempt to combine gh No unexpected external source/ref/history/policy/check change was found. No fetch, reset, checkout, ref overwrite or force operation was used. Source-bound accepted evidence can be reused without a new two-axis review or rerunning the whole gate suite. Complete history and publication reconciliationFetched all six endpoints with
The fetched published comment has 50,411 characters, one run marker: Durable complete original reports/ledger/evidence: #85 (comment) . Original stage-time “not posted” statements remain historical statements, superseded only by the publication audit in that same comment. This resume likewise does not rewrite them. Raw resume history evidence was saved under Review acceptance and independent bounded inspectionRead policy Independently read the entire current
Conclusion: prompt and completion audio are absent unconditionally, inside and outside Herdr, exactly matching the newly accepted intent. This is permission-extension behavior only; no claim that Pi itself, other extensions, external listeners or the OS can never produce sounds. Herdr blocked-state notifications are not falsely described as a completion-audio replacement. Full stable finding ledgerF-001 — intentional retirement of permission attention audio
Every prior actionable ID is accounted for: F-001 is the sole prior ID, now resolved as above. No new ID, unresolved investigation, report-only queue or decision-gated queue remains. Original axis counts remain historical (Standards 0 / Spec 1); the current curated ledger has 1 refuted and no actionable findings. Required checks and provenanceNo full gates, focused test execution or fresh Nix build was run during this resume. The following earlier accepted evidence remains bound to the exact unchanged source; fresh work here was git/API/history reconciliation and code inspection, not new execution of these gates.
There is no failed, pending, unknown or unavailable required automated check on the accepted evidence. The previously noted legacy pending aggregate with zero status contexts is not an extra required gate. No new dependency/source change invalidates the cached evidence. Coverage limits (unchanged, not a narrowed authorization)
Completion gates and exact operation state
Exact state: reviewed/local/existing remote head all remain Next permitted orchestration transition, outside this read-only task: accept this judgment, recheck boundary refs/history, append a final no-repair PR decision recording the user's exact all-audio acceptance, F-001's refuted disposition, unchanged-source check provenance and runtime limits, preserve/link the already durable complete originals, and verify the new comment receipt. Do not edit the earlier escalation or create a repair-start. Do not rerun a full fresh review/gates merely to record acceptance when source remains unchanged. Questions: none about product intent, repair scope, accounting or code correctness. Only the final publication operation remains for the authorized orchestrator; this task does not grant comment permission. Until that receipt exists, do not claim the entire policy workflow, including final history, is finished. Final publication audit — round 1 resumed, no repairAuthority and acceptance
Identity and safe publication boundary
History and duplicate prevention
Validation provenance and limitations
Delivery and exact operation state
|
Summary
herdr:blockedevent pair around both permission confirmation prompts ({ active: true, label }before the prompt,{ active: false }in afinally), using apermission: <command>label. Herdr panes now reportblockedwhile Pi waits for a human instead of looking idle. The events are unconditional and a no-op without Herdr's Pi integration (v9).playBell,PI_NO_BELL, the vendoredmessage.oga, and theagent_endfinish-bell handler — now that Herdr owns the attention signal.Tests
extensions/permission/index.test.tscovers exact pairing (including a throwing prompt), no-emit paths (allow/deny rules, YOLO toggle and pin, sandbox, headless), label formatting/truncation/empty-command fallback, and the remaining registration surface.biome check,tsc --noEmit,node tests/run.mjs, andnix flake checkpass.Closes #84