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 GenericContextWhen 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:
- 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. - Attach evidence. Build a
GenericContextin the write’scontext_builder. Put anything the Novarch engine should weigh into it. Extra fields are allowed and preserved. - Author rules that apply to it. In the RULES workbench
(
/app/rules/workbench), set the rule’s “applies to” to youraction_type. The Novarch engine then evaluates that rule on every matching write.
GenericContext fields
GenericContext has three optional identity fields and accepts arbitrary
extras.
| Field | Type | Meaning |
|---|---|---|
record_id | str | None | The primary record this write touches: a request id, document id, or ticket id. Shown to the operator as the row identity. |
summary | str | None | A one-line, human-readable description of the write. The operator reads this first. |
amount | float | None (≥ 0.0) | A monetary or quantitative magnitude, when the write has one. Optional. |
| (any extra) | JSON-serializable | Any 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, NovarchKillErrorfrom 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 verdictaccess_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
environmentisproductionandaccess_levelisadminthat 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_typeis 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_typeit 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_builderand@augur.toolsurface. - Configuration: point the SDK at your deployment.