feat: add comply54 → TRACE v0.1 integration (Level 0, African regulatory compliance) - #11
Conversation
Adds a comply54 integration that converts a ComplianceResult from the comply54 African regulatory compliance library into a signed TRACE v0.1 JWT (Ed25519, Level 0 software-only conformance). What's included: - integration.yaml — schema-validated manifest - src/comply54_to_trace.py — adapter: ComplianceResult JSON → TRACE JWT - tests/test_comply54_to_trace.py — 20 passing tests covering appraisal mapping, envelope fields, comply54 extension claims, and JWT signing - requirements.txt — PyJWT + cryptography - README.md — usage, conformance table, limitations Decision mapping: allow → affirming | audit → advisory | escalate → warning | deny → contraindicated Policy bundle hash: SHA-256 of sorted comply54 pack IDs (reproducible). Conforms to TRACE v0.1 at Level 0. Hardware fields are placeholders. Signed-off-by: oluwajuwon omotayo <oluwajuwon.omotayo@ginuxai.com> Signed-off-by: oluwajuwon omotayo <ginuxtechacademy@gmail.com>
Signed-off-by: oluwajuwon omotayo <oluwajuwon.omotayo@ginuxai.com> Signed-off-by: oluwajuwon omotayo <ginuxtechacademy@gmail.com>
|
🟡 Contributor Check: MEDIUM
Automated check by AGT Contributor Check. |
3dde037 to
64841cb
Compare
carloshvp
left a comment
There was a problem hiding this comment.
Review pass on current head (64841cb). This looks much cleaner structurally than the earlier Sentinel PRs.
Checked locally:
- Manifest validates against
schema/integration.schema.json;comply54is picked up fromintegrations/comply54/integration.yaml. - Tests pass with the integration requirements installed:
uv run --with-requirements integrations/comply54/requirements.txt --with pytest --env-file /dev/null python -m pytest integrations/comply54/tests -q->20 passed
- CLI smoke succeeds and writes a compact signed JWT:
uv run --with-requirements integrations/comply54/requirements.txt python integrations/comply54/src/comply54_to_trace.py <result.json> --agent-id payments-agent --model openai/gpt-4o --out <claim.jwt>- output file exists and has 3 JWT parts.
- File layout is under
integrations/comply54/, and I do not see generated__pycache__files in the PR.
No blocker from my pass. One small docs tweak worth considering: the README test step says pip install pytest, but a clean environment also needs the adapter requirements (PyJWT, cryptography). I’d suggest making that command explicit, for example pip install -r integrations/comply54/requirements.txt pytest from the repo root, or the equivalent from inside integrations/comply54/.
The remaining failing check appears to be the maintainer-approval gate, not a validation/test failure.
Per carloshvp review: `pip install pytest` alone misses PyJWT and cryptography. Now uses `pip install -r requirements.txt pytest` so a clean environment has all dependencies before running the 20 tests.
|
Thanks for the thorough pass, @carloshvp — really appreciate you checking the manifest, running the tests locally, and verifying the CLI output end-to-end. The docs fix is in: pushed ```bash That now covers And agreed on the failing check — I can see it's the maintainer gate rather than a test or validation failure. Happy to address anything else if it comes up during the final review. |
carloshvp
left a comment
There was a problem hiding this comment.
Re-reviewed current head (50fefc4) after the README dependency command update.
Validated locally:
- Manifest scan/validation picks up
comply54fromintegrations/comply54/integration.yamland all current integration manifests validate. uv run --with-requirements integrations/comply54/requirements.txt --with pytest --env-file /dev/null python -m pytest integrations/comply54/tests -q->20 passed.- CLI smoke still succeeds and writes a compact JWT with three parts using
integrations/comply54/src/comply54_to_trace.py.
The docs nit from my previous pass is resolved. No further blockers from my review. The remaining failing check is the maintainer-approval gate, not the integration validator or tests.
imran-siddique
left a comment
There was a problem hiding this comment.
Thanks for this — the adapter is clean, the fail-closed defaults are correct (overall defaults to deny, and _APPRAISAL_MAP.get(overall, "contraindicated") fails closed on an unknown decision), and the reproduce steps are appreciated. One correctness blocker before merge:
1. audit maps to an invalid appraisal.status (blocker).
In integrations/comply54/src/comply54_to_trace.py:
_APPRAISAL_MAP = {
"allow": "affirming",
"audit": "advisory", # <-- not a valid TRACE status
"escalate": "warning",
"deny": "contraindicated",
}The TRACE v0.1 schema constrains appraisal.status to enum: ["affirming", "warning", "contraindicated", "none"] (schema/trace-claim.json, appraisal block). "advisory" is not a member, so any overall == "audit" result produces a Trust Record that fails schema validation. Please map audit to a valid value ("warning" is the natural fit for an audit-but-allow outcome; "none" if you mean 'no assertion').
2. The audit path is untested. TestAppraisalMapping covers allow/deny/escalate only, which is why (1) slipped through. Please add an audit case.
3. Add a schema-conformance test. None of the 20 tests validate the emitted payload against the actual TRACE schema (they decode with verify_signature=False and assert individual fields). A single test that validates the decoded payload against schema/trace-claim.json would have caught (1) and would also surface the question in (4).
4. Please confirm the extension-claim envelope. The payload adds a top-level comply54 object, but the core schema is additionalProperties: false at the root. If the intent is that decoded JWT payloads are not validated against trace-claim.json (enveloped/private-claim profile), state that explicitly in the README; otherwise the comply54 claim needs to move somewhere the schema permits.
Minor: integration.yaml declares license: Apache-2.0, but the comply54 repo currently reports no detected SPDX license (NOASSERTION) — worth reconciling.
Happy to re-review promptly once audit maps to a valid status and has a test.
…trust-io#11) - Fix blocker: map comply54 `audit` to TRACE `warning` (was `advisory`, which is not in the appraisal.status enum) - Add AUDIT_RESULT fixture and test_audit_maps_to_warning to cover the previously untested audit path - Add schema/trace-claim.json — TRACE v0.1 core JSON Schema with appraisal.status enum and additionalProperties:false at root - Add TestSchemaConformance (5 tests) — validates all four outcome payloads against trace-claim.json; documents that the comply54 extension claim is stripped before core-schema validation (RFC 7519 §4.3 private-claim profile) - Add jsonschema>=4.21.0 to requirements.txt - Update README: fix mapping table, add audit->warning rationale, add Extension-claim profile section, update test count to 26 - Fix maintainer.email in integration.yaml 26 passed (was 20)
|
Hi @imran-siddique — thank you for the thorough review. All four points are addressed in
Tests: 26 passed (was 20). Happy to make any further adjustments. |
imran-siddique
left a comment
There was a problem hiding this comment.
Thanks for this, @kingztech2019. The comply54 adapter itself is clean and the security posture is good: no eval/exec/subprocess, no network calls, narrow deps, and the signing key is env-provided or ephemeral. Two blocking items before we can merge, plus a few small fixes.
Blocking
- Please remove the new top-level
schema/trace-claim.json. Integrations must be self-contained under their own directory, and perCONTRIBUTING.mdthe TRACE schema lives in the externaltrace-specrepo and is verified via theagentrust-trace-testspackage, not a schema added to this repo. This file is also non-canonical: withadditionalProperties: falseand itsappraisal.status/runtime.platformenums, it would reject the already-mergeddecisionassureintegration (which emitsappraisal.status: "denying",runtime.platform: "software-simulated", and a top-leveltool_transcript). If you need a schema for local tests, put it underintegrations/comply54/tests/fixtures/and repointSCHEMA_PATH. Any real change to the TRACE schema should be proposed upstream intrace-specfirst. - Back the "Level 0 conformance" claim with the real suite. Validating against a schema authored in the same PR is circular. Please run against the published
agentrust-trace-testsat Level 0 and cite the result.
Non-blocking (please fix before merge)
- The PR description maps
audit -> advisory, butadvisoryis not a validappraisal.status(enum isaffirming | warning | contraindicated | none). The code correctly useswarning; just fix the PR-body table. - Reconcile the test count (PR body says 20, README and the file say 26) and the dependency list (requirements.txt has 3 incl.
jsonschema; the body lists 2). - Consider a conformance test over the signed payload.
cnfis added inmain()after the mapping returns, so it is never schema-checked today. jsonschemalooks test-only; consider moving it to a dev/test requirement.
Note: the failing gate check is just the maintainer-approval gate for external PRs, not a problem with your code. validate, CodeQL, and Analyze all pass. Appreciate the contribution and happy to re-review once the schema file is out.
…(PR agentrust-io#11) Blocking agentrust-io#1 — Remove schema/trace-claim.json from repo root. Per CONTRIBUTING.md, the TRACE schema lives in trace-spec upstream and must not be added to this repo. Moves the file to integrations/comply54/tests/fixtures/trace-claim.json where it is a local regression fixture, not a canonical schema claim. SCHEMA_PATH updated accordingly. Blocking agentrust-io#2 — Back the Level 0 conformance claim with agentrust-trace-tests. Adds TestLevel0Conformance (4 tests) that run trace_tests.runner.run() from agentrust-trace-tests 0.1.0 against allow/deny/escalate/audit payloads with cnf.jwk populated. All four Level 0 modules (TR-ENV, TR-SIG, TR-POL) pass with no FAIL findings. TR-SIG correctly reports UNVERIFIED (not FAIL) for plain trace records at Level 0. agentrust-trace-tests replaces jsonschema in requirements.txt (jsonschema remains available transitively and for schema regression tests — install with: pip install -r requirements.txt jsonschema pytest). Total tests: 30 (was 26). README and test count updated. Signed-off-by: oluwajuwon omotayo <ginuxtechacademy@gmail.com>
|
Thanks for the detailed review, @imran-siddique. All six points addressed in c955a0c: Blocking
Non-blocking
All 30 tests pass locally. Happy to address anything else. |
imran-siddique
left a comment
There was a problem hiding this comment.
The integration.yaml manifest is schema-valid and the external links/PyPI package resolve, but the TRACE conformance claim is self-certified against a weakened, repo-local schema, and the adapter's actual output fails the canonical trace-spec schema. That's a direct CONTRIBUTING rule 4 violation and needs to be resolved before merge.
1. Conformance validated against a fabricated, weakened schema (blocking).
The PR adds schema/trace-claim.json at the repo root, but the canonical schema already lives in trace-spec. The submitted copy diverges specifically so the adapter passes. Validating the real adapter output against the canonical schema fails:
- Canonical requires
cnfin top-levelrequired; the submitted schema drops it, andcomply54_to_trace_payload()never setscnf(it's added later inmain(),src/comply54_to_trace.py:379). The conformance test validates the cnf-less payload, so it only "passes" because the local schema doesn't require it. model.weights_digestemits"sha256:not-attested"(src/comply54_to_trace.py:316); canonical enforces^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$, sonot-attestedfails. Submitted schema drops the pattern.build_provenance.digestemits"sha256:not-attested"(src/comply54_to_trace.py:340); same pattern failure, and canonical requiresbuild_provenance = [slsa_level, digest].
agentrust-trace-tests is never invoked, so the "Level 0 conformant / passes schema validation" claim is unsubstantiated. Please validate against the canonical schema (or the trace-tests suite) and fix the payload so it actually conforms, rather than shipping a divergent schema.
2. Out-of-scope layout. CONTRIBUTING mandates one directory per integration under integrations/<vendor>-<product>/ and states this repo is not for code requiring changes to core repos. The root-level schema/trace-claim.json both exceeds the integration directory and creates a second, divergent source of truth that conflicts with trace-spec. Remove it.
3. PR description contradicts the code. The body's mapping table maps audit -> advisory, but advisory is not in the appraisal.status enum (affirming|warning|contraindicated|none). The code and README correctly map audit -> warning (src/comply54_to_trace.py:247), and a test even asserts advisory is invalid. Please fix the description.
4. Doc inconsistencies. PR body says "20 tests" (README/file say 26); says tested against comply54==0.1.0 while integration.yaml sets tested_against.agentrust-trace: "0.2.0"; lists two requirements while requirements.txt has three. Directory is comply54 rather than the <vendor>-<product> convention used by existing integrations.
5. Signature framing (note). By default a fresh Ed25519 key is generated per run and embedded in cnf.jwk, so the JWT is self-signed with an ephemeral unanchored key. Acceptable at Level 0, but the "cryptographically verifiable evidence record" framing overstates what that provides.
Four root-cause fixes:
1. comply54_to_trace_payload() was missing cnf — the function now accepts
an optional key= parameter (defaults to load_or_generate_key()) and
always includes cnf.jwk in the returned payload. Previously cnf was
added post-hoc in main(), so every non-CLI caller produced a cnf-less
payload that failed the canonical schema.
2. Digest placeholders were "sha256:not-attested" — invalid per the
canonical pattern ^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$. Changed
model.weights_digest and build_provenance.digest to "sha256:" + "0"*64
(all-zeros, the correct Level 0 not-attested placeholder).
3. Local fixture schema (tests/fixtures/trace-claim.json) was too weak:
- cnf missing from top-level required array
- required: ["jwk"] missing inside cnf object (so cnf:{} would pass)
- no digest pattern on weights_digest or build_provenance.digest
All three gaps now match the canonical schema constraints.
4. integration.yaml tested_against.agentrust-trace was "0.2.0";
corrected to "0.1.0" (the released version used).
Additional fixes:
- Add test_signature_is_cryptographically_verified: decodes the JWT
using the actual Ed25519 public key (not verify_signature:False),
proving the signing/verification cycle is valid end-to-end.
- Add requirements-dev.txt (jsonschema>=4.0.0, pytest>=7.0.0) to
declare test-only dependencies explicitly rather than relying on
jsonschema being a transitive dep of agentrust-trace-tests.
- README: remove "not-attested" description; update test count to 31;
update test install command to use requirements-dev.txt.
31 tests pass (27 unit/schema + 4 agentrust-trace-tests Level 0).
Signed-off-by: oluwajuwon omotayo <ginuxtechacademy@gmail.com>
|
@imran-siddique — thanks for the detailed second review. The root causes you identified were real and I want to be direct about what went wrong before explaining the fixes. What happened: The original fixture schema ( Fixes in this commit ( 1. 2. Digest placeholders are canonical. 3. Fixture schema is strict.
4. Additional fixes made proactively:
All 31 tests pass. |
|
Following up @kingztech2019. You acknowledged the second review; the changes are still open on your side. Do you plan to push the fixes? Happy to help if anything is blocking you. I will revisit in about a week if I do not hear back. |
|
@imran-siddique — just wanted to point out that all the items from your third review were addressed in commit Quick summary of what landed:
All 31 tests pass. Would appreciate a re-review when you get a chance — happy to address anything further. |
imran-siddique
left a comment
There was a problem hiding this comment.
Apologies for the incorrect follow-up on 07-24, @kingztech2019. You are right: 7fe01c7 landed on July 8, about 90 minutes after my third review, and it addressed every point. That was a gap on our side (the triage keyed off the stale CHANGES_REQUESTED state and missed the commits pushed after the review). We are fixing that.
Re-reviewed the current head:
cnf.jwkis now generated insidecomply54_to_trace_payload(), so every caller gets a conformant payload, not just the CLI.weights_digestandbuild_provenance.digestuse the canonical Level 0 all-zeros placeholder.test_signature_is_cryptographically_verifiedverifies against the public key rather than decoding without verification.- validate, CodeQL, and Python analysis are green; only the maintainer gate remained.
Good to go. Approving and merging now. Thanks for the clean fixes and the direct callout.
What this does
This integration is built on top of
agt-policies-nigeria— the policy pack repo cited in Microsoft AGT. comply54 is the Python/TypeScript enforcement layer built on top of those same Rego policies, packaged as an importable library (pip install comply54). This adapter bridges comply54'sComplianceResultinto a signed TRACE v0.1 JWT (Ed25519, Level 0 software-only).comply54 evaluates AI agent actions against 12 African data protection and financial regulations: NDPA 2023 (Nigeria), CBN Transaction Controls, NFIU AML/MLPPA 2022, KDPA 2019 (Kenya), POPIA (South Africa), Ghana DPA, Rwanda DPA 2021, Egypt PDPL 2020, Ethiopia PDP 2024, Mauritius DPA 2017, Tanzania PDPA 2022, Uganda DPPA 2019, and 5 OWASP Agentic AI universal controls.
The adapter maps the compliance decision into a verifiable TRACE claim so the policy outcome becomes a cryptographic receipt — auditable evidence that the African regulatory policy ran and what it decided.
Files
Decision → Appraisal mapping
overallappraisal.statusallowaffirmingauditwarningescalatewarningdenycontraindicatedReproduce in 2 minutes
Verified output (4 scenarios, tested against comply54==0.1.0 from PyPI)
get_balanceallowaffirmingdenycontraindicatedescalatewarningdenycontraindicatedConformance level
Level 0 (software-only). Hardware TEE fields (
runtime.measurement,model.weights_digest,build_provenance.digest) are explicitly markednot-attestedor software-simulated. No claims are made beyond Level 0.Limitations (stated explicitly)
transparencyis empty — no SCITT log anchor at Level 0.model.*fields reflect what the caller passes via--model. comply54 evaluates policy against the agent's action; it does not independently verify which model ran.cc @imran-siddique — you know the underlying policy work from my AGT Nigeria contribution. comply54 is the enforcement library built on top of those same packs — this adapter makes the compliance decisions TRACE-verifiable. Happy to address any feedback before merge.