Register dry-run adapters via BuildOwnerMux when independent flags are on; U9 stub fail-closes on malformed/timeout/out-of-bounds without raising PlanTier. Co-authored-by: Cursor <cursoragent@cursor.com>
84 lines
1.9 KiB
Go
84 lines
1.9 KiB
Go
package intentflow
|
||
|
||
import "context"
|
||
|
||
// OwnerMux routes generate keys to Task, optional U10–U12 owners when their
|
||
// flags are on, and everything else to Query.
|
||
type OwnerMux struct {
|
||
Query ActionOwner
|
||
Task ActionOwner
|
||
Mutate ActionOwner
|
||
DocGov ActionOwner
|
||
RunControl ActionOwner
|
||
}
|
||
|
||
func (m OwnerMux) pick(actionKey string) ActionOwner {
|
||
switch actionKey {
|
||
case "task.run", "task.run.adhoc":
|
||
return m.Task
|
||
}
|
||
f := CurrentFeatureFlags()
|
||
if _, ok := mutateActionKeys[actionKey]; ok {
|
||
if f.U10Mutate {
|
||
return m.Mutate
|
||
}
|
||
return m.Query
|
||
}
|
||
if _, ok := docGovActionKeys[actionKey]; ok {
|
||
if f.U11DocGov {
|
||
return m.DocGov
|
||
}
|
||
return m.Query
|
||
}
|
||
if _, ok := runControlActionKeys[actionKey]; ok {
|
||
if f.U12RunControl {
|
||
return m.RunControl
|
||
}
|
||
return m.Query
|
||
}
|
||
return m.Query
|
||
}
|
||
|
||
func (m OwnerMux) Authorize(ctx context.Context, exec StepExec) error {
|
||
owner := m.pick(exec.ActionKey)
|
||
if owner == nil {
|
||
return ErrPlanActionForbidden
|
||
}
|
||
if a, ok := owner.(StepAuthorizer); ok {
|
||
return a.Authorize(ctx, exec)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (m OwnerMux) Execute(ctx context.Context, exec StepExec) (Receipt, error) {
|
||
if err := m.Authorize(ctx, exec); err != nil {
|
||
return Receipt{}, err
|
||
}
|
||
owner := m.pick(exec.ActionKey)
|
||
if owner == nil {
|
||
return Receipt{}, ErrPlanActionForbidden
|
||
}
|
||
return owner.Execute(ctx, exec)
|
||
}
|
||
|
||
func (m OwnerMux) LookupReceipt(ctx context.Context, idempotencyKey string) (Receipt, bool, error) {
|
||
owners := []ActionOwner{m.Task, m.Mutate, m.DocGov, m.RunControl, m.Query}
|
||
for _, o := range owners {
|
||
if o == nil {
|
||
continue
|
||
}
|
||
if rec, ok, err := o.LookupReceipt(ctx, idempotencyKey); err != nil || ok {
|
||
return rec, ok, err
|
||
}
|
||
}
|
||
return Receipt{}, false, nil
|
||
}
|
||
|
||
func (m OwnerMux) ObserveFence(stepID string, token int64) {
|
||
for _, o := range []ActionOwner{m.Query, m.Task, m.Mutate, m.DocGov, m.RunControl} {
|
||
if obs, ok := o.(FenceObserver); ok {
|
||
obs.ObserveFence(stepID, token)
|
||
}
|
||
}
|
||
}
|