Skip to content

build(primaite): qualify and pin the public source and experiment profile #39

Description

@Brad-Edwards

Objective

Establish one immutable, legally usable, runnable PrimAITE source and experiment profile for the RAES study.

Scope

  • Select and pin the public PrimAITE source/wheel identity, shipped use cases, YAML configurations, red/green/blue agents, traffic model, action/observation spaces, reward configuration, and benchmark/notebook material.
  • Record repository/release, package, dependency, Python, scenario, agent, evaluator, seed, and configuration identities and digests.
  • Verify licenses, redistribution/attribution requirements, external downloads, model weights, and retained-output permissions.
  • Reconstruct installation in a clean environment and execute reset, representative action/step, observation, reward/result, and termination behavior.
  • Identify undocumented defaults, incompatible dependencies, known defects, and stochastic entry points.
  • Choose the exact example scenario and public baseline protocol that downstream issues must implement.
  • Isolate dependencies behind the primaite extra; use uv conflicts declarations where simulator stacks are mutually incompatible.
  • Do not patch semantics silently. Any compatibility patch is pinned, explained, and tested against the unmodified source behavior.

Qualification semantics

The maintainer-selected simulator and experiment profile are admitted inputs to the RAES study. Qualification records the exact source and installation route, what RAES can attest or reproduce, stochastic-control coverage, known upstream defects, and limits on claim strength. Packaging gaps, incomplete determinism, compatibility limits, and source defects are disclosed limitations; they do not by themselves veto adapter implementation or require RAES to create behavior the simulator lacks.

Acceptance criteria

  • One immutable runtime/source profile and one public experiment protocol are selected.
  • Clean installation and a source-native smoke run pass through a documented supported route.
  • Exact scenario, agent, evaluator, reset, seed, metric, and termination identities are known.
  • License, attribution, redistribution, archive/maintenance, known-defect, and patch decisions are explicit.
  • Base-only and unrelated simulator extras remain independent.
  • The qualification record grades attainable RAES claims across source identity, configuration/control attestation, run evidence, and outcome reproduction, with every weakened or unsupported claim disclosed without changing the maintainer admission decision.

References

