diff --git a/docs/release-control/v6/internal/subsystems/agent-lifecycle.md b/docs/release-control/v6/internal/subsystems/agent-lifecycle.md index 1a3a56988..df597576a 100644 --- a/docs/release-control/v6/internal/subsystems/agent-lifecycle.md +++ b/docs/release-control/v6/internal/subsystems/agent-lifecycle.md @@ -938,6 +938,13 @@ must not be projected into the provider child process. Any future design that distributes subscription CLI state or authentication through an agent requires a separate agent-lifecycle contract change and verification. +Patrol investigation evidence copied by +`internal/api/patrol_action_broker.go` into an action's broker-owned origin is +API/AI audit provenance only. Tool-use evidence IDs do not identify an agent, +grant an agent scope, authorize a command channel, or replace live agent +readiness and dispatch admission. Any agent-mediated action still crosses the +existing lifecycle identity, authorization, and receipt boundaries. + First-run security discovery under shared `internal/api/` is likewise an adjacent API/security boundary, not an agent-lifecycle discovery surface. Unauthenticated `/api/security/status` responses must stay on the public tier diff --git a/docs/release-control/v6/internal/subsystems/api-contracts.md b/docs/release-control/v6/internal/subsystems/api-contracts.md index fe7b4fe36..a2d04b6d8 100644 --- a/docs/release-control/v6/internal/subsystems/api-contracts.md +++ b/docs/release-control/v6/internal/subsystems/api-contracts.md @@ -1955,7 +1955,8 @@ a new API state machine, queue contract, or verification-accounting field. `aicontracts.OrchestratorActionBroker` contract as a tenant-bound proposal adapter. It stamps the fixed `pulse_patrol` requestedBy actor and a broker-owned `ActionOrigin` (surface/finding/investigation/proposal - IDs) through the service's internal `PlanWithOptions`; the public plan + IDs plus the orchestrator-observed investigation evidence IDs) through the + service's internal `PlanWithOptions`; the public plan endpoint keeps calling plain `Plan`, so an HTTP request body can never claim a first-party origin. Enterprise still has no decision or execution method. After planning, core may auto-authorize only when the capability's @@ -1990,7 +1991,12 @@ a new API state machine, queue contract, or verification-accounting field. can reconcile decisions and outcomes onto the correct tenant. A Patrol proposal must carry both its finding and investigation IDs before anything persists; identity-less proposals are refused so a - governed action can never lose deterministic correlation. + governed action can never lose deterministic correlation. Enterprise binds + proposal evidence from the bounded, non-terminal tool-use events it + observed for that run; the broker copies those IDs into `ActionOrigin`, and + the lifecycle normalizes and de-duplicates them before persistence. Model + output cannot mint this audit provenance, and the IDs grant no planning, + approval, or execution authority. Authority proof: `TestContract_PatrolActionBrokerKeepsPolicyExecutionCoreOwned` in `internal/api/contract_test.go`. The proposal enters that broker from the investigation-only diff --git a/docs/release-control/v6/internal/subsystems/storage-recovery.md b/docs/release-control/v6/internal/subsystems/storage-recovery.md index d6129750b..f8abe3c11 100644 --- a/docs/release-control/v6/internal/subsystems/storage-recovery.md +++ b/docs/release-control/v6/internal/subsystems/storage-recovery.md @@ -147,6 +147,13 @@ governed tool and verification boundaries. CLI authentication state must not be included in recovery exports, snapshots, or support bundles, and must not be interpreted as recovery readiness. +Patrol investigation evidence copied by +`internal/api/patrol_action_broker.go` into broker-owned action origin is +correlation provenance, not storage or recovery proof. A tool-use evidence ID +must not be reinterpreted as backup freshness, protection coverage, restore +readiness, or verification truth; those claims continue to require their +domain-owned typed evidence and deterministic verification paths. + Unified Agent durable-operation-receipt metadata is likewise an internal monitoring and agent-lifecycle boundary. The exact current protocol may be a fail-closed prerequisite when unified resources derive the bounded diff --git a/internal/api/patrol_action_broker.go b/internal/api/patrol_action_broker.go index 665dac5c9..6ff2c4efb 100644 --- a/internal/api/patrol_action_broker.go +++ b/internal/api/patrol_action_broker.go @@ -167,6 +167,7 @@ func (b *patrolActionBroker) Submit(ctx context.Context, proposal aicontracts.Ac FindingID: proposal.FindingID, InvestigationID: proposal.InvestigationID, ProposalID: proposal.ProposalID, + EvidenceIDs: append([]string(nil), proposal.EvidenceIDs...), }, PolicyFactors: policyFactors, }) diff --git a/internal/api/patrol_action_broker_test.go b/internal/api/patrol_action_broker_test.go index e04b17264..81fc199c3 100644 --- a/internal/api/patrol_action_broker_test.go +++ b/internal/api/patrol_action_broker_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "errors" + "fmt" "net/http" "net/http/httptest" "strings" @@ -231,7 +232,9 @@ func TestPatrolActionBrokerSubmitPlansThroughCanonicalLifecycle(t *testing.T) { h, executor := newPatrolBrokerTestHandlers(t, unified.ApprovalAdmin) broker := NewPatrolActionBroker("default", h) - disposition, err := broker.Submit(context.Background(), patrolTestProposal()) + proposal := patrolTestProposal() + proposal.EvidenceIDs = []string{" evidence-2 ", "evidence-1", "evidence-1", ""} + disposition, err := broker.Submit(context.Background(), proposal) if err != nil { t.Fatalf("Submit: %v", err) } @@ -263,7 +266,8 @@ func TestPatrolActionBrokerSubmitPlansThroughCanonicalLifecycle(t *testing.T) { record.Origin.Surface != patrolActionOriginSurface || record.Origin.FindingID != "finding-1" || record.Origin.InvestigationID != "inv-1" || - record.Origin.ProposalID != "prop-1" { + record.Origin.ProposalID != "prop-1" || + fmt.Sprint(record.Origin.EvidenceIDs) != "[evidence-1 evidence-2]" { t.Fatalf("audit origin = %#v", record.Origin) } }