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 ¶
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.
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.
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).
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 ¶
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.
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).
const ( Accept Policy = protocol.InboundAccept Hold Policy = protocol.InboundHold Refuse Policy = protocol.InboundRefuse )
The three policies.
func Effective ¶
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.
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 ¶
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 ¶
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 "".