Skip to content

Custom workflows

Most agents are not Accounts Payable or Customer Support. GenericContext is the general case. It governs any consequential write with no required signal fields and an open bucket for whatever evidence your workflow produces. The Novarch engine reasons over that evidence against the rules you author, the same way it does for the typed starter packs.

from novarch.verticals.generic import GenericContext

When to use it

Reach for GenericContext whenever the write is not an AP invoice payment or a CS refund. Access provisioning, document publishing, outbound messaging, data exports, infrastructure changes, claims adjudication: they all gate the same way. You do not need a typed context class to get enforcement. You need an action_type and a rule.

Reach for the typed APContext or CSContext starter pack when your workflow matches its shape and you want the bundled rules. Everything else uses GenericContext.

The model

Governing a custom workflow is three decisions:

  1. Pick an action_type. A short, stable string that names the write ("grant_access", "publish_article", "export_dataset"). It is the join key between your writes and your rules.
  2. Attach evidence. Build a GenericContext in the write’s context_builder. Put anything the Novarch engine should weigh into it. Extra fields are allowed and preserved.
  3. Author rules that apply to it. In the RULES workbench (/app/rules/workbench), set the rule’s “applies to” to your action_type. The Novarch engine then evaluates that rule on every matching write.

GenericContext fields

GenericContext has three optional identity fields and accepts arbitrary extras.

FieldTypeMeaning
record_idstr | NoneThe primary record this write touches: a request id, document id, or ticket id. Shown to the operator as the row identity.
summarystr | NoneA one-line, human-readable description of the write. The operator reads this first.
amountfloat | None (≥ 0.0)A monetary or quantitative magnitude, when the write has one. Optional.
(any extra)JSON-serializableAny additional field you set. GenericContext is extra="allow", so extras are kept, sent to the server, serialized into the session the Novarch engine reads, and rendered as evidence in the audit doc.

There are no required signal fields. A write carrying only a summary is valid. The richer the evidence you attach, the more precisely your rules can be written.

Worked example: an access-provisioning agent

An agent grants system access on request. Company policy says production admin access always needs an approved change ticket. The agent has no reason to know that policy. The enforcement layer holds it.

from novarch import augur, NovarchKillError
from novarch.verticals.generic import GenericContext
@augur.tool(kind="read")
def lookup_request(request_id: str) -> dict:
return access_db.get(request_id)
@augur.tool(
kind="write",
action_type="grant_access",
context_builder=lambda req: GenericContext(
record_id=req["request_id"],
summary=f"Grant {req['access_level']} on {req['resource']} to {req['requester']}",
# Extras: anything your rules should weigh.
resource=req["resource"],
access_level=req["access_level"], # "read" | "write" | "admin"
environment=req["environment"], # "staging" | "production"
requester_role=req["requester_role"],
change_ticket=req.get("change_ticket"), # "CHG-1234" or None
),
)
def grant_access(req: dict) -> str:
return iam.grant(req)
@augur.session(tenant_id="acme", team_id="it-ops", agent_id="access-bot")
def run(request_id: str):
req = lookup_request(request_id)
augur.set_reasoning("Requester is on the on-call rotation; access is time-boxed.")
grant_access(req) # gated on the Novarch engine's verdict

access_level, environment, requester_role, and change_ticket are not fields on GenericContext. They are extras. Because GenericContext is extra="allow", they travel with the action and the Novarch engine sees them.

A generic agent is often a rules engine or a scripted sweep with no rationale to record. Open the session with reasoning_profile="unavailable" and its actions say so. See Declaring that your agent has no reasoning.

The matching rule

In the RULES tab, author a rule whose “applies to” is grant_access:

Production Admin Access Needs a Change Ticket (BLOCK)

Block any access grant where environment is production and access_level is admin that does not reference an approved change ticket (CHG-NNNN). Production admin access without a change ticket has no authorization trail. Block it regardless of the requester’s role or the agent’s stated reasoning.

The Novarch engine evaluates this rule on every grant_access write, reads the extras (environment, access_level, change_ticket) from the action context, and blocks when they match. On a block it cites this rule and the evidence that fired: the same auditable Decision you get for AP and CS.

How rules scope to your action_type

Rule scoping is by action_type, set on each rule’s “applies to”:

  • A rule whose “applies to” matches the write’s action_type is evaluated on that write.
  • A rule with no “applies to” applies to every action, across all workflows. An un-scoped rule can only tighten, never loosen.
  • The RULES tab warns (it does not block) when “applies to” matches no action_type it has seen, so a typo surfaces before the rule silently governs nothing.

This is the same machinery the AP and CS packs use. They are rules scoped to process_invoice and issue_refund. Your custom workflow is a first-class peer: pick the action_type, author the rules, and the Novarch engine applies them. See Rules for authoring detail.

What the Novarch engine sees

The full action context, identity fields and extras alike, is serialized into the session log the engine reads, alongside any deterministic signals that fired. The engine reasons over all of it against your active rules and returns a Decision. The typed verticals ship signal extractors tuned to their fields; a custom workflow leans on the evidence you attach. Put the facts a rule needs to decide directly into the context.

Next steps

  • Rules: author the plain-English rules that gate your action_type.
  • augur decorators: the context_builder and @augur.tool surface.
  • Configuration: point the SDK at your deployment.