Skip to content

Latest commit

 

History

History
executable file
·
130 lines (95 loc) · 11.3 KB

File metadata and controls

executable file
·
130 lines (95 loc) · 11.3 KB

m87-governed-loop — Handoff Brief

Status: partial build, clean stopping point. Handing to the separate build-mode project for completion. Date: 2026-06-05 Origin: uploaded m87-governed-loop.bpmn (reference model) + README.md. Goal is a dual reference/executable MIT OSS repo for github.com/MacFall7, hardened enough to claim BPMN authorship for the Camunda FDE application.


What this is

A BPMN 2.0 encoding of the M87 governed execution loop (Proposal ≠ Execution), in two forms:

  • reference/m87-governed-loop.bpmn — the original isExecutable="false" governance reference model. Untouched. This is the spec.
  • executable/m87-governed-loop.bpmn — a Camunda 8 / Zeebe isExecutable="true" variant. Service tasks carry zeebe:taskDefinition job types; the four gateways carry FEEL conditions, and every gateway's default flow routes to a refusing exit so fail-closed is structural, not convention.

The governance decisions are implemented once as pure deterministic functions and reused by the engine workers. That is the design spine: a gate is a function, a gateway is the same function, and the function is unit-testable without a running cluster.


Design decisions (locked)

  1. Two BPMN files, not one. Keep the pristine reference model for the spec/diagram; ship the executable variant for deployment. Node IDs are identical across both so the diagram interchange and the invariant-mapping table stay valid for both.
  2. Fail-closed is structural in the executable model. Gateway default flows go to End_Halt / End_RejManifest / FailReceipt / End_RejVerify. The permissive branch requires an explicit FEEL condition (=constraints_ok = true, =tool_in_manifest = true, =budget_exceeded = false, =artifact_verified = true). Missing/ambiguous variables route to refusal.
  3. Pure gates own the logic; workers are thin. governed_loop/gates.py is the single source of truth. The Zeebe/pyzeebe worker layer only marshals variables in and out. Same intent for the Java module.
  4. Missing telemetry = fail. A run that cannot prove it stayed inside the autonomy budget is treated as having left it. Unknown risk is not "low". No declared hash means not verified.
  5. Receipts are deterministic. Timestamp is injected by the caller so the same inputs + clock produce a byte-identical receipt. Even a failure emits an artifact-backed receipt (hash of the failure reason).
  6. Zeebe variable contract (set by the validate, execute, verify-artifact workers): constraints_ok: bool, tool_in_manifest: bool, budget_exceeded: bool, artifact_verified: bool.

Terminal-state map (BPMN end event ↔ Terminal enum)

BPMN end event Terminal Trigger
End_Complete COMPLETE artifact produced and verified
End_Halt HALT constraints unsatisfied / unclear (default branch)
End_RejManifest REJECT_MANIFEST tool absent from manifest, or action_type mismatch
End_Failed FAILED budget exceeded, runtime error, or missing telemetry
End_RejVerify REJECT_VERIFY no artifact, hash mismatch, or validation command failed

Done and verified

  • Repo scaffold + MIT LICENSE.
  • reference/ model preserved verbatim. xmllint well-formed.
  • executable/ Zeebe variant authored. xmllint well-formed; 8 zeebe:taskDefinition, 4 gateway defaults, 4 FEEL conditions.
  • policy/governance_policy.yaml + policy/tool_manifest.yaml.
  • Python pure core: policy.py, gates.py, receipts.py, __init__.py, pyproject.toml.
  • Pure-core smoke test passed (11/11 paths + budget boundary + deterministic receipt). All five terminals reached on the right inputs; every gate fails closed on malformed/ambiguous input. Run record below.
COMPLETE, HALT(no trace / risk=high / forbidden target),
REJECT_MANIFEST(unknown tool / action mismatch),
FAILED(budget steps / missing telemetry / error flag),
REJECT_VERIFY(tampered hash / validation fail)  -> all matched expected terminal
boundary steps==20 -> COMPLETE (at-limit is allowed)

Remaining work (for the build-mode project)

  • workers/python/governed_loop/workers.py — pyzeebe job workers (validate, execute, verify-artifact, etc.) wiring the pure gates to the Zeebe variable contract above. Lazy-import pyzeebe so the package imports without it.
  • workers/python/tests/test_gates.py — port the smoke cases into a real pytest suite (one assertion per fail-closed exit + the success path + the budget boundary). Frame as invariant proofs.
  • Java module under workers/java/pom.xml (JUnit 5 + SnakeYAML + io.camunda:spring-boot-starter-camunda-sdk), Gates.java/Policy.java/Receipts.java mirroring the Python logic exactly, Workers.java with @JobWorker handlers, GatesTest.java mirroring the Python tests. Note: Java was NOT compiled in the origin session (no local JDK/Maven). Compile + run mvn test on first pickup.
  • .github/workflows/ci.yml — matrix: xmllint --noout both BPMN files; pytest (Python); mvn -q test (Java).
  • docs/invariant-mapping.md — expand the README invariant→BPMN table; note it now applies to both files.
  • README rewrite — document the dual reference/executable structure, the Zeebe deploy steps (zbctl deploy executable/m87-governed-loop.bpmn), the variable contract, and the test story. Drop the original footer caveat ("mapping to executable is out of scope") since that is now done.
  • Verify the executable BPMN against the real Camunda 8 schema (Modeler or zbctl/Web Modeler deploy) — xmllint only checks well-formedness, not Zeebe schema conformance.

