Skip to content

Authorization Contract v2 ​

Normative rules for event actors, entity CEL facts, and model-bound records.

This version keeps a triage platform floor for humans, including the narrow verified label-transition grant below, and applies the selected harness's effective human_min_role to prompting events, actor-originated facts needed for actionable CEL results, and Fullsend-controlled model-bound records. Harness CEL may impose further conditions but cannot replace these admission gates. ADR 0151 establishes the floor and harness minimum while preserving separate bot grants and fresh model-bound checkpoints. This version also adopts selective entity acquisition and decision-focused audit evidence for the future entity-first path under ADR 0147, with bounded CEL actor-verdict reuse under ADR 0148. Existing dispatch continues to use v1 until migrated. The historical decision and rationale are recorded in ADR 0054. The NormalizedEvent v1 specification defines the actor.role, actor.kind, actor.role_verified, and actor.bot_role fields consumed by this contract. Bot identity and role resolution are specified by ADR 0107.

ADR 0107 target contract — not yet implemented: The bot identity fields, resolver outcomes, and bot-specific fail-closed rows below describe the behavior to be implemented by the adapters and dispatch path. Until that migration lands, existing compatibility behavior remains authoritative.

Role ordering ​

Fullsend uses a forge-neutral role hierarchy. Roles are listed from most to least privileged:

admin > maintain > write > triage > read > none > external
RoleMeaning
adminFull repository control
maintainRepository settings without destructive admin
writePush, label, comment, merge
triageLabel and moderate without push access
readRead-only collaborator
noneAuthenticated user without explicit repository permission
externalActor outside the repository or project (currently Jira-only; GitHub maps non-collaborators to read on public repositories or none; GitLab maps them to none)

Forge-native permission mapping ​

ForgeNative levelFullsend role
GitHubadminadmin
maintainmaintain
writewrite
triagetriage
readread
(no collaborator entry)none
(fork/non-collaborator)read (public repository) or none
GitLabOwneradmin
Maintainermaintain
Developerwrite
Reportertriage
Guestread
JiraAdministratorsadmin
Developerswrite
(other named role)read
(no project membership)external

On GitHub the mapping source is the collaborator permission API (GET /repos/{owner}/{repo}/collaborators/{username}/permission), which returns the user's effective role including inherited org grants regardless of membership visibility. A built-in role_name is used as is. Custom repository roles are an organization-level GitHub Enterprise Cloud feature, so most repositories only ever return built-in names; Fullsend does not interpret a custom role's name or its fine-grained permissions. A custom repository role resolves to the strongest base role set to true in user.permissions (admin, maintain, push → write, triage, pull → read), or none if no flag is set. Only when user.permissions is absent or null does the legacy permission field (admin, write, read) apply; anything else is none. Legacy read stays read, because it cannot distinguish Read from Triage. Fork authors and non-collaborators are mapped to read (public repositories) or none, never external. The external role is currently produced only by the Jira adapter for actors without project membership. The platform human floor denies read, none, and external alike; a selected harness may require more than the floor. The author_association field is not used because it does not correctly reflect private org membership (see Excluded fields).

A GitHub repository that lists owners_file under authorization in .fullsend/config.yaml consults its repo-root Prow OWNERS file before the collaborator API:

OWNERS listRole granted
approversat least write
reviewersat least triage
(not listed)none; the collaborator API decides

OWNERS can only raise a role, never lower one. An entry that names an OWNERS_ALIASES key stands for that alias's members. A login equal to any alias key never matches, and nested aliases are not expanded. The files are read from the base branch for PR-scoped events and from the default branch otherwise, so a PR cannot add its author. See the authorization field.

On Jira the mapping source is the project's role membership roster for the configured --jira-project, resolved once per poll cycle for event-backed dispatch. Entity-source CEL may reuse a completed provider-verified actor verdict across poll cycles and bound run checks only under the bounded cache rules below; event admission does not use that cache. An actor is matched by Jira account ID. Role names are matched case-insensitively against the names above; unrecognized role names fall through to read. Actors with no membership in the configured project — or whose issue belongs to a different project — are mapped to external (fail-closed). See the Jira integration guide for details on the name-based matching limitation.

Bot identity fields ​

The adapter/provider MUST classify the verified actor using authoritative source-system metadata before authorization. This may include a provider actor name when that forge gives it bot-specific semantics, such as GitHub's [bot] logins; labels, review types, and arbitrary event-content strings are not bot identity signals. For post-migration producers with actor.kind: bot, actor.role MUST be none, and actor.role_verified is true exactly when the provider completes the bot-role lookup, including a successful no-match result; it is false when resolution fails. For actor.kind: human, actor.bot_role MUST be absent or null, and actor.role contains the forge permission role when actor.role_verified is true. For non-label dispatch, only a non-null, provider-resolved actor.bot_role can pass the bot gate. The human triage floor does not apply to a recognized bot; platform authorization instead requires a valid normalized event, an applicable source and target, and a transition supported by the selected harness. CEL may further restrict routing but cannot create or broaden bot identity authorization.

For humans on all event paths, including a verified label addition, role_verified: false makes actor.role unusable as a forge-permission claim. It does not invalidate an independently verified configured-source grant such as OWNERS for the same verified actor and target. An exact provider-proven human label addition may instead use its narrow triage grant for a harness whose minimum is triage; stronger minima require a separately verified effective role. The versioned CEL input MUST carry a verified effective role and its source when present, and MUST distinguish a label grant from role evidence without reinterpreting v1 actor.role_verified. Missing role_verified on a trusted pre-migration event is treated as legacy input and retains the current human authorization behavior; new adapters MUST emit the field. Legacy status is determined by the adapter implementation contract, not by guessing from this field's absence. An accepted forge label mutation supplies only the transition-specific human triage grant, not a verified repository role. For a bot using its separate narrow label grant, omission means that bot-role lookup is not applicable, not that it failed.

Authorization-sensitive CEL role comparisons in v2 MUST use the versioned verified effective-role input. The legacy event.actor.role value is not a verified permission claim when actor.role_verified is false, even if a narrow label grant admits the event. A harness that relies on a legacy role comparison for a stronger condition MUST update its predicate for the versioned input before using v2 routing; it cannot interpret the grant as that stronger role. The same rule applies to an overlay when: that selects human_min_role.

Platform floor and per-harness human minimum ​

Fullsend MUST require a verified effective normalized human role of at least triage as a platform floor for ordinary human actors, with freshness specified separately for each admission path below. The exact accepted human label addition and its proven projection may instead use the narrow, provider-proven triage grant below; it is not a general role claim. For each selected harness, Fullsend MUST resolve and enforce that harness's effective human_min_role before its CEL predicate runs. An effective role comes from the source provider or a trusted configured permission source such as OWNERS. An explicit human_min_role at the top level or in a guarded overlay MUST be one of triage, write, maintain, or admin. Every explicit value, including one in an overlay that does not match, MUST be validated. An invalid value or unverifiable harness revision denies evaluation; it MUST NOT use the default. An explicitly empty value is invalid rather than absent. Within each base layer, matching overlays apply in order, with later matches winning, before that layer merges into its child. An explicit child scalar overrides the inherited value, then the child's matching overlays apply. When no explicit value survives this resolution, the resolved harness role supplies this default:

Resolved harness roleDefault human_min_role
triage or reviewtriage
Any other role, including a custom or unrecognized rolewrite

An explicit value may lower a write default to triage or require maintain or admin, but it cannot lower the platform floor. The minimum is resolved under that trusted composition and overlay order before the harness-specific actor gate or trigger CEL. An overlay that sets this field MUST pass ADR 0112's guarded-field dependency validation: its when: may read only the normalized event specification's trusted event or entity fields, platform-verified runtime.forge, and config from the same pinned trusted revision as the harness, plus the versioned verified effective-role input. That input is trusted for this guard only when its identity, target, source, and role are independently verified. Guard inputs and their provenance MUST be available without using actor-filtered entity facts, model content, or a previous admission decision under the minimum being selected. A role-based guard MUST use the versioned verified effective-role input. Raw event.actor.role is excluded for this field even though v1's trusted-field list permits it, and the narrow human label grant is not a role. The versioned CEL input schema MUST specify the verified role's exact path and provenance before role-based overlays can be enabled. Trigger CEL and deprecated forge: blocks cannot set the field. Harness role selects the agent credential identity (the mint role on GitHub); privilege_levels scopes tokens for run stages. Neither token permissions nor workflow stage names infer a different human threshold. The default table is an authorization policy, not a claim about the capabilities of every harness using a role.

ADR 0112's trusted-field check is a dependency check, not proof that the matching value is safe. For example, a change author can choose a fork, but only a writer can make is_fork false. A harness that conditionally lowers a stricter top-level minimum SHOULD have a when: that positively establishes the trusted condition for which the lower minimum is intended. An unconditional lower value belongs at the top level. The final selected minimum applies to the prompting actor and to other actors' decision-relevant facts and model-bound records for the entire evaluation cycle.

A known absent event or optional key is a nonmatch under ADR 0112. If required verified guard metadata is unavailable, or the CEL evaluation has another error, Fullsend MUST fail the evaluation rather than treating the error as a nonmatch and using a possibly weaker default. All processes that make an admission decision MUST use the same verified guard context and ordered overlay result; dispatch cannot resolve the guard without an event and let run resolve it again with one. If a guard needs entity.source.system, Fullsend MUST obtain only that provider-verified structural metadata from the event or candidate identity, or with a platform discovery credential independent of the still-unpinned harness run credential. It MUST NOT acquire actor-originated entity facts for this purpose.

The agent registry and local child and base harness files that determine role or human_min_role MUST come from one actor-independent, trusted checkout pinned to an immutable revision. For a change-proposal event, that is the provider-verified target/base commit, never the proposed head; for other events, it is a trusted repository revision selected and pinned by the platform. An actor-supplied ref, CEL input, proposed head checkout, or mutable target working tree MUST NOT select or replace these policy files or their revision. A remote registered harness or base layer MUST be selected through that trusted registry and base chain, pass a trusted remote-resource allowlist, and have its content verified against a declared hash before contributing role or human_min_role. A hash or allowlist match alone does not authorize an actor-supplied remote source. Configured actor-permission sources retain their separate trusted-ref and freshness requirements below.

human_min_role constrains human actors and their records only. A provider-classified bot using the exact verified label handoff or a bot with an exact registered role uses its separate gate, even when the selected harness has a maintain or admin human minimum. Harness CEL and bot policy may narrow those bot paths; the human minimum does not serve as bot authorization or deny an otherwise authorized bot.

The same effective minimum applies independently to a human prompting event, actor-originated entity facts whose authority is needed to make CEL actionable or prove an actionable absence or aggregate, and every Fullsend-controlled model-bound record. Event admission uses fresh authority. Entity CEL may use a live or eligible bounded actor verdict under the rules below; model-bound records receive their own fresh checks at the required exposure checkpoints. Raw state used only for conservative deferral follows the causal classification below and does not require authorizing every historical updater. For ordinary role-based admission, read, none, external, and unresolved human roles do not pass; only the exact verified human label transition and its proven projection may use the narrow triage grant. Admission of one event does not authorize another actor's fact or model-bound record. Trusted Fullsend invocation provenance, rather than a human event actor, authorizes eventless entity discovery; decision-relevant actor-originated facts still pass the selected harness's minimum.

A harness evaluation and any sandbox it starts MUST use one trusted composed harness revision, resolved role, and effective human_min_role. Fullsend MUST pin that policy before the first harness-specific actor gate and retain it through event admission, entity CEL, snapshot and model-record checks, and later Fullsend-controlled retrieval. Its policy identity MUST bind the trusted checkout revision, registry and composed harness content digest, trusted remote-resource allowlist revision or digest, verified remote-layer hashes, resolved role, ordered overlay matches and overrides, verified guard inputs and provenance, effective minimum, and its top-level, overlay, or default source. When dispatch, execution, or retrieval use separate processes, the handoff MUST preserve this identity with integrity. The receiver MUST verify the pin against its loaded policy before minting or using harness credentials and before admitting content. Passing only an agent name or credential role is insufficient. If the policy changes mid-cycle, Fullsend MUST either retain the original verified pin for the whole cycle or stop and re-evaluate from the first gate under a new trusted revision; it MUST NOT silently mix minima or overlay outcomes. A changed guard result or an expired guard claim requires the same stop or first-gate re-evaluation. If the receiver cannot verify the handoff pin, it MUST stop and MAY restart authorization from the first gate. A new child cycle under ADR 0144 may resolve and record a new pin.

The platform MAY reject a below-floor human event before loading harnesses unless the event has the verified label-transition grant. It MUST enforce each selected harness's effective minimum before evaluating that harness's CEL predicate, including for a verified human label addition. When admission uses a role, the platform MUST expose that same verified effective human role to CEL, including any valid configured OWNERS upgrade. When it uses the narrow label grant, the versioned CEL input MUST expose grant provenance separately and MUST NOT synthesize a verified triage role. Harness CEL MAY add stronger or narrower conditions, but cannot admit an actor or fact that failed the applicable gate. A false CEL predicate is a harness nonmatch, not a platform authorization denial.

For each selected harness, the event-admission audit MUST bind the verified actor identity, effective role and source or narrow label-grant evidence, the policy identity above, and the gate outcome, including denial before CEL. A cross-process handoff records the policy identity sent and verified, or the reason verification failed and authorization restarted or stopped. A bot decision records its registered role or verified label-transition evidence instead of a human role. An early platform-floor denial records the actor evidence and floor outcome.

Current dispatch compatibility ​

The existing GitHub bash dispatcher retains its route-specific human guards and bot compatibility behavior, as recorded in Authorization v1. Those routes do not classify CEL harnesses or set their role requirements. After provider-backed actor.bot_role recognition, bots use the separate bot gate above.

Current Go implementation: Since #8727, fullsend dispatch runs IsEventAuthorized() before harness enumeration, then FilterAuthorizedHarnesses() applies each candidate's role-derived triage or write default before MatchHarnesses(). Jira polling preloads candidates, then runs both authorization checks before CEL. This implements the v1 default for ordinary events. Migration must resolve any explicit or guarded-overlay minimum before that harness's CEL predicate and expose the same verified effective role to CEL. Today Go still lets human label-added events bypass the per-harness threshold; it applies an OWNERS upgrade to its gate but passes the original event role to CEL. The harness loader does not recognize human_min_role and can ignore an unknown YAML key. Adding the field today does not enforce it, including an intended maintain or admin minimum. A v2-conforming runner MUST reject a configured human_min_role it cannot parse, validate, or enforce rather than silently treat it as absent. The field cannot be deployed as effective policy while a selected runner still ignores it. The current execution matrix carries agent name and credential role, not the composed policy identity required for a v2 cross-process handoff.

Fail-closed behavior ​

The human role gate is fail-closed: when the effective role or a harness's effective minimum cannot be determined, that harness's path is denied. The exact verified human label addition may still satisfy a triage minimum with its narrow grant; an unresolved role never satisfies a stronger minimum. The registered-bot and separate bot-label gates use their own provider evidence.

The entity-evaluation rows below specify the future ADR 0098 path as revised by ADR 0147 and ADR 0148 and do not describe behavior currently implemented by fullsend dispatch or fullsend poll.

The human role-lookup rows below apply to every target human event path. The bot-specific rows are ADR 0107 target behavior; legacy events remain subject to the compatibility rules until implementation migration is complete.

ConditionOutcome
Collaborator API returns a custom role_name with no effective permission signalForge role maps to none; a separate verified OWNERS grant may satisfy the selected harness minimum, or an exact verified label addition may satisfy triage; otherwise deny
Collaborator API returns an error or times outForge role is unusable; a separate verified OWNERS grant may satisfy the selected harness minimum, or an exact verified label addition may satisfy triage; otherwise deny
Bot-role lookup returns no registered identity on a non-label pathactor.role remains none; actor.role_verified is true; actor.bot_role is absent; denied
Bot-role lookup fails or is unverifiable on a non-label pathactor.role remains none; actor.role_verified is false; actor.bot_role is absent; denied, with the failure retained in resolver/audit diagnostics
Collaborator API response is not valid JSON or does not match the expected shapeForge role is unusable; a separate verified OWNERS grant may satisfy the selected harness minimum, or an exact verified label addition may satisfy triage; otherwise deny
actor.role is empty or missingEvent fails NormalizedEvent validation; never reaches dispatch
actor.role_verified is false for a human, including a verified label additionIgnore actor.role; require a separate verified configured-source role meeting the selected harness minimum, or the exact human label grant for a triage minimum; otherwise deny
Trusted harness human_min_role is invalid or the resolved harness role or revision is unverifiableDeny that harness; do not silently use the default or evaluate its CEL predicate
An overlay setting human_min_role reads an untrusted or admission-dependent fieldReject that harness configuration before resolution
A human_min_role guard lacks required verified evidence or has a CEL evaluation errorFail the evaluation; do not treat the overlay as a nonmatch or use a fallback minimum
Dispatch, run, or retrieval cannot verify the same ordered overlay outcome and guard contextStop; optionally restart authorization from the first gate with a newly pinned policy
The selected harness policy changes within an evaluation or sandbox cycleRetain the original pinned revision and minimum for the entire cycle, or stop and re-evaluate from the first gate under the new revision; never mix them
Human event actor meets the platform triage floor but not the selected harness minimumDeny that harness before CEL; other harnesses are evaluated under their own minimums
Legacy bot event has an actor.role other than noneValid in the v1 compatibility schema; authorization follows current compatibility behavior during migration and is not an ADR 0107 bot-role authorization result
actor.role_verified is false but actor.bot_role is non-null for a botEvent fails NormalizedEvent validation; never reaches dispatch
actor.bot_role is non-null for a humanEvent fails NormalizedEvent validation; never reaches dispatch
Username is emptyDenied
OWNERS is missing or malformed, or OWNERS_ALIASES is present but malformed (owners_file enabled)OWNERS check skipped; the collaborator API decides
Username contains characters outside [A-Za-z0-9-] (owners_file enabled)OWNERS check skipped; the collaborator API decides
Fullsend invocation provenance for poll discovery or a bound run's eventless check is missing or unverifiableThat eventless evaluation is denied
Harness entity sources are missing or malformedThat harness is skipped for scheduled evaluation
Effective platform eligibility policy is missing, malformed, or unverifiableScheduled evaluations governed by that policy are denied
Effective platform eligibility policy uses a wildcard without explicit platform-level justificationScheduled evaluations governed by that policy are denied
A required entity field or history scope is not yet loaded, or pagination/coverage is incompleteIts value is unknown, never false or empty; read more or defer to bound run; no sandbox starts from an unresolved result
A poll candidate remains inconclusive, cannot be handed off, or its bound run fails before completion or safe deferralRetain a durable retry path or prove source rediscovery after checkpoint advancement; no sandbox starts from an unresolved decision
A required entity source is unsupported, unavailable, fails, or cannot establish complete coverage within allowed boundsEvaluation fails closed; no sandbox starts and the failure is not recorded as a false CEL result
A conservative dependency or provider query cannot be shown to include every authorized fact that could change a definitive CEL resultExpand the plan or leave the result unknown; if it cannot be resolved, evaluation fails closed
Current attribution or required actor-role or exact label-grant evidence for a fact requiring the actor gate is missing or unverifiableThe fact cannot be admitted as authority; if it could affect an actionable result, evaluation remains unknown or fails closed and cannot start a sandbox
A versioned platform policy with an explicit maximum age no greater than five minutes for cross-check CEL actor-verdict reuse is missing or unverifiableReuse is disabled; obtain a live authoritative role verdict or leave dependent CEL unknown, with no sandbox on unknown
A needed CEL actor-verdict cache entry is expired, invalidated, or scoped to another source, actor, repository, policy, or revisionRefresh from the authoritative actor-role source; if that cannot complete, the dependent result remains unknown and cannot start a sandbox
The actor of a fact requiring the gate is human and below the selected harness minimum without the exact applicable human label grant for a triage minimum, or is a bot with no recognized role or narrow grantThe fact is excluded from that harness's authorized CEL view and cannot trigger execution
Versioned normalized-entity contract is missing or incompleteEntity-first execution denied

Exceptions ​

The verified label-added transition below supplies an exact triage grant for a human author and a separate narrow grant for a provider-classified bot without a registered bot role. A human label author must meet the selected harness's minimum through that triage grant or a separately verified role. Registered-bot roles use a separate gate. Legacy compatibility paths do not create additional v2 grants or authorize unrelated entity facts or model-bound records.

Label application ​

Current compatibility behavior: GitHub authorizes an added-label event without applying the ordinary actor.role threshold. This existing behavior preserves bot-to-bot handoffs such as adding ready-to-code after triage completes, including handoffs from provider bots that are not Fullsend- registered.

V2 target behavior: When transition.kind is label_changed and label.action is added, the GitHub adapter MUST establish an accepted forge label mutation and authoritative actor-to-transition provenance. For a human, that evidence supplies a narrow triage grant for the exact accepted transition and its proven entity projection. It satisfies a harness with human_min_role: triage without claiming that the actor has a verified repository role. A write, maintain, or admin minimum requires a separate fresh, verified effective human role meeting it. A provider-classified bot MAY use its own verified-transition grant without an actor.bot_role lookup, preserving bot-to-bot handoffs. Neither grant authorizes unrelated history or model-bound records. At the time of ADR 0107, GitHub is the only implementation; another adapter MUST provide equivalent forge-authoritative evidence before enabling either grant, and absent or unverifiable evidence denies the event. The platform gate MUST NOT inspect the label name or maintain an agent-role label allowlist; harness/CEL routing owns that mapping and may further narrow the dispatch.

Bot-submitted reviews (GitHub) ​

Current compatibility behavior: Until the resolver is implemented, fullsend dispatch continues to authorize a GitHub review_submitted event from a bot without requiring a collaborator permission or actor.bot_role. This existing exception is not evidence that the bot identity was resolved.

ADR 0107 target behavior: After the resolver is implemented, a GitHub review event is authorized only when the provider classifies the actor as a bot and resolves its exact registered actor.bot_role; actor.role remains none. The downstream harness CEL trigger may further constrain which bot role and review state are accepted.

Legacy lifecycle close (pull_request_target.closed) ​

Current bash compatibility behavior: The GitHub bash dispatcher routes pull_request_target.closed to retro without a separate collaborator-role check, so any closer may trigger it. This is not a read-only capability: retro's token can write issues and pull requests, and its post-script can file issues, comment, and apply labels. The Go CEL path has no close-event exception and applies the selected harness's role-derived default; a retro role requires write.

The ungated bash route is not a general v2 grant for CEL-based dispatch or entity facts. Under this contract, a human close-event actor must meet the selected harness's effective minimum before CEL. A retro-role harness defaults to write; an explicit human_min_role may choose another allowed value. The legacy /fs-retro slash command retains its bash write guard.

Schedule and manual dispatch ​

When source.system is schedule or manual, the actor is the configured operator or service identity (for example, a GitHub App bot or workflow GITHUB_ACTOR). Adapters MUST classify that identity from authoritative provider metadata. Human identities use verified forge permissions and the selected harness's effective minimum; bot identities use provider-backed actor.bot_role resolution and retain actor.role as none. An unrecognized or unresolved bot identity is denied.

Current compatibility behavior: Until provider-backed bot-role resolution is wired into the adapters and dispatch path, adapters set actor.role to the service identity's effective repository permission (typically write), so the standard permission gate applies.

Target contract, not yet implemented: Existing schedule/manual compatibility handling remains authoritative until provider-backed bot-role resolution is wired into the adapters and dispatch path.

Entity-source CEL evaluation ​

This future path was adopted by ADR 0098, clarified by ADR 0144, and revised for selective acquisition and audit by ADR 0147, with bounded cross-check actor-verdict reuse under ADR 0148. ADR 0151 supplies the platform floor and per-harness human minimum. It applies to an entity-source harness's CEL predicate whether a verified prompting event is present or event == null; legacy event-only dispatch is unchanged. The entity-source path is not yet implemented by fullsend dispatch, fullsend poll, or bound fullsend run. Entity-first execution MUST NOT be enabled until the versioned normalized-entity contract defines CEL-eligible and prompt-eligible fields and their coverage states. That contract is a platform allowlist; a harness may request fewer fields but cannot expand it.

With a prompting event, the event-actor authorization gate for the selected harness still runs before its CEL predicate; neither discovery nor an entity-source query replaces it. The event actor authorizes that event, not other actor-originated entity facts. A verified human label addition may use its exact triage grant for a triage-minimum harness; a stronger minimum requires a separately verified effective role. The separate verified GitHub bot label grant may admit a provider-classified bot's exact accepted transition and proven entity projection without a registered bot role. Neither grant authorizes other label applications, the entity's current label state without transition provenance, or other records from the same actor. Current label state may use a grant only when the adapter proves it is the projection of the same accepted transition, with no intervening replacement. For a triage-minimum harness, this exact projection remains eligible while that proof holds, even if the labeler later loses repository permission; it is evidence of the accepted mutation, not a current role. A higher human minimum still requires the labeler's current verified effective role, and model-bound records still pass their separate fresh gate. For an eventless check, the following Fullsend-origin and platform-eligibility gate applies instead.

fullsend poll may enumerate candidates without synthesizing a NormalizedEvent or event actor. A bound fullsend run may instead recheck only its canonical entity with event == null, including after consuming an initial event. Both paths require eligible entity sources and effective platform policy permitting eventless evaluation. They are authorized only when the caller has trusted Fullsend-controlled invocation provenance: a verified, non-user-assertable platform execution identity bound to the invocation and its target, such as an attested workflow/job identity or installation credential. Neither the actor of an earlier event nor an authenticated wake peer supplies this authority. A CLI flag, request header, or other caller-supplied claim is insufficient. A caller that cannot establish that provenance MUST be denied. Wildcard eligibility (* or all) requires the same explicit platform-level justification as any other wildcard allowlist in this contract.

Each entity-source evaluation's audit record MUST identify its verified initiator (the event actor for an event-bearing check, or the trusted Fullsend invocation identity for an eventless check), target repository, effective policy and pinned policy identity, canonical entity, acquisition-plan identity, and decision outcome, including UNKNOWN, deferred, or denied outcomes. The audit MUST record needed source scopes and bounded queries, their coverage and completeness evidence, current attribution, live or eligible cached verified role or exact label-grant provenance, and harness-gate outcome for individual actor-originated records used as decisive witnesses. Complete negative or aggregate claims require bounded query and source-coverage evidence bound to the effective minimum, authorization policy, permission or grant source, and check time or equivalent authorization epoch. For a label-grant witness, the audit MUST bind the provider's accepted transition proof, current source revision and proven projection, and check time; the grant is not recorded as a verified actor role. Summaries MAY use counts and digests of admitted, excluded, and unresolved actor checks; they do not require an audit roster of every record and actor examined to establish completeness. When an actor verdict is reused for CEL, the audit MUST record bounded evidence of its authoritative source, lookup start time or age measured from that start, the completed verdict, the maximum age allowed by the effective platform policy, effective authorization policy and source revision, and whether a live, local-cached, or shared-cached verdict was used. This may be aggregated for collection-wide checks rather than listing every actor. If a cached actor verdict contributes to a false CEL result, the audit MUST also identify the bounded recheck or rediscovery guarantee, or show that the candidate remains retryable. Unrelated history is not fetched or listed solely for audit. The audit does not retain raw rejected content. An eventless child cycle attributes initiation to the Fullsend identity, never to an earlier event actor or historical entity actor.

Authorization of an event actor or Fullsend invocation origin does not authorize other entity content. For each evaluated harness, Fullsend derives a conservative acquisition plan from its CEL predicate and the normalized-entity contract. The platform retains a closed classification of action-indicating categories; it need not fetch every category for every predicate. The plan MUST cover every field, history range, or derived fact that could change the predicate's result, including actor-originated issue or change-proposal bodies, comments, reviews, and label applications or current label state when used as action-indicating evidence. Counts, existence tests, and other aggregates over actor-originated records have the same dependency. The evaluator MAY defer planned reads and stop once partial evaluation yields a definitive result. A source field is actor-neutral for a particular CEL use only when an actor-originated change cannot make the predicate newly actionable. For a service-clock predicate, raw authoritative updated_at means time since any forge update; it is not recomputed from actor-filtered history. Where the provider guarantees that it only advances, updated_at < moving_cutoff can become true as time passes. An update, including one by an unauthorized actor, can defer that staleness run but cannot make it newly eligible. A harness that needs time since the last authorized update must acquire and authorize the relevant history. By contrast, updated_at >= cutoff can become true because of an update and requires provenance and authorization for that causal occurrence, or remains unknown. Raw comments_count, current labels, or newest-comment metadata cannot enter the authorized CEL view if an unauthorized record or change could affect the result. Fullsend MUST recompute such a fact from admitted records, prove the relevant causal occurrence or platform exception, or leave it unknown. Presenting an action signal as current state never bypasses the actor gate. A provider query MAY narrow reads only when it is proven to include every authorized fact that could affect the result; otherwise Fullsend expands the query or leaves the dependent result unknown. A provider aggregate may replace individual reads only if it preserves the actor-filtered meaning under the eligible CEL verdicts for the current check and proves the coverage needed for that result. Unanalyzable dynamic access requires a conservative broader plan or a fail-closed unresolved outcome. Cached fields or history may satisfy a planned read only after their source revision and query coverage are validated for the current check. Accepted events and ordinary entity wakeups invalidate affected entity facts under ADR 0144. They do not by themselves invalidate a separately scoped actor-verdict cache entry; a trusted authorization change does.

Fullsend distinguishes not requested or not loaded, partially loaded, successfully empty, absent or inapplicable, unsupported, and failed data. Unloaded or incomplete data MUST NOT masquerade as an empty collection or a false condition. For every actor-originated fact whose authority is needed to make CEL actionable or establish an actionable absence or aggregate under the causal classification above, Fullsend MUST re-establish its current source-revision actor and obtain an eligible verified authorization verdict before admitting the fact into the selected harness's CEL view. A human ordinarily must have an effective role from the provider or a configured permission source meeting that harness's human_min_role. Only the exact verified label-added transition and its proven projection may instead use the human triage grant when the harness minimum is triage; the grant is not an actor-role verdict for other facts or CEL role comparisons. A bot needs a provider-backed exact registered role under ADR 0107, except for the exact transition covered by the verified bot label-added grant. That grant requires accepted transition provenance and positive provider bot classification and supplies authority only for that transition, not unrelated entity history. A verified effective human role remains available to harness CEL when present; the human label grant is exposed as separate provenance. CEL may impose further conditions but cannot replace the gate. An unauthorized fact is excluded from that harness's authorized view. Missing or unverifiable attribution or authorization of a fact needed to prove an actionable result remains unresolved and cannot prove absence. Historical or inferred actor relationships are insufficient; only the bounded provider-verified actor-role cache defined below may replace a live actor-permission lookup. CEL may further restrict the authorized view, but cannot create authority.

Bounded CEL actor-verdict cache ​

This exception to a fresh actor-permission lookup for each entity check is adopted by ADR 0148. For entity-source CEL only, Fullsend MAY reuse a completed, authoritative human permission or exact registered bot-role verdict across entity checks and harnesses, poll cycles, separate poll and bound run loops, and the bound run's final CEL decision. The cached value MUST be the verified effective role or verified absence, never a platform-floor boolean, per-harness authorization decision, or CEL result. Every check reapplies the selected harness's effective human minimum or bot gate, as applicable, and its CEL conditions. Both positive and verified negative results may be cached; errors, timeouts, unresolved lookups, caller claims, and partially verified roles MUST NOT be cached. A verified label grant is transition-specific and MUST NOT be cached as an actor-role verdict. The key MUST bind the provider, tenant, canonical repository or project, immutable actor ID, verified actor kind, effective authorization policy and its revision or digest, and permission source. When applicable, it also binds the selected ref and the presence or absence and content or revision of both OWNERS and OWNERS_ALIASES, or the bot-registry revision. A login or entity ID is not an actor cache key. Where an authority does not expose a revision token, the authoritative lookup's start time defines the observed epoch; known changes to that source still invalidate its entries.

Within-check memoization under ADR 0147 avoids repeated lookups while the verdict remains eligible under an absolute five-minute hard cap. Cross-check reuse is disabled until a versioned platform policy defines and enforces an explicit maximum age no greater than five minutes. The platform MAY choose a shorter age; repository or harness settings MUST NOT widen the hard cap or the effective platform bound. Only a completed verdict may enter the cache. Its absolute expiry is measured from the authoritative lookup's start at the earlier of the hard cap and any shorter platform bound. An entry expires at that fixed time even if it is read, handed from poll to run, or retained by a shared host cache; no hit extends its lifetime. An expired verdict must be refreshed before it can contribute to a definitive CEL decision, including during one long progressive check. Known role, membership, OWNERS, OWNERS_ALIASES, bot-registry, or effective-policy changes MUST invalidate affected entries without waiting for TTL expiry. An ordinary entity wakeup invalidates affected entity facts, not an otherwise valid actor verdict, unless it carries trusted evidence of an authorization change. Shared cache readers MUST enforce expiry with a trusted clock or a conservative skew allowance; uncertain age makes an entry unusable. An expired, invalidated, or mismatched entry requires a new authoritative lookup. If that lookup cannot complete, the dependent CEL result remains UNKNOWN or is denied under the fail-closed rules above; the stale verdict is never used. A shared host cache MUST remain inaccessible to the sandbox and agent.

A false CEL result dependent on any cached actor verdict MUST cause a recheck after that verdict expires or is invalidated, and no later than the first of those events plus a bounded, platform-controlled discovery or reconciliation interval, even when the entity has not changed. The recheck obtains a new verdict and may be scheduled directly or guaranteed by source rediscovery. The platform MUST define that interval and either retain a durable candidate through checkpoint or watermark advancement or prove rediscovery after it. If timely re-evaluation cannot be guaranteed, the cached verdict MUST NOT close the candidate: Fullsend uses a live verdict for a final false decision or keeps the candidate inconclusive and durably retryable under a bounded retry schedule. A cached actor result alone cannot permanently close the candidate.

This deliberately accepts CEL actor-role evidence up to the platform maximum age. A still-valid positive cache entry may admit a newly observed actor-originated record after an unobserved revocation, and a negative entry may exclude a newly promoted actor until expiry and the required bounded recheck. A positive entry may likewise keep a negative predicate false after an unobserved revocation. The bound run still checks the current entity facts, source-revision actor, and coverage before its final CEL decision; only the verified actor-role verdict may be reused within TTL. The CEL cache MUST NOT satisfy event-actor admission or ADR 0133's model-bound record admission and exposure checkpoints. Those gates continue to resolve fresh authority under their separate rules. Fresh model-bound checks may redact records or deny further execution, but they do not undo a sandbox launch admitted using a positive CEL cache entry. The launch may already have occurred after an unobserved revocation within the permitted age.

Fullsend MAY evaluate the predicate over a partially materialized authorized view. It may return a definitive true or false only when that result is the same for every possible completion of still-unloaded inputs admitted under the actor-verdict rules above and consistent with established source coverage. This quantifier includes still-valid cached CEL verdicts within the bounded platform age, even if actor permission changed without a known invalidation. Coverage MUST account for pagination and concurrent source changes; a required collection remains incomplete unless a stable view or sound reconciliation establishes its relevant range. For example, an authorized witness may settle an existence test as true; false or a negative condition requires complete coverage of its relevant authorized search space. An UNKNOWN result requires further relevant field or history reads and re-evaluation in poll or bound run, or retention as retryable inconclusive work. A provider failure, unsupported source, unresolved authorization, or inability to prove completeness in a remaining decision dependency within allowed bounds fails closed: it cannot be converted to false, empty, or a sandbox run.

fullsend poll MAY hand an unresolved candidate and its acquisition hints to a bound fullsend run without claiming a CEL match. A definitive true result in poll also selects only a candidate, not a sandbox launch. The bound run MUST independently re-resolve current entity state and actor provenance, use a live or eligible cached CEL actor verdict, and reach a definitive true result before starting a sandbox. A definitive false result does not start one. Any deciding verdict that expires or is invalidated before sandbox admission MUST be refreshed and CEL re-evaluated; otherwise the decision remains inconclusive and the sandbox does not start. Any handoff is provisional. Until the bound run reports a definitive false decision with any cache-dependent recheck durably arranged, completes actionable work, or safely defers it under ADR 0144, discovery MUST retain a durable retry record or prove that its source query will rediscover the candidate after advancing a checkpoint or watermark. Scheduling a run, delivering a wakeup, or receiving a process-start receipt is not completion. If the bound run fails or remains inconclusive, the candidate MUST remain retryable and cannot be recorded as false, empty, or handled.

