Skip to content

feat(boatstack): repeated denials escalate their solutions, never their severity - #228

Merged
bigboateng merged 1 commit into
mainfrom
feat/denial-escalation
Jul 27, 2026
Merged

feat(boatstack): repeated denials escalate their solutions, never their severity#228
bigboateng merged 1 commit into
mainfrom
feat/denial-escalation

Conversation

@bigboateng

Copy link
Copy Markdown
Contributor

What

Builds on #226/#227. An agent that hits the same guardrail repeatedly is signalling that the stated law is not reaching it — and under that pressure agents drift toward worse moves, not better ones (observed live: sixteen identical no-progress repair attempts ending in a protected-boundary write attempt). The guard now keeps a bounded per-worktree denial ledger; from the third identical denial (same category at the same stage) the message escalates its corrective information: the You can: pick list lifts from 3 commands to the full legal set, and the denial prescribes a fresh diagnostic (boatstack-helper doctor) — the fresh-probe discipline. Structured payload gains escalated + repeat_count.

How

  • denial_ledger.go: .git/boatstack/guard/denials.json — keyed (category, workflow_stage), bounded (≤32 keys, oldest pruned), fail-calm end to end (corrupt/unwritable ledger ⇒ unescalated rendering; a denial never becomes a crash or an allow because bookkeeping failed).
  • safety.go: HookDecision bumps the ledger on every deny and resets it on any allowed mutation-capable call (forward progress; read-only allows do not reset). SafetyFinding.RepeatCount carries the count to the renderer.
  • denial.go: escalation lifts the pick cap and appends the repeat notice in all three render modes.
  • statemap.go/paths.go: new guard-denial-ledger entry (runtime-worktree, per-worktree, gitignored, guard-protected — already inside the deliveryStatePathPattern deny set) + GuardDir() resolver; mirrored in references/artifacts.md and held by the existing ownership conformance.

Boundary conformance

  • Boundary: guard deny/allow path → per-worktree runtime state (.git/boatstack/guard/).
  • Control law: repeated-denials-escalate-to-solutions.
  • Authorized actor: the safety hook itself (safety-hook/ambient-safety-hook); raw mutation of the ledger is guard-denied like all .git/boatstack state.
  • Required evidence: denial_escalation_conformance_test.go — third-identical escalates end to end through HookDecision; allowed-mutation reset; key isolation (category and stage); corrupt-ledger and unresolvable-repo degradation; ledger bound; escalation-lifts-cap-not-severity (option SET identical, severity identical). Statemap conformance green with the new entry; ownership doc mirror updated.
  • Failure behavior: ledger unreadable/unwritable ⇒ denial renders unescalated (fail-calm, never fail-open).
  • Classification: optimization — never changes what is denied or allowed; the constitutional corpus floor is untouched by construction (full corpus green).

Verification

  • go build ./... && go vet ./... clean; full go test ./... green across all three packages.
  • Release note: 2026-07-27-repeated-denials-escalate.md; preflight passes.

…ir severity

An agent that hits the same guardrail repeatedly is signalling that the
stated law is not reaching it, and under that pressure agents drift toward
worse moves. The guard now keeps a bounded per-worktree denial ledger
(.git/boatstack/guard/denials.json, declared in the state-ownership map,
guard-protected like all runtime state). From the third identical denial
(category at stage) the rendering escalates its corrective information:
the pick list lifts from three commands to the full legal set and the
denial prescribes a fresh diagnostic (boatstack-helper doctor) — the
fresh-probe discipline. Admissibility never changes: what was denied
stays denied, and the constitutional corpus floor is untouched.

Any ALLOWED mutation-capable call is forward progress and clears the
ledger, so a stale streak never escalates an unrelated denial; read-only
allows do not reset. Bookkeeping is fail-calm end to end: a corrupt or
unwritable ledger degrades to the unescalated rendering — a denial never
becomes a crash or an allow because bookkeeping failed.

Conformance (denial_escalation_conformance_test.go): third-identical
escalates end to end through HookDecision, allowed-mutation reset,
key isolation (category and stage), corrupt-ledger and unresolvable-repo
degradation, ledger bound, and escalation-lifts-cap-not-severity. The
statemap gains the guard-denial-ledger entry + GuardDir resolver, held by
the existing ownership conformance and mirrored in artifacts.md.

control-law: repeated-denials-escalate-to-solutions

Disclosure-Reviewed: reviewed — public-safe only, private facet kept out of this commit
@bigboateng
bigboateng merged commit 6ec9782 into main Jul 27, 2026
12 checks passed
@bigboateng
bigboateng deleted the feat/denial-escalation branch July 27, 2026 21:56
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.

1 participant