Repo tree (current)

m87-governed-loop/
├── LICENSE
├── HANDOFF.md                       <- this file
├── reference/m87-governed-loop.bpmn   (isExecutable=false, preserved)
├── executable/m87-governed-loop.bpmn  (Zeebe, isExecutable=true)
├── policy/governance_policy.yaml
├── policy/tool_manifest.yaml
├── workers/python/
│   ├── pyproject.toml
│   └── governed_loop/{__init__,policy,gates,receipts}.py   (smoke-verified)
├── workers/java/  (dirs scaffolded, sources pending)
└── .github/workflows/  (pending)

Career-application tie-in (for context)

Shipping the executable variant + green CI lets the Camunda FDE screening answer move from "worked with workflow tools conceptually" toward "configured, debugged, or written BPMN workflows." The warm-route DM hook: a governed execution loop authored in Camunda's own engine format. Application package lives at Job Search/m87-career-ops/applications/camunda-ai-process-fde/.


Build-mode completion — 2026-06-05

Picked up in the build-mode project. Remaining-work checklist is now done; status: shippable.

Delivered

  • workers/python/governed_loop/workers.py — eight pyzeebe job handlers + engine-agnostic pure handlers (handle_validate/execute/verify/...). pyzeebe is lazily imported inside create_worker, so the package imports without the extra.
  • workers/python/tests/test_gates.py26 invariant proofs, green. One assertion block per fail-closed exit + the success path + the budget boundary + deterministic-receipt + worker-routing.
  • Java module workers/java/pom.xml (JUnit 5 + SnakeYAML + io.camunda:spring-boot-starter-camunda-sdk), Terminal/Decision/Policy/Manifest/Receipts/Gates.java (pure core, mirrors Python), PolicyLoader.java (SnakeYAML), Workers.java (@JobWorker), GatesTest.java (8 proofs mirroring Python).
  • .github/workflows/ci.yml — three jobs: xmllint both BPMN + fail-closed structure assertion; pytest; mvn test.
  • docs/invariant-mapping.md — three-layer mapping (BPMN ↔ pure function ↔ proof) + terminal map.
  • README.md rewritten for the dual reference/executable structure, variable contract, deploy steps, and test story. Old "out of scope" caveat dropped.
  • workers/python/README.md added so pip install -e workers/python resolves its readme.

Bug found and fixed (origin pure core)

  • gates.py decide() raised AttributeError on a non-dict execution result — it called .get() while building the failure receipt, so a malformed result crashed instead of failing closed. The origin smoke test missed it because it only called the gate function directly, never routed a non-dict through decide(). Fix: decide() now derives a safe telemetry view (result if isinstance(result, dict) else {}) for the receipt cost fields, while the budget gate still sees the raw value for the correct reason string. Covered by test_failed_*[not-a-dict].

Verified locally

  • xmllint --noout both BPMN: well-formed. Fail-closed structure assertion: 4 gateway defaults, 4 FEEL conditions, 8 zeebe:taskDefinition — all present.
  • Python: pip install -e ".[dev]" + pytest26 passed.

Still requires a real environment (cannot be done in a JRE-only / clusterless sandbox)

  • Java compile + mvn test — sandbox had a JRE only (no javac, no Maven, no root). The pure-core Java mirrors the verified Python line-for-line and GatesTest depends only on the core + JUnit (not on Camunda), so mvn test proves the logic on first pickup. One thing to confirm: the @JobWorker/@Variable import package matches the exact Camunda SDK version (io.camunda.zeebe.spring.client.annotation for 8.6.x). If Workers.java fails to resolve, bump/adjust the import to the SDK version pinned in pom.xml.
  • Camunda 8 schema conformance of executable/xmllint checks well-formedness only, not the Zeebe schema. Deploy once via zbctl deploy resource or Web Modeler to confirm the engine accepts it.

Post-audit hardening — 2026-06-05 (v0.2.0)

Adversarial CTO-grade audit run (report: m87-governed-loop-AUDIT-2026-06-05.md). BPMN structure verified sound (fail-closed wiring correct on all four gateways, full reachability/DI). Four flags found and all fixed + regression-tested, verified locally in both languages:

  • H1 / L1 / L2 — budget gate now fails closed on malformed telemetry. Non-numeric (steps_used: "3"), boolean, or negative telemetry previously crashed decide() / Gates.decide (TypeError / ClassCastException); now coerced and routed to FAILED. (gates.py, Gates.java; tests in both suites.)
  • M1 — manifest targets_allowed is now enforced. A write to a target outside a tool's declared allow-list returns REJECT_MANIFEST (was COMPLETE). (tool_in_manifest / toolInManifest.)
  • H2 — receipt hashing is now canonical and cross-language identical. Both languages hash structured artifacts via canonical JSON; verified Java computeHash({k:v,n:1}) == Python compute_hash == e18b0bd8…. (Receipts.canonicalJson.)
  • M2 — runtime equivalence is now proven. test_bpmn_equivalence.py parses the executable BPMN and asserts its routing == decide() across all five terminals.

Decorative policy/manifest keys (escalation.on_unclear, version, tool external) annotated as descriptive-only in the YAML.

Verified locally: Python pytest44 passed. Java pure core compiled under javac 17 + a logic harness → all checks pass incl. cross-language hash parity. (Java mvn test + the full Workers.java/Camunda path still run authoritatively in CI.)