Activity

  1. added
    in-progressAn agent is actively working this issue via /implement
    on Aug 2, 2026
  2. Brad-Edwards commented on Aug 2, 2026

    @Brad-Edwards
    CollaboratorAuthor

    🛠️ Picked up by /implement - driver claude-code, branch 39-qualify-primaite-profile, 2026-08-02T14:59:14.953Z.

  3. Brad-Edwards commented on Aug 2, 2026

    @Brad-Edwards
    CollaboratorAuthor

    gc workflow phase recorded: preflight (issue #39). Posted by the MCP server to enforce ordering between workflow steps (issue #794 MVP-2). Do not edit or delete — used by downstream tools to gate phase prerequisites.

  4. Brad-Edwards commented on Aug 2, 2026

    @Brad-Edwards
    CollaboratorAuthor

    gc workflow phase recorded: plan (issue #39). Posted by the MCP server to enforce ordering between workflow steps (issue #794 MVP-2). Do not edit or delete — used by downstream tools to gate phase prerequisites.

    Plan — qualify and pin the public PrimAITE source and experiment profile

    Requirement-free qualification run (no REP-* in scope; governance via
    --skip-requirement). This mirrors the CyberBattleSim (#54) and CybORG (#56)
    qualification precedent: a backend-local immutable qualification record plus one
    selected public experiment protocol under raes_adapters.primaite. No adapter,
    SDL, backend manifest, or conformance claim is implemented here. The Step 2.5
    guardrails (docs/decisions/primaite-qualification-guardrails.md) are binding.

    Selected immutable source profile (already investigated, real evidence)

    • Repository Autonomous-Resilient-Cyber-Defence/PrimAITE, tag v4.0.0
      (annotated tag f6513362e9882217fb90595de51e7ca659ee1416), commit
      98617981d7f6ae2c3ffd9a8cc39944e05c9a09ea, tree
      3928ab452c51206ba8706b3f575e31fe99eb842e.
    • Distribution primaite==4.0.0, requires-python >=3.9,<3.13. Not on PyPI
      — the README documents wheel-from-GitHub install only (packaging gap, same
      class as CyberBattleSim). Import root primaite canonicalized to
      sha256 9fe7ba15… over 234 files; the built wheel's tree matches the source
      tree exactly.
    • License: MIT, Crown copyright 2023 DSTL UK. GitHub reports NOASSERTION
      because the LICENSE preamble is mangled ("MIT License License …"); the
      grant/warranty clauses are standard MIT and the string propagates into wheel
      metadata (recorded finding).

    Selected public experiment protocol

    • Shipped use case data_manipulation.yaml (metadata.version 3.0,
      max_episode_length 128), driven through the source-native Gymnasium
      entrypoint primaite.session.environment.PrimaiteGymEnv.
    • Participants: BLUE defender (proxy-agent, the RL-controlled seat), RED
      data_manipulation_attacker (red-database-corrupting-agent, scripted), two
      GREEN probabilistic-agent users. Baseline participant = scriptable policy
      over the action space (do-nothing baseline used for the smoke).
    • Spaces (observed): action Discrete(78); observation flattened
      Box(shape=[1652], int64, 0..1). Reset arity 2 (ndarray obs + empty info),
      step arity 5. Reward is a float.
    • Termination: step() hardcodes terminated=False; the episode ends only
      by truncated=True when step_counter >= max_episode_length (128).
      Fixed-horizon, truncation-only — no source-native success/failure terminated
      condition in this scenario.

    Clean install + source-native smoke (documented supported route, verified)

    • Build the wheel from pinned source, install into a fresh venv without the
      rl extra (README-documented option), pin setuptools==75.6.0 (upstream
      dev pin; supplies the pkg_resources module the runtime imports), run under an
      isolated HOME, cleared PYTHONPATH, PYTHONSAFEPATH=1, no post-install
      network. Qualified on Python 3.11.15 (classifier/README supported range).
    • Smoke (do-nothing baseline, observed identical across 3 fresh-process runs
      despite uncontrolled green RNG): representative first step reward 0.65,
      terminated=false, truncated=false; full episode 128 steps ending
      truncated=true, final step reward -0.8, cumulative -57.05. Recorded as
      observed-stable bounded evidence, not a seed-controlled determinism claim.

    Recorded findings / limitations (all graded, none vetoing)

    1. Undeclared runtime dependency on pkg_resources/setuptools: breaks with
      setuptools>=81 (module removed) and on default Python-3.12 venvs (no
      setuptools seeded). Qualified route pins setuptools==75.6.0.
    2. reset(seed=int) raises KeyError('torch') — set_random_seed dereferences
      sys.modules["torch"] unconditionally, so the public seed seam is unusable
      without the heavy rl/torch stack. Seeded replay is therefore not attestable
      on the light route.
    3. requires-python <3.13 (metadata) vs classifiers/README <3.12
      inconsistency; qualified on 3.11.
    4. typer[all] extra removed upstream (benign warning); primaite CLI also
      imports undeclared click, so primaite setup/CLI is broken on the no-rl
      route — the Gym runtime does not need it (import creates app dirs).
    5. jupyterlab==3.6.1 is a core (non-optional) dependency, so even the
      no-rl install resolves 133 packages. Upstream ships no lockfile and uses loose
      constraints, so the graph is a dated resolved snapshot (normalized
      sha256 b05fa4c0…), not reproducible. All licenses permissive/weak-copyleft
      (MIT/BSD/Apache/ISC/PSF/MPL-2.0), no strong copyleft; one transitive dep
      (ypy-websocket 0.8.4) declares no license.
    6. Green ProbabilisticAgent.rng seeds from the uncontrolled global NumPy RNG;
      documented as a stochastic source.

    Files

    • src/raes_adapters/primaite/__init__.py — load_qualification() +
      read_public_protocol() accessors (mirror cyberbattlesim).
    • src/raes_adapters/primaite/qualification.json — the immutable record: source
      identity, runtime source tree, source-file digests, wheel/sdist evidence,
      Python/host runtime, clean-install + bounded smoke, protocol selection,
      stochastic sources, dependency resolution + license summary + full resolved
      graph, legal, patches (none), maintenance, known defects, packaging decision,
      admission + graded claim strength.
    • src/raes_adapters/primaite/public-protocol.md — the selected protocol prose
      (its sha256 is asserted by the record).
    • tests/test_primaite_qualification.py — assert record identity, spaces,
      termination model, smoke bounds, stochastic disclosure, legal/patch
      dispositions, admission, and extras["primaite"] == [].
    • pyproject.toml — add dependency-light primaite = [] extra; uv lock to
      register it (no second lockfile; no conflicts needed while the extra is
      empty — the incompatible gymnasium==0.28.1/numpy~=1.23/pydantic==2.7.0
      pins never enter the lock because the source installs separately).
    • README.md — list PrimAITE among qualified backends.
    • Kept from preflight: docs/decisions/primaite-qualification-guardrails.md,
      docs/index.md, mkdocs.yml.

    Bounded-artifact rule honored: no native observations, traffic contents, action
    ids, reward vectors, object reprs, paths, or tracebacks enter the record — only
    types, shapes, dtypes, booleans, scalar measures, and stable digests.

    Verification

    uv run --frozen ruff + mypy src + full nox -s verify (hygiene, policy with
    --skip-requirement, lint, tool-tests, typecheck, tests across base + each
    extra alone incl. primaite, distributions clean-install/identity, strict
    docs). Coverage ≥ 80%. No CHANGELOG.md/version edits (Release Please owns
    them); PR title will be a valid Conventional Commit (build(primaite): …).

  5. Brad-Edwards commented on Aug 2, 2026

    @Brad-Edwards
    CollaboratorAuthor

    gc_codex_review — cycle 1 of 1 (pre-push) on issue #39 (branch 39-qualify-primaite-profile)
    Diff mode: inline — the complete diff was supplied in one prompt

    Core review

    Verdict: ship

    This is shaped correctly: it adds backend-local PrimAITE qualification evidence and a source-native protocol without promoting backend facts into RAES or base semantics. The empty optional extra accurately avoids asserting installability for an unpublished upstream artifact, while the resource accessors match the existing qualification-evidence seam. The record explicitly bounds reproducibility claims and preserves the obvious next variation—another module-local profile selection—without introducing a registry or premature abstraction.

    No blocking findings.

    Security review

    Verdict: ship

    This change adds backend-local PrimAITE qualification evidence, a dependency-light optional extra, and package-resource accessors without introducing a runtime service, untrusted-input boundary, or execution path. The design seam is correctly contained in raes_adapters.primaite; the record documents the separate upstream-install limitation rather than silently acquiring or executing remote content. I found no concrete, exploitable security regression in the supplied diff.

    No blocking findings.

  6. Brad-Edwards commented on Aug 2, 2026

    @Brad-Edwards
    CollaboratorAuthor

    gc_codex_review pre-push cycle 1 of 1 complete for issue #39 on branch '39-qualify-primaite-profile'. Posted by the MCP server to enforce the pre-push hard-cap-1 contract (issues #796, #804, #906). Do not edit or delete — used by the next gc_codex_review (uncommitted) invocation to count cycles.

  7. Brad-Edwards commented on Aug 2, 2026

    @Brad-Edwards
    CollaboratorAuthor

    Review decision record — codex cycle 1 (issue #39)

    Reviewer: codex
    Cycle: 1

    Architectural read:

    Core reviewer: This is shaped correctly: it adds backend-local PrimAITE qualification evidence and a source-native protocol without promoting backend facts into RAES or base semantics. The empty optional extra accurately avoids asserting installability for an unpublished upstream artifact, while the resource accessors match the existing qualification-evidence seam. The record explicitly bounds reproducibility claims and preserves the obvious next variation—another module-local profile selection—without introducing a registry or premature abstraction.

    Security reviewer: This change adds backend-local PrimAITE qualification evidence, a dependency-light optional extra, and package-resource accessors without introducing a runtime service, untrusted-input boundary, or execution path. The design seam is correctly contained in raes_adapters.primaite; the record documents the separate upstream-install limitation rather than silently acquiring or executing remote content. I found no concrete, exploitable security regression in the supplied diff.

    Blocking findings: 0 (clean run)

  8. Brad-Edwards commented on Aug 2, 2026

    @Brad-Edwards
    CollaboratorAuthor

    gc_test_quality_review cycle 1 of 1 — issue #39

    Reviewer: test-quality (claude-sonnet-5 via gc_test_quality_review)
    Branch: 39-qualify-primaite-profile
    Cycle: 1 / 1
    Findings: 0 (clean run)

  9. Brad-Edwards commented on Aug 2, 2026

    @Brad-Edwards
    CollaboratorAuthor

    gc_test_quality_review cycle 1 of 1 complete for issue #39 on branch '39-qualify-primaite-profile'. Posted by the MCP server to enforce the gc_test_quality_review hard-cap-1 contract (issue #884 follow-up, default lowered in #906). Do not edit or delete — used by the next gc_test_quality_review invocation to count cycles.

  10. Brad-Edwards commented on Aug 2, 2026

    @Brad-Edwards
    CollaboratorAuthor

    Review decision record — test-quality cycle 1 (issue #39)

    Reviewer: test-quality
    Cycle: 1

    Architectural read:

    Both files are golden-record tests over qualification-evidence modules whose entire implementation is "read a packaged resource and parse/return it verbatim" — there's no business logic to exercise, so pinning the record's exact content is the correct test shape here, not a shortcut around one. The primaite module deliberately mirrors the cyborg qualification accessor's shape (load_qualification/read_*) while explicitly declining to implement an adapter yet, per the ADR — a sound seam that keeps qualification evidence decoupled from adapter construction and doesn't foreclose the adapter work that will presumably follow (as it did for cyborg). Where independent verification is possible without a full external reproduction run, the tests do it (dependency-digest recomputation, patch-content hashing, pyproject.toml cross-checks) rather than just re-asserting literals pulled from the same JSON. The expensive, real reproduction (git clone, wheel builds, subprocess) is correctly isolated in tools/verify_cyborg_qualification.py, outside the pytest suite, so these aren't integration tests in unit-test clothing. I'd ship this as-is.

    Blocking findings: 0 (clean run)

  11. Brad-Edwards commented on Aug 2, 2026

    @Brad-Edwards
    CollaboratorAuthor

    Pre-PR base synchronization

    • Source: refs/remotes/origin/dev at 0f20e975fb7e3f583f4bd6bee1be30da78919eca
    • Outcome: already_current
    • Published feature head: b7cf56c05858ed46603ac3b242d7657ab2e2bfa7
    • Verified tree: 5bc7da9073c6520bd39a265cfa40bb243a2d8afb
  12. Brad-Edwards commented on Aug 2, 2026

    @Brad-Edwards
    CollaboratorAuthor

    Ready for review — issue #39

    PR: #67
    Plan: #39 (comment)

    Outcome

    PrimAITE v4.0.0 is qualified and pinned as backend-local evidence under raes_adapters.primaite, with a bounded source-native smoke and graded limitations. Bundled at maintainer request: raes upgraded to 3.3.0 (env-packs 3.6.2) and the active GitHub identity reconciled to OpenRAE/adapters (Sonar key and digest-pinned historical evidence unchanged). Branch is merged current with dev (incl. NASim #68). Base and all four simulator extras stay independently installable; no existing backend's runtime behavior changes.

    Files changed

    Added:

    • docs/decisions/primaite-qualification-guardrails.md
    • src/raes_adapters/primaite/__init__.py
    • src/raes_adapters/primaite/public-protocol.md
    • src/raes_adapters/primaite/qualification.json
    • tests/test_primaite_qualification.py

    Modified:

    • .ground-control.yaml
    • README.md
    • docs/decisions/identity-register.yaml
    • docs/index.md
    • docs/maintainers/project-services.md
    • mkdocs.yml
    • pyproject.toml
    • src/raes_adapters/cyborg/qualification.json
    • tests/test_cyberbattlesim_qualification.py
    • tests/test_cyborg_conformance.py
    • tests/test_cyborg_qualification.py
    • tests/test_nasim_qualification.py
    • tools/check_project_services.py
    • tools/tests/test_identity_policy.py
    • uv.lock

    Reviews

    • codex: Cycle 1 clean (0 findings) on the PrimAITE core; the raes 3.x + OpenRAE delta exceeded pre-push cap-1, over-cap re-review declined by maintainer, accepted on green full-verify + identity gates.
    • test-quality: Cycle 1 clean (0 findings) on the PrimAITE qualification tests.

    Traceability reconciliation

    • Pending — requirement status transition and IMPLEMENTS/TESTS reconciliation run in Phase E once the PR merges.

    Status

    • CI: ✅ green
    • SonarCloud: ✅ passed
    • PR ready for user review and merge. Ground Control reconciliation (requirement status + traceability) runs on merge (Phase E).
  13. Brad-Edwards commented on Aug 2, 2026

    @Brad-Edwards
    CollaboratorAuthor

    gc workflow phase recorded: traceability_reconciled (issue #39). Posted by the MCP server to enforce ordering between workflow steps (issue #794 MVP-2). Do not edit or delete — used by downstream tools to gate phase prerequisites.

    no in-scope requirements; touched-files audit clean.

  14. Brad-Edwards commented on Aug 2, 2026

    @Brad-Edwards
    CollaboratorAuthor

    Final report — issue #39 complete

    PR: #67
    Plan: #39 (comment)

    Outcome

    PrimAITE v4.0.0 is qualified and pinned as backend-local evidence under raes_adapters.primaite, with a bounded source-native smoke and graded limitations. Bundled at maintainer request: raes upgraded to 3.3.0 (env-packs 3.6.2) and the active GitHub identity reconciled to OpenRAE/adapters (Sonar key and digest-pinned historical evidence unchanged). Merged to dev in PR #67. Base and all four simulator extras stay independently installable; no existing backend's runtime behavior changes.

    Files changed

    Added:

    • docs/decisions/primaite-qualification-guardrails.md
    • src/raes_adapters/primaite/__init__.py
    • src/raes_adapters/primaite/public-protocol.md
    • src/raes_adapters/primaite/qualification.json
    • tests/test_primaite_qualification.py

    Modified:

    • .ground-control.yaml
    • README.md
    • docs/decisions/identity-register.yaml
    • docs/index.md
    • docs/maintainers/project-services.md
    • mkdocs.yml
    • pyproject.toml
    • src/raes_adapters/cyborg/qualification.json
    • tests/test_cyberbattlesim_qualification.py
    • tests/test_cyborg_conformance.py
    • tests/test_cyborg_qualification.py
    • tests/test_nasim_qualification.py
    • tools/check_project_services.py
    • tools/tests/test_identity_policy.py
    • uv.lock

    Reviews

    • codex: Cycle 1 clean (0 findings) on the PrimAITE core; raes 3.x + OpenRAE delta exceeded pre-push cap-1, over-cap re-review declined by maintainer, accepted on green full-verify + identity gates.
    • test-quality: Cycle 1 clean (0 findings) on the PrimAITE qualification tests.

    Traceability reconciliation

    • IMPLEMENTS / TESTS / DOCUMENTS added: 0
    • Links updated: 0
    • Stale links removed: 0

    Requirement-free qualification run (no REP-* in scope; no new structural gate). No IMPLEMENTS/TESTS links to reconcile.

    Status

    • CI: ✅ green
    • SonarCloud: ✅ passed
    • PR ready for user review and merge.
  15. removed
    in-progressAn agent is actively working this issue via /implement
    on Aug 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions