models
strands_teams.models ¶
Base Pydantic models and generic result primitives for strands_teams.
This module hosts the framework-level data-transfer types shared by dispatch, hooks, display, and use-case layers. All models are domain-agnostic: they carry no codebase-analysis vocabulary. Concrete use cases subclass or compose these.
Copied and renamed from the pre-split code_context_agent package: - StrictModel / FrozenModel from models/base.py. - Bundle, GeneratedFile, PhaseTiming from models/output.py (verbatim). - AnalysisConfidence -> TeamConfidence and CalibratedJudgment (renamed).
EvidenceType module-attribute ¶
Kinds of evidence a finding can cite (file:line, a test, a command exit, an artifact revision, a URL, or a log excerpt).
Severity module-attribute ¶
Ordered severity levels for a finding.
Disposition module-attribute ¶
Disposition = Literal[
"open",
"acknowledged",
"remediated",
"accepted_risk",
"rejected",
"superseded",
]
Lifecycle states a finding moves through as it is triaged and resolved.
NO_CURRENT_REVISION module-attribute ¶
Sentinel expected_current_revision meaning "this logical_name has no current revision yet".
A first promotion of a logical_name passes this sentinel as expected_current_revision (spec 005 AC-2-4). It is deliberately a value no real revision_id (a hex content hash) can collide with.
EpistemicStatus module-attribute ¶
EpistemicStatus = Literal[
"observed",
"inferred",
"hypothesized",
"disputed",
"accepted",
"rejected",
"superseded",
]
The epistemic standing of a TeamEvent's claim (AC-1-2).
observed— a directly recorded fact (e.g. a dispatch completed).inferred— derived from other events without direct observation.hypothesized— a tentative claim awaiting confirmation.disputed— contradicted by another event/actor.accepted/rejected— an adjudicated outcome.superseded— replaced by a later event (retained in the log, never deleted).
HumanDecisionStatus module-attribute ¶
Lifecycle of a :class:HumanDecision gate.
pending— awaiting a human; the runner suspends the transition (AC-4-3).resolved— approved by a human; the runner resumes and re-authorizes (AC-4-4).rejected— declined by a human; the runner abandons the transition (AC-4-6).
GateName module-attribute ¶
GateName = Literal[
"capability",
"separation_of_duties",
"required_approvals",
"open_blocking_finding",
"human_required",
]
The ordered gate stack the PolicyEngine runs; the name of the gate that failed is reported on a denying :class:PolicyVerdict (AC-3-1).
StrictModel ¶
Bases: BaseModel
Base model with strict validation.
This model: - Allows mutation (frozen=False) - Validates assignments - Forbids extra fields - Strips whitespace from strings
Use for internal mutable state.
FrozenModel ¶
Bases: BaseModel
Immutable model (like a frozen dataclass).
This model: - Prevents mutation (frozen=True) - Validates assignments - Forbids extra fields
Use for result objects and immutable data transfer.
GeneratedFile ¶
Bundle ¶
PhaseTiming ¶
TeamConfidence ¶
Bases: FrozenModel
Confidence metadata attached to any team judgment.
Renamed from AnalysisConfidence to keep the framework layer domain-agnostic.
CalibratedJudgment ¶
AuthorityGrant ¶
Bases: FrozenModel
What a dispatched team is permitted to do (ASTR §4.7).
A capability grant attached to a team's task contract. Every capability defaults to False (deny by default): a caller must explicitly widen a team's authority. The framework enforces may_create_blocking_finding in dispatch_team code — a team whose grant forbids blocking findings cannot persist one, regardless of what its agents' prompts say.
Attributes:
| Name | Type | Description |
|---|---|---|
may_create_blocking_finding | bool | Whether this team may persist a |
may_write | bool | Whether a later-phase specialist coding runtime granted to this team may modify files. A Codex |
may_approve | bool | Whether this team may approve/accept work (e.g. mark a finding remediated or sign off an artifact revision). |
Budget ¶
Bases: FrozenModel
How much a dispatched team may spend (ASTR §6.2).
The resource envelope attached to a team's task contract. All three limits are non-negative; a negative value is rejected at construction (FrozenModel validates assignments). A value of 0 means "no explicit limit supplied" — dispatch_team only overrides the run-default swarm execution_timeout when max_wall_clock_minutes is strictly positive.
Attributes:
| Name | Type | Description |
|---|---|---|
max_wall_clock_minutes | float | Wall-clock ceiling for the team, in minutes. |
max_cost_usd | float | Dollar ceiling for the team. This is the framework-contract value that a Bedrock |
max_retries | int | Retry ceiling. Bounds a Codex harness's retry count when a specialist coding runtime is granted (research-coding-runtimes.yaml codex_gpt56_bedrock; consumed in Phase 3 / spec 004-coding-runtime). |
Evidence ¶
Bases: FrozenModel
A single evidence reference backing a finding.
Attributes:
| Name | Type | Description |
|---|---|---|
type | EvidenceType | The kind of evidence ( |
uri | str | A locator for the evidence — e.g. |
detail | str | None | Optional human-readable note about what this evidence shows. |
Finding ¶
Bases: FrozenModel
A typed, evidence-bearing finding produced by a team (ASTR §6.6, §8.3).
Immutable and extra="forbid" (inherited from :class:FrozenModel), so a finding is safe to pass by reference through invocation_state and any on-disk findings.json that carries unknown keys is rejected at the parse boundary rather than silently accepted.
Attributes:
| Name | Type | Description |
|---|---|---|
statement | str | The finding itself, stated in one or two sentences (non-empty). |
severity | Severity | One of |
blocking | bool | Whether this finding must be resolved before the work is accepted. A |
disposition | Disposition | Lifecycle state (defaults to |
affected_artifacts | list[str] | Paths/ids of artifacts this finding is about. |
evidence | list[Evidence] | Evidence refs backing the finding (file:line / test / command exit / artifact / url / log). |
confidence | float | Calibrated confidence in |
created_by | str | Identifier of the agent/team that authored the finding. |
created_at | datetime | UTC creation timestamp. |
updated_at | datetime | UTC timestamp of the last lifecycle update. |
ArtifactRevision ¶
Bases: FrozenModel
An immutable, content-addressed revision of one logical artifact (ASTR §4.8, §6.4).
A lightweight metadata record — not the artifact blob itself, which lives at the layout-resolved path. Immutable and extra="forbid" (inherited from :class:FrozenModel): any attempt to assign a field after construction raises a pydantic.ValidationError (AC-1-1). Build one with :meth:from_content so content_hash and per-facet hashes are computed deterministically from the content (AC-1-2); constructing directly is supported for round-tripping a persisted revision.
Attributes:
| Name | Type | Description |
|---|---|---|
artifact_id | str | Stable identity of the logical artifact across all its revisions (defaults to |
revision_id | str | Identity of this specific revision. Content-addressed: defaults to |
content_hash | str | Deterministic hash of the revision's content alone (AC-1-2). |
artifact_type | str | The kind of artifact (e.g. |
logical_name | str | The name the alias pointer maps to a current revision. |
produced_by | str | Identifier of the team/agent/runtime that produced it. |
parents | list[str] |
|
supersedes | list[str] |
|
classification | str | Handling classification (e.g. |
facets | dict[str, str] | Per-facet content hashes ( |
created_at | datetime | UTC creation timestamp (not part of |
from_content classmethod ¶
from_content(
*,
logical_name,
facets=None,
body=None,
artifact_type="artifact",
produced_by="",
artifact_id=None,
revision_id=None,
parents=None,
supersedes=None,
classification="internal",
created_at=None,
)
Build a revision from content, computing content_hash deterministically (AC-1-2).
Supply the content either as facets (a {facet_name: content} mapping, enabling facet-level semantic_diff) or as a single body string (wrapped as one "body" facet). content_hash and revision_id (which defaults to content_hash — content addressing) are derived from the content alone; artifact_id defaults to logical_name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logical_name | str | The logical artifact's name. | required |
facets | Mapping[str, str] | None | Facet-name to facet-content mapping. Mutually exclusive with | None |
body | str | None | A single-blob content string (wrapped as facet | None |
artifact_type | str | Kind of artifact. | 'artifact' |
produced_by | str | Producer identifier. | '' |
artifact_id | str | None | Stable logical identity; defaults to | None |
revision_id | str | None | Explicit revision id; defaults to | None |
parents | list[str] | None | Input revision ids. | None |
supersedes | list[str] | None | Superseded revision ids. | None |
classification | str | Handling classification. | 'internal' |
created_at | datetime | None | Explicit timestamp; defaults to now. | None |
Returns:
| Type | Description |
|---|---|
ArtifactRevision | A frozen :class: |
Source code in packages/strands-teams/src/strands_teams/models.py
StaleArtifactError ¶
Bases: Exception
Raised by promote_artifact when the optimistic compare-and-set fails (AC-2-3).
Signals that expected_current_revision did not match the alias's stored current revision for logical_name — another promotion won the race, or the caller's view of "current" is stale. The alias pointer is left unchanged.
Attributes:
| Name | Type | Description |
|---|---|---|
logical_name | The artifact whose promotion was rejected. | |
expected | The revision the caller believed was current. | |
actual | The revision actually current in the alias store. |
Record the artifact, the expected revision, and the actually-current one.
Source code in packages/strands-teams/src/strands_teams/models.py
RevisionConflictError ¶
Bases: Exception
Raised when a revision_id is re-persisted with content differing from the stored one (AC-1-5).
The revision store is content-addressed and append-only: a given revision_id binds to exactly one content_hash. Re-persisting the same revision_id with identical content is idempotent; re-persisting it with differing content is a conflict that leaves the stored revision unchanged.
Attributes:
| Name | Type | Description |
|---|---|---|
revision_id | The revision id that was re-persisted with differing content. | |
stored_hash | The content hash already on disk for that revision id. | |
incoming_hash | The content hash of the rejected write. |
Record the conflicting revision id and the stored vs. incoming content hashes.
Source code in packages/strands-teams/src/strands_teams/models.py
PromotionResult ¶
Bases: FrozenModel
The structured outcome of a successful promote_artifact compare-and-set (AC-2-6).
Returned (as model_dump_json()) by the promote_artifact tool so the calling agent parses JSON per the strands tool norm (research-coding-runtimes line 26).
Attributes:
| Name | Type | Description |
|---|---|---|
logical_name | str | The promoted artifact's logical name. |
current_revision | str | The new current |
superseded_revision | str | The prior current |
TeamEvent ¶
Bases: FrozenModel
A typed, causal, replayable event on the team event plane (ASTR §6.5, §7).
Immutable and extra="forbid" (inherited from :class:FrozenModel): an unexpected field — notably a raw-transcript or chain-of-thought field — is rejected at construction rather than silently accepted (AC-1-4). Serializing one event to a single JSON line and re-parsing it yields an equal object, so the event log is a lossless round-trip substrate for replay (AC-1-5).
event_id, created_at, and correlation_id are auto-assigned when omitted: a fresh UUID event id, a UTC creation timestamp, and a correlation id defaulting to goal_id when present and otherwise to the assigned event_id (AC-1-3).
Attributes:
| Name | Type | Description |
|---|---|---|
event_id | str | Unique identity of this event (auto-assigned UUID hex if omitted). |
event_type | str | The kind of transition/claim (e.g. |
actor | str | Identifier of the agent/team/runtime that produced the event. |
recipients | list[str] | Identifiers this event is addressed to (empty = broadcast). |
thread_id | str | The conversation/thread this event belongs to. |
goal_id | str | The goal (durable lead loop) this event belongs to. |
task_id | str | The task within the goal this event belongs to. |
artifact_bindings | list[str] |
|
causal_parents | list[str] |
|
correlation_id | str | Groups related events; defaults to |
epistemic_status | EpistemicStatus | The claim's standing (AC-1-2). |
payload | dict[str, Any] | The structured outcome of the transition — NO transcript/CoT. |
evidence | list[Evidence] | Evidence refs backing the event (reuses the P1 type). |
created_at | datetime | UTC creation timestamp (auto-assigned if omitted). |
ApprovalToken ¶
Bases: FrozenModel
A revision-bound, signed, expiring approval of a consequential transition (ASTR §14.7).
An approval names the exact :class:ArtifactRevision it approves — by both artifact_revision_id and the revision's immutable revision_content_hash captured at grant time (AC-1-2) — so it goes stale the moment the artifact changes (AC-5-1). Its signature is an HMAC over the canonical serialization of its identity fields (:func:approval_signing_payload), so a forged or tampered token fails :meth:signature_valid and the PolicyEngine treats it as absent (AC-1-4). Past valid_until the engine rejects it as expired (AC-1-5). invalidation_rules names the artifact facets whose change sunsets the approval (AC-5-2, AC-5-3).
Build one with :meth:mint so the signature is computed with the configured signing key; constructing directly is supported for round-tripping a persisted token.
Attributes:
| Name | Type | Description |
|---|---|---|
token_id | str | Unique identity of this approval (auto UUID hex when minted). |
approved_transition_kind | str | The transition |
artifact_revision_id | str | The |
revision_content_hash | str | The approved revision's |
approver | str | Principal who granted the approval (the separation-of-duties and required-approvals gates read this — AC-3-3, AC-3-4). |
granted_at | datetime | UTC timestamp the approval was minted. |
valid_until | datetime | UTC expiry; at or after it the token is expired (AC-1-5). |
signature | str | HMAC-SHA256 hex over :func: |
invalidation_rules | list[str] | Facet names whose change sunsets the approval (AC-5-2); empty means any change to the bound artifact invalidates it. |
mint classmethod ¶
mint(
*,
approved_transition_kind,
artifact_revision_id,
revision_content_hash,
approver,
signing_key,
valid_until=None,
valid_for=None,
invalidation_rules=None,
token_id=None,
granted_at=None,
)
Mint a signed :class:ApprovalToken over its canonical payload (AC-1-3).
Computes the HMAC signature with signing_key so the token verifies against the same key later (AC-1-4). Supply the expiry either as an absolute valid_until or a relative valid_for (added to granted_at); when neither is given the token is valid for 24 hours (AC-1-5).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
approved_transition_kind | str | The transition kind approved. | required |
artifact_revision_id | str | The approved revision's id. | required |
revision_content_hash | str | The approved revision's content hash (AC-1-2). | required |
approver | str | The approving principal. | required |
signing_key | str | The configured signing key (from | required |
valid_until | datetime | None | Absolute UTC expiry (wins over | None |
valid_for | timedelta | None | Relative validity window from | None |
invalidation_rules | list[str] | None | Facet names whose change sunsets the approval. | None |
token_id | str | None | Explicit id; defaults to a fresh UUID hex. | None |
granted_at | datetime | None | Explicit grant time; defaults to now. | None |
Returns:
| Type | Description |
|---|---|
ApprovalToken | A signed, frozen :class: |
Source code in packages/strands-teams/src/strands_teams/models.py
signing_payload ¶
Return this token's canonical signing payload (AC-1-3).
Source code in packages/strands-teams/src/strands_teams/models.py
signature_valid ¶
Return whether signature verifies against signing_key (AC-1-4).
Uses :func:hmac.compare_digest for a constant-time comparison so a signature check cannot be timing-attacked. A token whose signature does not verify is treated as absent by the PolicyEngine.
Source code in packages/strands-teams/src/strands_teams/models.py
is_expired ¶
Return whether the token is at or past its valid_until (AC-1-5).
Decision ¶
Bases: FrozenModel
An auditable record of a consequential choice (ASTR §11).
Captures the full deliberation: the question, the options considered, what was selected, the rationale, the evidence (reusing the P1 :class:Evidence vocabulary — file:line / test / command exit / artifact / url / log, AC-2-2), any dissent, and the prior decision_id this record supersedes when it reverses or replaces an earlier decision (AC-2-3). The PolicyEngine appends one on every allow/deny verdict (AC-2-4).
Attributes:
| Name | Type | Description |
|---|---|---|
decision_id | str | Unique identity of this decision (auto UUID hex if omitted). |
question | str | The question the decision answers. |
options | list[str] | The options that were considered. |
selected | str | The option that was chosen. |
rationale | str | Why the selection was made. |
evidence | list[Evidence] | Evidence refs backing the decision (reuses the P1 type — AC-2-2). |
dissent | list[str] | Recorded dissenting views (empty when unanimous). |
supersedes | list[str] | Prior |
decided_at | datetime | UTC timestamp the decision was recorded. |
TransitionRequest ¶
Bases: FrozenModel
A proposed consequential state transition the PolicyEngine judges (ASTR §14.7).
The model constructs one of these and hands it to PolicyEngine.authorize_transition; the engine — never the model — issues the final verdict (AC-6-1). A consequential transition (e.g. a release or a merge) is refused unless a valid revision-bound approval is present and no open blocking finding remains (AC-6-2, AC-6-3).
Attributes:
| Name | Type | Description |
|---|---|---|
actor | str | The principal requesting the transition (the capability and separation-of-duties gates read this — AC-3-2, AC-3-3). |
kind | str | The transition kind (e.g. |
target_revision_id | str | The |
consequential | bool | Whether this transition is consequential (release path) and thus subject to the structural approval + blocking-finding refusal (AC-6-2, AC-6-3). |
presented_tokens | list[str] | The |
required_capability | Literal['may_write', 'may_approve', 'may_create_blocking_finding'] | The :class: |
target_artifact | str | Optional logical artifact name the transition targets, used to match findings that reference the artifact by name as well as by revision id (AC-3-5). |
HumanDecision ¶
Bases: FrozenModel
A first-class human-gate protocol state (ASTR §14, §5.2).
Where a transition kind is human-gated, the PolicyEngine emits one of these as pending rather than letting the model self-approve (AC-4-1). The runner persists it to disk and suspends the outer loop for that transition until a human resolves it (AC-4-3, AC-4-5); an approving resolution resumes and re-authorizes the transition (AC-4-4), a rejecting one abandons it (AC-4-6).
Attributes:
| Name | Type | Description |
|---|---|---|
decision_id | str | Unique identity of this gate (auto UUID hex if omitted). |
transition | TransitionRequest | The :class: |
prompt | str | The human-readable question posed to the human. |
options | list[str] | The choices offered to the human. |
status | HumanDecisionStatus |
|
resolved_by | str | Principal who resolved the gate (empty while pending). |
resolved_at | datetime | None | UTC timestamp of resolution ( |
created_at | datetime | UTC timestamp the gate was emitted. |
resolve ¶
Return a resolved copy of this gate (frozen — never mutates in place).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
approved | bool |
| required |
resolved_by | str | The principal resolving the gate. | required |
resolved_at | datetime | None | Resolution timestamp; defaults to now. | None |
Returns:
| Type | Description |
|---|---|
HumanDecision | A new :class: |
Source code in packages/strands-teams/src/strands_teams/models.py
PolicyVerdict ¶
Bases: FrozenModel
The PolicyEngine's final allow/deny on a transition (ASTR §14.7, AC-3-1).
The model never decides — this verdict, produced by PolicyEngine.authorize_transition, is the authority (AC-3-1). On a deny, failed_gate names which gate in the ordered stack refused and reason is human-readable. On an allow, failed_gate is None (AC-3-6). When the denying gate is human_required, pending_human_decision carries the gate the runner must suspend on (AC-4-1).
Attributes:
| Name | Type | Description |
|---|---|---|
allowed | bool | Whether the transition is authorized. |
failed_gate | GateName | None | The gate that refused ( |
reason | str | Human-readable explanation of the verdict. |
pending_human_decision | HumanDecision | None | The pending gate to suspend on, set only when |
compute_content_hash_from_facet_hashes ¶
Fold a revision's per-facet hashes into one whole-artifact content_hash.
A stable SHA-256 over the sorted {facet_name: facet_hash} mapping. Because it consumes only the persisted per-facet hashes, the whole-artifact hash is reproducible from a round-tripped record (the model validator uses exactly this), while remaining a deterministic function of content — identical content yields identical facet hashes and thus an identical fold; any differing byte changes at least one facet hash and thus the fold (AC-1-2).
Source code in packages/strands-teams/src/strands_teams/models.py
compute_content_hash ¶
Return a deterministic content hash over a revision's facet contents (AC-1-2).
The hash is a function of the facet content alone — not of any metadata (logical_name, produced_by, created_at). Each facet's content is hashed independently, then those per-facet hashes are folded into one digest via :func:compute_content_hash_from_facet_hashes (so the whole-artifact hash and the per-facet hashes on the persisted record always agree). Two revisions built from byte-identical facet content therefore produce equal hashes; any differing byte produces a different hash (AC-1-2).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
facets | Mapping[str, str] | Mapping of facet name to that facet's content string. An empty mapping hashes to the well-defined fold of | required |
Returns:
| Type | Description |
|---|---|
str | A 64-char lowercase hex SHA-256 digest. |
Source code in packages/strands-teams/src/strands_teams/models.py
approval_signing_payload ¶
approval_signing_payload(
*,
token_id,
approved_transition_kind,
artifact_revision_id,
revision_content_hash,
approver,
valid_until,
)
Return the canonical serialization an ApprovalToken signs over (AC-1-3).
A stable, sorted JSON object over exactly the six fields the signature binds: token_id, approved_transition_kind, artifact_revision_id, revision_content_hash, approver, and valid_until (serialized as an ISO-8601 string). Deterministic — identical inputs always fold to the same string — so :meth:ApprovalToken.mint and :meth:ApprovalToken.signature_valid sign/verify over byte-identical payloads.