policy

package
v0.6.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 14, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package policy decides the inbound policy a session runs under — the step 3 of plan 6.8, with the native `crossSessionInbound` scan of 6.10 folded in — and is the ONLY implementation of that decision: the SessionStart hook (P3-4) calls it to fill the by-pid map's `inbound`, the prompt-hook poll and the watcher's pipeline (internal/harness/ inbound) run under the value it produced.

D18 settles the shape: the policy is `accept`, `hold` or `refuse` and NOTHING else — there is no `auto` shape in any phase. `accept` is the default in every permission mode and entrypoint — bypassPermissions, auto, `claude -p` workers included — so a team message is injected immediately and nothing waits for a human. `refuse` and `hold` are the opt-ins (the `team_inbound` option): under `refuse` nothing is injected and nothing acknowledged; under `hold` (P5-9) each message is recorded in the session's pending file, injected only after the human releases it from a terminal (`brigade inbox release`), and acknowledged only then. `refuse` also applies whenever the option asked for something this harness cannot honour (an unknown spelling fails closed) and whenever the native settings scan found a `crossSessionInbound` of `hold` or `refuse` — even over an option of `hold`. The scan is load-bearing, not defensive (E0-9): under a native `refuse` a socket post SUCCEEDS and the frame vanishes, and under a native `hold` it is held natively, never expires and is lost at session end, so a Brigade `accept` or a released `hold` there would acknowledge messages Claude never saw — the message gone AND the sender told it arrived. Refusing to deliver into a session that cannot receive is strictly better than pretending to. `permission_mode`, `non_interactive` and the entrypoint are recorded for diagnostics only; Decide echoes them and a test table proves they never touch the outcome.

Index

Constants

View Source
const (
	NativeHold   = protocol.InboundHold
	NativeRefuse = protocol.InboundRefuse
)

The two native values that flip Brigade to Refuse (6.10, E0-9). A native `accept`, or any other value, is not a hit.

View Source
const MaxSettingsBytes = 1 << 20

MaxSettingsBytes bounds what the scan will parse of one settings file; a larger file is treated as malformed (none), because a settings file is a few KiB and the scan is best effort.

View Source
const SettingKey = "crossSessionInbound"

SettingKey is the native Claude Code setting the scan looks for, at the TOP LEVEL of a settings file only (6.10).

View Source
const WarnOptionUnknown = `Brigade: the team_inbound option could not be interpreted; the inbound policy is refuse — nothing is acknowledged blind.`

WarnOptionUnknown is attached when the option value handed to Effective is none of Accept, Hold and Refuse — a caller bug (config.ParseInbound never yields anything else) that fails closed rather than open.

Variables

This section is empty.

Functions

func SettingsFiles

func SettingsFiles(claudeConfigDir, cwd string) []string

SettingsFiles names the three files the scan reads, most specific first (the native precedence, so a hit in the local file is reported over one in the project file over one in the user file): <cwd>/.claude/ settings.local.json, <cwd>/.claude/settings.json, <claudeConfigDir>/ settings.json. An empty cwd skips the two project files; an empty claudeConfigDir skips the user file.

Types

type Decision

type Decision struct {
	// Policy is the effective inbound policy.
	Policy Policy
	// Warnings are the fixed-text lines the hook prints as context, in
	// order: the option's, then the native scan's.
	Warnings []string
	// The diagnostics, echoed from Inputs unchanged.
	PermissionMode string
	NonInteractive bool
	Entrypoint     string
}

A Decision is the policy plus the warnings the hook prints and the diagnostics it records.

func Decide

func Decide(in Inputs) Decision

Decide is Effective over Inputs, with the diagnostics carried through.

type Inputs

type Inputs struct {
	// Option is the policy the team_inbound option asks for, as
	// config.ParseOptions or config.FromWatcherEnv resolved it.
	Option config.Inbound
	// OptionWarning is config.Options.TeamInboundWarning (or the watcher
	// env's), "" when the option was accept, hold or refuse.
	OptionWarning string
	// Native is the result of ScanNative.
	Native Scan
	// PermissionMode is the hook stdin's permission_mode; diagnostics only.
	PermissionMode string
	// NonInteractive is true for a `claude -p` session; diagnostics only.
	NonInteractive bool
	// Entrypoint is CLAUDE_CODE_ENTRYPOINT (cli, sdk-cli); diagnostics only.
	Entrypoint string
}

Inputs are everything the hook knows when it decides the policy. Only Option, OptionWarning and Native are consulted; PermissionMode, NonInteractive and Entrypoint are diagnostics (6.5) that Decide copies into the Decision so the caller records them beside the policy, and a test table proves they cannot change it (D18: no `auto` shape).

type Policy

type Policy string

A Policy is the effective inbound policy: Accept, Hold or Refuse (D18).

The three policies.

func Effective

func Effective(option config.Inbound, optionWarning string, native Scan) (Policy, []string)

Effective computes the policy (6.8 step 3, 6.10): the option's own value when it is accept, hold or refuse; Refuse when the option was invalid (optionWarning is the config.ParseInbound warning that says so, passed through unchanged so the hook prints it); and Refuse — whatever the option said, `hold` included — when the native scan found `hold` or `refuse` (the scan's own warning is appended: Brigade's release path ends in a socket post, and under a native hold or refuse that post is lost while Brigade would acknowledge it, 3.6). The warnings come back in that order, each a fixed text that never echoes a setting value the user did not choose from the known ones.

func (Policy) String

func (p Policy) String() string

String renders the policy as the `inbound` wire value (4.4.2) and as the SessionStart context line prints it.

func (Policy) Valid

func (p Policy) Valid() bool

Valid reports whether p is one of the three policies.

type Scan

type Scan struct {
	// Found is true when Value is NativeHold or NativeRefuse.
	Found bool
	// Value is the native value found, "" when none.
	Value string
	// File is the settings file the value came from (the most specific
	// file in native precedence order when several carry one).
	File string
	// Checked lists every file the scan looked at, in the order it looked,
	// for diagnostics.
	Checked []string
}

A Scan is the result of ScanNative: whether a native `hold` or `refuse` was found, which value and in which file. The zero Scan means none.

func ScanNative

func ScanNative(claudeConfigDir, cwd string, readFile func(string) ([]byte, error)) Scan

ScanNative reads, best effort, the three settings files of SettingsFiles for a top-level `crossSessionInbound` of `hold` or `refuse` (6.10). readFile is injected (nil means os.ReadFile) so tests never touch a real config directory. A missing, unreadable, over-large or malformed file, a value that is not a string, a string other than the two, and a `crossSessionInbound` nested under another key are all NOT hits; any `hold` or `refuse` in any of the three files IS one, whatever the other files say — the documentation says a project or local `refuse` "applies over every other source", and this scan cannot model the native precedence exactly, so it fails closed: a hit anywhere makes Brigade refuse, and the warning names the file so the user can fix it.

What the scan CANNOT see, and the hook's warning must not pretend to: managed settings (a policy file outside these paths), a `--settings` file or JSON on the command line, and any value Claude Code computes itself. A native `hold` or `refuse` from those sources makes `injected` a lie the plugin can only document (6.10, E2E-03).

func (Scan) Warning

func (s Scan) Warning() string

Warning renders the context line the hook prints for a hit (6.10): it names the setting, the value (one of the two known strings, never free text) and the file, says what Brigade did about it, names Brigade's own `hold` as the review option once the native setting is gone (P5-9, 3.6: the native setting must be "accept" for a release to be delivered), and points at `brigade inbox`, which lists a refusing session's waiting messages without acknowledging anything. For a Scan that found nothing it returns "".

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL