feat(docs): Add enactment pipeline - #359
Open
misonijnik wants to merge 5 commits into
Open
Conversation
OpenTaint work now runs one of two pipelines over the same machine and the same `.opentaint/` tree, differing only in where the source and sink rules come from: - assessment (`assessment-agent`, renamed from `appsec-agent`) — discovered from the project's dependency attack surface, as before - enactment (`enactment-agent`) — generalized from a supplied finding set, so both rule sides exist before the first scan and that scan is rule-first `appsec-agent` becomes a thin entry point: toolchain and nesting checks, then it picks the pipeline and loads it in the same session (MAIN must keep the build and scans). An existing `state.yaml` decides for it; `mode` is not switchable, since neither pipeline's tracking means anything to the other. The shared phase machine keys off `state.yaml`'s `mode`, so `get_status.py` holds ASSESSMENT_PHASES and ENACTMENT_PHASES and `generate.py init` takes `--mode`. Enactment adds `reference_set`, `boundaries`, and `crossref`; it closes on the cross-reference, which judges each supplied finding by trace identity — never by a rule-id match — and writes `.opentaint/enactment.md`. One more piece both pipelines share: - `discover-universal-boundaries`, the leaf that generalizes one finding family into a saturated source and sink boundary — widen, re-factor the whole family, split rather than listing one pattern branch per finding New tracking: `reference/<id>.yaml` and `boundaries/<family>.yaml`. Rule units keep their existing format and path, so `create-rule` and `create-test-project` are untouched.
The two pipelines are not alternatives. One `.opentaint/` tree accumulates the artifacts of every pass over it, in either order and repeatedly across commits: enactment then assessment hunts with the boundaries the supplied report produced, assessment then enactment measures that report against the corpus the first pass built, and either re-run on a later HEAD is a regression check. `init` refused a mode switch and told the user to start a fresh tree, which made all of that impossible. `mode` is now the pipeline of the current pass, not a property of the tree: - `init` keeps every durable artifact across a switch and prints what carried over - `history.yaml` records one entry per pass, keyed on (commit, mode, levels); re-running init with the same knobs is a resume, not a new pass. `get_status.py --full` prints the chain once there is more than one - `findings` survives an assessment pass, so a later enactment pass inherits the set and `--findings` is required only the first time One consequence: an assessment pass rescans a tree that may carry a reference set, which invalidates the coverage manifest an enactment pass wrote. So `crossref` is gated on the reference set existing rather than on the mode — it closes an assessment pass too, re-judging the supplied findings against the new results. Identical work; only the triggering pass differs. Both pipeline skills now open by saying the run is one pass over a tree that outlives it, and their resumption sections forbid deleting or rewriting an artifact just because this pass didn't produce it.
The enactment split landed `discover-universal-boundaries` authored against Java: it takes `language` as an input and the boundaries stage passes it, but its workflow told the leaf to lean on annotations, `@Valid`, `$MAP.get(...)`, and a JVM descriptor for `candidate_patterns` — vocabulary that belongs to one language, in a body that is supposed to hold the reasoning method. Keep the reasoning, which is language-agnostic, and move the vocabulary into `references/<language>.md` the way the leaf's siblings already do. The Java reference is grounded in the bundled ruleset rather than in generic advice, because what a family actually needs is decided by what the built-ins already match: `spring/untrusted-data-source.yaml` taints every non-scalar parameter of a mapping-annotated method and the servlet rule taints the whole `HttpServletRequest` of an entry method, which puts a Java family's real gap on non-HTTP transports, reactive accessors, and second-order re-entry. It carries the per-class sink coverage with its two notable absences, Bean Validation as the validation impostor, and the `Class#member` plus JVM descriptor shape the spec's `candidate_patterns` take. Also re-apply two skill doc fixes that an earlier rebase dropped: the silently-skipped-file note in the pass-through Java reference, and the `project.yaml` dependencies wording in triage-dependencies. skills/ is regenerated from skills-templates/ via build.py.
The enactment split left two orchestrators — assessment-agent and enactment-agent — behind an appsec-agent that only chose between them, and the two ran the pipeline in different orders: enactment authored both rule sides before its first scan, assessment authored sinks after the model round. Two documents describing one machine drift, and the divergent order made the shared stage references say "in enactment mode" about work that is not mode-specific at all. Collapse them back into `appsec-agent` and make the difference a `mode` knob with three values, which is all the difference ever was — where the run's boundary evidence comes from: - onboarding — the external-method frontier: every dependency member the project's own code calls, taken as a trust boundary until a leaf verdicts it. This is the old dependency sweep, named for what it does and scoped to the once-per-project pass it is; init refuses a second one over a tree that already carries the ledger, rules, and models it produced - discovery — the project, a diff, or an informal spec, resolved to the code it names - enactment — the supplied finding set, normalized into the reference set Universal rules are now every mode's output, not enactment's: intake ends with a family list whatever it started from, the boundaries stage generalizes each family into one universal source and one universal sink, and the seeded units are what rule authoring works from. `discover-universal-boundaries` takes `evidence` rather than `finding-ids`, and its reasoning is unchanged — a swept frontier member and a reference finding factor through a boundary the same way. One phase list, in the order the spine has always implied and every mode now follows: build, intake, boundaries, source_rules, scan, approximations, sink_rules, triage, then poc and — whenever the tree carries a reference set, whichever pass left it — crossref. Both boundary sides are decided before the scan that proves them; only the sink rules are authored after it, against the frontier that scan names, so a model can never stand in for a boundary nobody authored. Tracking gains `tracking/scope.yaml` (the family list for the modes that don't carry it on reference files), the boundary spec keys its factorization by `evidence`, and `partition discover` becomes `partition frontier` so the partitioner and the discovery mode no longer share a name for different things. skills/ is regenerated from skills-templates/ via build.py; no engine, rule, or model behaviour changes.
misonijnik
force-pushed
the
misonijnik/enactment
branch
from
August 19, 2026 07:55
57f3b62 to
20eaf2f
Compare
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.
No description provided.