Fullsend MUST omit or minimize unneeded untrusted fields where the normalized entity contract permits. Further trust or injection filtering MAY run after CEL routing and before the harness pre-script. Retained content remains untrusted for dispatch and CEL evaluation throughout. ADR 0133 defines the separate model-bound admission and filtering gate, as revised for the per-harness human minimum by ADR 0151: only filtered content whose source record and actor pass the fresh gate below may be exposed to the model under bounded harness scope. Any resulting agent run uses the harness's configured identity and permissions.

Model-bound actor-attributed data admission ​

The model-bound gate applies to every source-system record that Fullsend may expose to a model, regardless of whether the record arrived in an event, was found by polling or snapshotting, or was read through a proactive or Fullsend-controlled API. Fullsend MUST establish the record's current actor attribution through the source system and resolve that actor's current authorization. A human MUST have a fresh, verified effective role meeting the selected harness's human_min_role at each required checkpoint. A bot MUST have fresh provider-backed recognition of its exact registered actor.bot_role under ADR 0107; its actor.role: none is not compared with the human minimum. Missing or unverifiable attribution, an unrecognized or unresolved bot identity, and a human below the selected minimum MUST redact the record; there is no data-only fallback. The platform derives the default minimum from the resolved harness role as specified above, not from a workflow stage or token capability. CEL conditions for selecting the run do not authorize separate model records. A verified label transition, whether human- or bot-authored, does not admit its source record to model input without this separate fresh gate and ADR 0133 filtering.

The model-record admission evidence MUST bind each admitted or redacted record's current-revision actor and gate result to the selected harness's pinned policy identity and the fresh checkpoint. A CEL match or earlier event admission is not this evidence.

Source-record attribution MAY come from a provider-verified event or be resolved from the source system. Model-bound actor authorization MUST be resolved from the current authoritative permission or registered-role source under the effective policy after event or retrieval admission; it MAY be co-fetched with a later record read or lazily resolved when first needed. A resolved result MAY be cached only for the logical lifetime of the containing event or entity structure and agent invocation, subject to the fresh checkpoints below. Re-reading or re-fetching the data, creating a new structure for retry or requeue, or terminating and re-invoking the agent ends that lifetime and requires fresh resolution, even if the same in-memory structure is reused. The cache lifetime is not extended by object identity or by retaining the structure.

Event-carried authorization evidence MAY support event admission and routing, but MUST NOT satisfy model-bound checks. Event-triggered runs MUST resolve record-actor authorization after event admission and before model admission, again immediately before sandbox initialization, and again immediately before first model exposure. If sandbox initialization and first exposure are contiguous, one fresh check MAY serve both. Non-event retrieval paths MUST perform the equivalent checks after retrieval admission. A Fullsend-controlled capability invoked after sandbox initialization MUST resolve authorization immediately before releasing each result to the model. That release check is the mid-run equivalent of the sandbox checkpoints.

A cache entry MUST NOT cross any checkpoint. Implementations MAY coalesce contiguous checks and MAY batch or cache lookups for multiple records from the same actor, provided the cache remains within this logical lifetime. Failure, unavailability, or loss of authorization at any checkpoint MUST redact the record and, if no authorized records remain, deny the run. Revocation after first exposure cannot retract content already present in the model context, but every later retrieval result requires its own release check.

Excluded fields ​

The following fields are not authorization evidence and must not be used for dispatch gating:

