feat: workflow audit records at the framework level - #3
Merged
Conversation
Nshai
force-pushed
the
feat/framework-audit
branch
from
August 11, 2026 00:02
454b4aa to
fea9680
Compare
A workflow definition can now declare the shape of its own audit record via
IAuditedWorkflowDefinition, and the runtime hands every node an
IWorkflowAuditRecorder bound to that declaration. Storage stays
workflow-agnostic — a root plus string-typed entries with JSON payloads — so a
new workflow needs no schema change. Recording is best-effort by contract: an
audit write must never fail the work it describes, so store failures are logged
and swallowed.
GET /workflows/{name}/instances/{id}/state returns an instance's lifecycle
status alongside whatever record its workflow declared, projected back through
the declared sections so a run in progress shows what is still outstanding as
readily as what is done. ?section= narrows the response.
The framework default is InMemoryAuditRecordStore. Abacus.Run.Service displaces
it with an EF Core SQLite store under Abacus:AuditRecords, whose connection
string is resolved from IOptions inside the context factory rather than read at
wire time, so a test host's configuration override actually applies.
README.md and docs/wiki.md document the declaration, the recorder's guarantees,
the state route, the configuration section, and the custom-store extension point.
The audit hook had no worked example outside the test fixtures, so the answer to "what does a definition actually have to do" lived only in prose. Workflow example-order is that answer: it declares four sections, opens the record with attributes, files a plan, records one entry per line, and settles both terminal paths. The work it does is deliberately dull — plan, price, total — because the point is the auditing around it, not the domain. Two details are the reason it exists rather than a shorter sample. Line entries are keyed by SKU, so a retried attempt corrects the record instead of appending a contradictory second line. The failure path records the outcome before letting the exception propagate, because a recorder failure is swallowed by design and an explanation filed after the throw is an explanation lost. The host may now define workflows. ArchitectureBoundaryTests carves out the Abacus.Run.Service.Workflows.<Name> namespace: extension points outside it still mean the shell has grown behaviour of its own, but a workflow that ships inside the host assembly by convention is a documented consumer of the framework. Test isolation. The host substitutes a SQLite audit store, so every fixture was writing into the deployed database file and inheriting records from previous runs — 600 KB of them had accumulated in the integration bin. Both fixtures now take a temp file each and delete it on dispose, and runtime database files are ignored rather than left to be committed by accident. Verified against a running host, not only the suite: both documented curl invocations return the records shown in the docs.
Nshai
force-pushed
the
feat/framework-audit
branch
from
August 11, 2026 00:05
fea9680 to
03b1726
Compare
Commits here are authored by the repository owner. The tooling used to produce a change is not part of the change's history, so no co-author trailer, no generated-with footer on pull requests, and no tool attribution in code or docs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the framework-level audit hook, the API that reads a record back, and a worked example in the host. Two commits, reviewable in order.
Why
Events answer what did the runtime do. They do not answer why is this result defensible — the plan a node formed, the input it worked from, the output it produced. Those are workflow-specific questions, so this adds the hook and the storage without the framework ever owning a schema.
What changed
1.
9d439d0— the framework hookIAuditedWorkflowDefinition, declaring a root kind plus its sections.WorkflowRunnerreads that at build time and hands every node anIWorkflowAuditRecorderbound to it, reachable viaHostExecutorRuntime.AuditorWorkflowBuildContext.Audit. A workflow that declares nothing gets null and pays nothing.IAuditRecordStoreholds a root row plus string-typed entries with opaque JSON payloads, so a new workflow needs no schema change.GET /workflows/{name}/instances/{id}/statereturns lifecycle status plus the record, projected back through the declared sections.?section=narrows it. The route is scoped by workflow name because the response shape comes from that workflow's declaration — a mismatched name is a wrong URL, so404.InMemoryAuditRecordStoreis the framework default;Abacus.Run.Servicedisplaces it with an EF Core SQLite store underAbacus:AuditRecords.2.
454b4aa— the worked exampleWorkflow
example-orderinsrc/Abacus.Run.Service/Workflows/ExampleOrder. The work is deliberately dull — plan, price, total — because the point is the auditing around it. Two details are why it exists rather than something shorter:Design notes worth a reviewer's attention
ArchitectureBoundaryTestspreviously forbade any workflow definition in the host assembly. It now carves outAbacus.Run.Service.Workflows.<Name>; extension points outside that namespace still fail the test.HostFixtureandChaosFixturenow take a temp file each and delete it on dispose, and runtime.dbfiles are gitignored.Testing
534 unit / 97 integration / 7 chaos, 0 failures.
New coverage: 9 unit tests for the recorder's guarantees (undeclared sections dropped, re-record replaces, sequence monotonic, store failure swallowed, close-before-open writes nothing), 5 integration tests for the state route against a fixture-local workflow, and 7 for the example workflow end to end.
Also verified against a running host, not only the suite — both documented
curlinvocations return the records shown in the docs, including theDeadStoppedfailure path with its pre-failure line still recorded.Docs
README.mdgains an "Audit records" section with a runnable example;docs/wiki.mdgains "Workflow audit records" with the declaration walkthrough, the recorder's guarantees, the example, the API route, the config section, the custom-store extension point, and a troubleshooting entry.Scope
Framework audit only. The QA workflow realization, RAG/ingestion, and
Abacus.Data.Serviceare deliberately excluded, as is the tenant gate-configuration work onfeat/tenant-executor-gate-configuration— this branch does not depend on it.