Skip to Content
RuntimePolicies & Workflows

Policies & Workflows

A one-off question produces an AnswerRun. But most CFO work is standing: “is our D&O coverage still sized correctly?”, “does NAV reconcile this period?”. OpsDNA models these as DomainPolicies — materialized standing queries — executed by @opsdna/workflow-policy and traced as PolicyRun records, because a standing obligation re-asked ad hoc in chat would drift, go stale, or silently stop being asked. Side-effectful work (actually doing something after approval) runs through workflow-core/workflow-runtime against decision packets.

DomainPolicy (packages/workflow-policy)

src/schema.ts defines DomainPolicySchema: a policy has at least one activation (DomainPolicyActivationSchema — what triggers evaluation), a typed query plan, and a source with provenance. A load-bearing refinement: model-proposed policy sources must include a proposalRef — an LLM can propose a standing policy, but the proposal itself becomes auditable state before the policy can run. hashDomainPolicy gives every policy a canonical content hash.

The package also ships two concrete policy domains as first-class implementations: D&O coverage sizing (insurance.ts, do-coverage-projections.ts) and NAV reconciliation (nav-persona-projections.ts), plus audience-projection registries that decide what different personas may see of a policy result, and approval-proof.ts for approval evidence.

PolicyRun invariants

PolicyRunSchema (src/schema.ts) is deliberately strict. Its refinement (policyRunIssue) enforces, among others:

// packages/workflow-policy/src/schema.ts (refinement highlights) if (run.tenantId !== run.typedQueryPlan.tenantId) return "PolicyRun tenantId must match typedQueryPlan.tenantId." if (run.queryPlanHash !== hashTypedQueryPlan(run.typedQueryPlan)) return "PolicyRun queryPlanHash must match the typed query plan." // every permission decision must be bound to this plan hash... if (decision.planHash !== run.queryPlanHash) return "PolicyRun permission decisions must be bound to the PolicyRun query plan hash." // ...to resolved runtime resources, not plan templates... if (containsUnresolvedPlanRef(decision.scope) || containsUnresolvedPlanRef(decision.resourceRef)) return "PolicyRun permission decisions must be bound to resolved runtime resources and scopes, not plan-template refs." // ...and every step must have a decision for every required gate: if (!permissionDecisionKeys.has(`${step.id}:${gate}`)) return `PolicyRun is missing a ${gate} permission decision for step ${step.id}.`

Linked AnswerRun ids must be unique within a run, and every audit verdict must actually target the PolicyRun’s graph (filterAuditVerdictsByRole). derivePolicyRunAggregateTrustState folds audit verdicts into a single trust state, mirroring AnswerRun. In short: a PolicyRun that decodes is a PolicyRun whose plan, permissions, and audits are mutually consistent — you cannot persist a half-gated run.

Findings

packages/findings turns policy results into managed state: registry.ts (finding types), lifecycle.ts (open → resolved transitions), policy-adapter.ts (bridging PolicyRuns to findings), and contradiction-flow.ts (what happens when new evidence contradicts an existing finding).

Workflow execution (workflow-core / workflow-runtime)

When a policy or user action needs to do something, packages/workflow-runtime/src/runtime.ts executes tool steps against a WorkflowToolRegistry, and commits or fails a DecisionPacket (@opsdna/decision-packets) — the approval artifact that authorized the run. Traces are hash-versioned (WORKFLOW_RUN_TRACE_HASH_VERSION = "workflow-run-trace:v1"). Two authority modes exist:

  • executeApprovedWorkflow — real execution under generic_approval authority
  • executeReviewedWorkflowDemo — fixture tools for review demos, explicitly “without granting production authority”

packages/workflow-core holds the shared plan/action schemas and planner primitives that both the policy layer and the runtime consume.

Don’t confuse the three “workflow” packages. workflow-policy = standing queries and PolicyRuns (read/evaluate). workflow-runtime = side-effectful execution gated by decision packets (write/act). workflow-core = shared schemas. And as everywhere in the runtime: the legacy document-to-workflow experiments in packages/query-execution are not the place for new policy contracts.

Policy evals live in the eval harness (bun run evals:workflow-policy) — see AI Layer & Evals. Trace persistence is covered by packages/persistence; permission gating by Permissions & Evidence.

Last updated on