FieldWhy excluded
author_associationDoes not reflect private org membership; an org admin with private membership gets CONTRIBUTOR instead of MEMBER (github/gh-aw-mcpg#2862). Also reflects contribution history, not current authority.
Contribution historyPast contributions do not confer repository permissions. A former maintainer's contributions cannot substitute for a provider-verified role verdict; a valid CEL cache entry may still reflect a role from before revocation within its bounded age.
actor.is_entity_authorBeing the author of an issue or PR does not grant repository permissions. This field supports routing decisions in CEL triggers, not authorization.

Principle: relationship and contribution-history fields are not evidence of current authority. Event-backed human authorization must be derived from the source provider's verified permission model or a trusted configured permission source at event time and meet the selected harness's effective minimum, except that an exact provider-verified human label addition may use its narrow triage grant. Neither path relies on cached or inferred relationships. Fullsend-originated eventless evaluation must instead be authorized from trusted Fullsend-controlled invocation provenance for poll enumeration or bound rechecks. Actor-originated entity facts needed for an actionable CEL result are separately gated by current source-revision attribution and a live or eligible bounded role verdict against that harness's minimum, except for the exact human label transition and its proven projection under a triage minimum. Deferral-only raw state follows the causal classification above. Model-bound records receive separate fresh checks. Historical actors and relationship fields must not serve as authorization evidence.

Enforcement point ​

The triage platform floor and selected harness's effective human minimum are platform-enforced gates that CEL cannot override. Event-backed dispatch checks the normalized human event actor's fresh verified effective role against both before evaluating that harness's CEL predicate. An exact verified human label addition may instead satisfy a triage minimum with its narrow grant; a stronger minimum requires the verified role. Bots use the registered-role or separate narrow label-transition grants above. Fullsend-originated eventless evaluation checks trusted invocation provenance and platform eligibility before resolving the entity. An entity-source predicate may then be evaluated progressively for each eligible harness. An actor-originated fact that could make CEL actionable, or whose actor-filtered coverage is needed to prove an actionable absence or aggregate, passes that harness's minimum using live or eligible bounded role evidence, or the exact human label grant when the minimum is triage. Its current source-revision attribution and coverage are checked separately. Deferral-only raw state follows the causal classification above. Not-yet-loaded facts remain unknown rather than being treated as unauthorized, absent, or empty.

Forge event
  --> NormalizedEvent (adapter)
  --> Human triage floor/label grant or bot grant  <-- enforced here
  --> Resolve trusted harness and verified event/target metadata
  --> If needed, resolve structural entity-source metadata only
      from the provider or platform discovery credential
  --> Resolve guarded minimum overlays from that metadata
      --> Human role or exact label-grant gate    <-- before this harness's CEL
  --> Event-only CEL routing or entity-source resolution
      --> Dependency-driven acquisition and per-harness actor gate
      --> CEL true / false / UNKNOWN
      --> Model-record gate and execution only on definitive true

Fullsend poll discovery / bound run eventless recheck
  --> Trusted-origin authorization gate     <-- enforced here
  --> Enumerate and resolve candidates (poll) / resolve canonical entity (run)
  --> Evaluate each candidate harness (poll) or the bound harness (run):
      --> Resolve guarded overlays and effective human minimum
          from verified pre-admission metadata
      --> Dependency-driven acquisition and per-harness actor gate
      --> CEL true / false / UNKNOWN (event is null)
      --> Unknown may continue in bound run after current entity re-resolution
      --> Model-record gate, filtering, and execution only on true

CEL triggers: harness-specific conditions ​

Harness trigger expressions select runs and may require stronger human roles or narrower bot roles, labels, and entity states. Such CEL conditions may further narrow a run but never weaken or replace the platform floor or the selected harness's admission minimum, and they do not create a bot or transition grant. A CEL nonmatch is not a platform denial. On the event-backed path, an event that fails the role or exact label-grant gate never reaches that harness's CEL.

Current fullsend dispatch runs IsEventAuthorized() before harness loading and FilterAuthorizedHarnesses() before MatchHarnesses(). Jira polling preloads harnesses but applies both checks before CEL. In the target path, the platform MAY reject a below-floor human event before loading harnesses unless the exact verified label grant applies, but MUST resolve and enforce each candidate harness's minimum before its CEL predicate. On the Fullsend-originated eventless path, the trusted-origin gate runs before poll enumeration or bound-entity resolution and CEL evaluation. For each eligible entity-source harness, the platform admits actor-originated facts needed for an actionable CEL result only after current source-revision attribution and a live or eligible bounded role verdict meeting its effective minimum, or the exact applicable human label grant for a triage minimum. CEL receives that verified effective role when present and may apply further conditions. An exact human label transition may instead carry separate triage-grant provenance; CEL MUST NOT treat it as a verified role. The verified bot label-event grant authorizes only its exact transition, as described above. Eventless checks use event == null. Legacy event-only harness routing does not acquire entity history under this section.

Per-repo configurability ​

The triage floor and the narrow human-label and bot grants are platform-level security boundaries. Individual repositories cannot disable or lower them. A trusted harness author MAY set human_min_role at the top level or in an ADR 0112 trusted-fact-guarded overlay to triage, write, maintain, or admin. This may lower a harness's write default to triage or raise its minimum; the selected value applies to human event, entity-fact, and model-record admission alike. A repository may disable a harness or use trigger CEL to require additional conditions, but cannot make it available to a human rejected by either admission gate. A source event may select the field only through the validated trusted facts above; it cannot supply policy text.

A repository can add permission sources through the authorization field (today only owners_file). These sources may raise the verified effective role used by the platform and visible to CEL; they do not change either minimum or bypass the gate. The current bash has_repo_permission helper and Go event and per-harness gates retain their v1 compatibility behavior until migrated.

Versioning ​

This is the v2 normative contract under ADR 0015. ADR 0147 supersedes v1's future full-history entity-evaluation and audit requirements; ADR 0148 adds bounded cross-check reuse of provider-verified actor-role verdicts for entity-source CEL only. ADR 0151 sets the immutable triage platform floor and the selected harness's effective human_min_role for human event, entity-fact, and model-record admission. The resolved harness role supplies the default map above; an explicit top-level or trusted-fact-guarded overlay value may change it within the allowed range. Trigger CEL may further narrow selection but does not replace the admission minimum. V2 retains v1's role-derived defaults; a resolved explicit or guarded-overlay value supersedes the fixed default for that harness across event, entity-fact, and model-record admission. The separate fresh model-bound release checkpoints remain.

Migration MUST teach harness loading and base composition to recognize and validate human_min_role with key-presence awareness, including in overlay entries, before the value can affect any v2 decision. The versioned CEL schema MUST name and classify the verified effective-role path before a role-based minimum overlay can run. An explicit empty or invalid value MUST NOT silently inherit or default during scalar merge; a forge: value or overlay with untrusted or admission-dependent guard inputs MUST be rejected before resolution. A runner that cannot interpret a configured value MUST reject that harness, including a stronger maintain or admin value; it MUST NOT silently use the v1 role-derived default. Migration also requires versioned event and entity CEL inputs that carry verified effective roles and distinguish exact label-grant provenance. Existing authorization-sensitive CEL comparisons against an unverified legacy role MUST be migrated before v2 routing. Migration also requires dispatch and run to use the same verified overlay guard context, a verified policy-identity handoff with the ordered overlay outcome and minimum, per-harness event and entity actor gates, and an entity-context-snapshot v2 contract that can represent all four allowed minimums and the changed record-admission rule. Entity-context-snapshot v1 retains its historical stage-derived triage/write rule until that migration. Authorization v1 remains authoritative for existing dispatch during migration, including its bash thresholds and Go role-derived defaults. An explicit per-harness minimum change under v2 is a harness-policy change bound to that harness's reviewed revision, not a normative contract version change. Breaking changes to v2 require docs/normative/authorization/v3/.

ADR 0107 migration exception: The migration from legacy bot authorization exceptions to provider-backed bot identity is also permitted within v2. It is a policy tightening, not a new supported actor population: existing compatibility behavior remains authoritative until an adapter implementation is updated; once updated, registered bots use actor.bot_role and unregistered non-label bots are denied. The adapter implementation itself is the migration boundary; no per-event mode marker or separate deprecation window is required. An updated adapter MUST enforce the target behavior and MUST NOT silently fall back to legacy bot authorization when its resolver fails. The normalized-event fields used to carry this distinction are additive. The verified label-added bot handoff grant remains; human label additions meet the selected harness's minimum only when the v2 human gate is implemented. This bot migration exception does not alter the general v3 rule for unrelated authorization changes.

Changev2 impact
Breaking (requires v3): remove a role from the hierarchy, change the platform triage floor, change the harness-role default map or allowed human_min_role values, stop applying the minimum to human label additions or any of the three admission paths, alter a target label or bot grant, change fail-closed to fail-open, remove a fresh model-bound checkpoint, or weaken the actor and coverage gates beyond the bounded CEL cache adopted hereDispatch implementations must migrate
Adapter bot-identity migration (allowed in v2): update an adapter to emit and enforce the ADR 0107 target representationThe adapter's existing compatibility behavior applies until that implementation update; afterward, registered bots use actor.bot_role and unregistered non-label bots are denied
Non-breaking (allowed in v2): add a role under the existing default map, change one reviewed harness's explicit minimum, add equivalent forge mappings, or clarify documentationThe platform floor and contract remain; a harness minimum change binds to its new revision