Skip to content

feat(cyborg): implement logical step control and action translation #16

Closed

Description

@Brad-Edwards

Objective

Drive the CybORG step loop from compiled RAES orchestration/action contracts while preserving the declared execution controls.

Scope

  • Implement logical step, trial-length, turn-order, reset, and terminal-condition handling for red, blue, green, backend, and evaluator operations.
  • Translate admitted RAES action contracts and normalized arguments to pinned CybORG driver calls.
  • Project execution status, operation receipts, participant histories, and mapped shared-state transitions back into RAES contracts.
  • Preserve action-instance, participant, episode, sequence, target, precondition, effect, and receipt joins.
  • Reject unmapped actions/arguments and invalid native outputs before they cross the adapter boundary.
  • Record seed/stochastic-control disclosures without claiming determinism CybORG cannot provide.

Acceptance criteria

  • B-line, Meander, Sleep, representative blue actions, and green behavior execute with the declared ordering.
  • Trial lengths 30, 50, and 100 terminate at the specified logical step.
  • Every admitted action joins to one driver operation and one typed terminal result.
  • Invalid/unmapped actions have no simulator effect and return bounded RAES diagnostics.
  • Replaying a controlled seed suite reproduces the declared control facts; uncontrolled variation is disclosed.
  • No Gym/PettingZoo tuple, action ID, raw log, hidden truth, reward vector, or native object appears in portable output.

References

Activity

  1. Brad-Edwards commented on Jul 31, 2026

    @Brad-Edwards
    CollaboratorAuthor

    🛠️ Picked up by /implement - driver codex, branch 16-logical-step-control, 2026-07-31T14:49:48.256Z.

  2. Brad-Edwards commented on Jul 31, 2026

    @Brad-Edwards
    CollaboratorAuthor

    gc workflow phase recorded: preflight (issue #16). 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.

  3. Brad-Edwards commented on Jul 31, 2026

    @Brad-Edwards
    CollaboratorAuthor

    gc workflow phase recorded: plan (issue #16). 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.

    Implementation plan

    1. Add red-first tests in tests/test_cyborg_execution.py for the issue contract: exact action/argument admission with zero native effects on rejection; one aggregate driver call and one typed terminal result per valid action; pinned Blue→Green→Red participant ordering; B-line, Meander, Sleep, and green occurrences; logical trial cutoffs at 30/50/100; native-terminal handling; reset/quarantine behavior; controlled-seed disclosures; and hostile native values proving no tuple, action id, reward, hidden truth, log, repr, or exception text crosses the boundary.
    2. Extend the module-private CyborgDriver/provisioner ownership seam so construction, policy-bound reconstruction, reset, step, quarantine, stop, and cleanup share one re-entrant mutation lock and one active native handle. Keep lazy fixed imports for the pinned CybORG source, closed exact action-address dispatch, normalized argument conversion, caller random-state restoration, and bounded failures.
    3. Add CyborgOrchestrator to admit one published RAES episode-control contract carried by the compiled workflow operation, reconcile workflow status/history through published RAES shapes, bind one initialized logical clock and the selected red variant, and prevent participant execution until an admitted run exists. It will not expose a public step method or advertise an Evaluator.
    4. Add CyborgParticipantRuntime on BaseParticipantRuntime. Reuse RAES episode/action/history commit behavior, require the validated normalized selection, trigger at most one aggregate turn for the admitted external blue action, project internal red/green occurrences as distinct joined participant events, commit mapped shared-state/joint-action/time records, and advance the RAES clock exactly once only after complete projection validation. Invalid post-step output quarantines the native session; pre-effect failures preserve both snapshot and driver-call count.
    5. Extend manifest.py, target.py, and __init__.py with only the orchestrator/participant/time capabilities and published contract IDs actually exercised. Preserve the single distribution, closed target config, provisioning realization envelope, and absent evaluator. Update README/API documentation only where the new public target capability needs discovery; retain the preflight guardrail note and MkDocs navigation.
    6. Verify each acceptance criterion against exact source/test lines, run focused execution/provisioner/manifest tests with ACES_REQUIREMENT_UID=REP-004, then run the managed completion gate and canonical uv tool run --from 'nox[uv]==2026.4.10' nox -f noxfile.py -s verify graph. No changelog, new schema/profile/vocabulary, second lockfile, native dependency, or backend-specific SDL change is planned.

    Whole-repository design checks

    • Security: action request type, live episode/run, exact contract address, normalized argument shape, target closure, trial bound, time state, driver result shape, portable models, and final snapshot transition are validated at their existing layers. No auth/network surface or secret input is added; native values and exceptions are default-deny.
    • Maintainability: reuse CyborgProvisioner, SourceInstalledCyborgDriver, BaseParticipantRuntime, ReferenceTimeRuntime, base clock/projection/redaction helpers, manifest/target factories, and the existing nox/test patterns.
    • Extensibility: fixed private translator functions are selected by exact admitted action addresses. A reviewed action/source profile or red variant adds one evidence-backed binding without changing portable contracts, receipts, diagnostics, or session ownership.
    • Scope/claims: RAES owns the semantic contracts. Full declared precondition/effect validation and direct receipt/action foreign-key projection remain explicitly unavailable until RAES exposes the necessary public context; no local DTO or fabricated receipt fills those gaps.
  4. Brad-Edwards commented on Jul 31, 2026

    @Brad-Edwards
    CollaboratorAuthor

    gc_codex_review — cycle 1 of 1 (pre-push) on issue #16 (branch 16-logical-step-control)
    Diff mode: inline — the complete diff was supplied in one prompt

    Core review

    Verdict: don't-ship

    The intended shape is sound: published RAES orchestration, participant, and time contracts remain the portable boundary, while native action translation and aggregate-turn mechanics stay inside the CybORG module. The production seam is not yet preserved, however. Native lifecycle mutations, portable state construction, and logical-time updates are split across independently locked components, so they are neither serialized nor committed atomically; target-bearing actions also bypass the required realized-target closure. Those are boundary-contract failures rather than local polish, and they affect the obvious next variations—concurrent control-plane calls, additional target-bearing actions, and multi-trial reset.

    Blocking findings (3):

    1. [class] Native and portable transitions are not one serialized transaction — src/raes_adapters/cyborg/participant_runtime.py:155
      The effectful native turn completes under the provisioner's lock, but that lock is released before logical-time advancement, construction of the shared-state/joint-action records, workflow completion, and final runtime validation. A failure in any post-step operation—for example, a missing red/green episode causing _portable_turn to raise, model construction failing, or the manager rejecting a hand-built record—can restore the baseline portable snapshot while leaving the mutated native handle available. The separate execution-control lock also permits start or reset to interleave with this gap. Use one module-private re-entrant session coordinator for start, turn, and reset, validate the complete candidate portable state before publishing it, and quarantine the native session on every failure after an effectful call.
    2. [class] Target-bearing actions reach CybORG without realized-target validation — src/raes_adapters/cyborg/driver.py:285
      validate_action_selection establishes only that hostname is a string; Analyse, Remove, and Restore then forward that value directly to native constructors. A normalized but unrealizable hostname therefore triggers an aggregate native turn—including red and green effects—instead of being rejected before the driver call. Resolve each portable target through the active scenario realization and admit only an exact mapped native hostname before constructing any target-bearing action.
    3. [one-off] Coordinated reset leaves the logical trial clock exhausted — src/raes_adapters/cyborg/participant_runtime.py:126
      After resetting all participant episodes and the native aggregate, this path only clears the source-terminal latch. It never applies the declared new_segment_zero time reset. After a trial reaches max_steps, a successful reset therefore leaves the clock at the limit and the next action is immediately rejected as trial-complete. Include the RAES time transition in the same coordinated reset transaction and test reset followed by a successful first turn after an exhausted trial.

    Security review

    Verdict: ship

    This change is shaped correctly around the existing RAES runtime seam: validated portable orchestration and participant inputs cross into a module-private CybORG session through fixed action and policy dispatch, while native results are projected through bounded allowlists and failures are returned without native exception text or object representations. It adds no network, authentication, persistence, filesystem-input, shell, or caller-controlled import surface, and the obvious extensions remain additional reviewed bindings rather than dynamic execution paths. I found no concrete, exploitable security issue introduced by this diff.

    No blocking findings.

  5. Brad-Edwards commented on Jul 31, 2026

    @Brad-Edwards
    CollaboratorAuthor

    gc_codex_review pre-push cycle 1 of 1 complete for issue #16 on branch '16-logical-step-control'. 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.

  6. Brad-Edwards commented on Jul 31, 2026

    @Brad-Edwards
    CollaboratorAuthor

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

    Reviewer: codex
    Cycle: 1

    Architectural read:

    Core reviewer: The intended shape is sound: published RAES orchestration, participant, and time contracts remain the portable boundary, while native action translation and aggregate-turn mechanics stay inside the CybORG module. The production seam is not yet preserved, however. Native lifecycle mutations, portable state construction, and logical-time updates are split across independently locked components, so they are neither serialized nor committed atomically; target-bearing actions also bypass the required realized-target closure. Those are boundary-contract failures rather than local polish, and they affect the obvious next variations—concurrent control-plane calls, additional target-bearing actions, and multi-trial reset.

    Security reviewer: This change is shaped correctly around the existing RAES runtime seam: validated portable orchestration and participant inputs cross into a module-private CybORG session through fixed action and policy dispatch, while native results are projected through bounded allowlists and failures are returned without native exception text or object representations. It adds no network, authentication, persistence, filesystem-input, shell, or caller-controlled import surface, and the obvious extensions remain additional reviewed bindings rather than dynamic execution paths. I found no concrete, exploitable security issue introduced by this diff.

    Blocking findings: 3

    Finding 1 — class (3 instances)

    • ID: F1
    • Title: [core] Native and portable transitions are not one serialized transaction
    • Location: src/raes_adapters/cyborg/participant_runtime.py:155
    • Decision: fix
    • Rationale: The effectful native turn completes under the provisioner's lock, but that lock is released before logical-time advancement, construction of the shared-state/joint-action records, workflow completion, and final runtime validation. A failur…
    • Instances:
      • src/raes_adapters/cyborg/orchestrator.py:94
      • src/raes_adapters/cyborg/participant_runtime.py:112
      • src/raes_adapters/cyborg/participant_runtime.py:155

    Finding 2 — class (3 instances)

    • ID: F2
    • Title: [core] Target-bearing actions reach CybORG without realized-target validation
    • Location: src/raes_adapters/cyborg/driver.py:285
    • Decision: fix
    • Rationale: validate_action_selection establishes only that hostname is a string; Analyse, Remove, and Restore then forward that value directly to native constructors. A normalized but unrealizable hostname therefore triggers an aggregate native t…
    • Instances:
      • src/raes_adapters/cyborg/driver.py:285
      • src/raes_adapters/cyborg/driver.py:289
      • src/raes_adapters/cyborg/driver.py:293

    Finding 3 — one-off

    • ID: F3
    • Title: [core] Coordinated reset leaves the logical trial clock exhausted
    • Location: src/raes_adapters/cyborg/participant_runtime.py:126
    • Decision: fix
    • Rationale: After resetting all participant episodes and the native aggregate, this path only clears the source-terminal latch. It never applies the declared new_segment_zero time reset. After a trial reaches max_steps, a successful reset therefor…
  7. Brad-Edwards commented on Jul 31, 2026

    @Brad-Edwards
    CollaboratorAuthor

    gc_test_quality_review cycle 1 of 1 — issue #16

    Reviewer: test-quality (claude-sonnet-5 via gc_test_quality_review)
    Branch: 16-logical-step-control
    Cycle: 1 / 1
    Findings: 1

    Finding 1 — [critical] src/raes_adapters/cyborg/driver.py:250

    Problem: SourceInstalledCyborgDriver.step(), _native_blue_action(), _project_turn(), the module-level _project_native_action() mapping table, and construct_execution()'s red-agent import/binding branch are new production code in this diff, but no test in the repository ever calls them. tests/test_cyborg_execution.py uses FakeExecutionDriver, whose step()/construct_execution() hand-build the same _NativeTurnResult tuples the real driver is supposed to derive from CybORG's native Results/Observation objects, instead of exercising the real translation. tests/test_cyborg_provisioner.py's only real-driver test (test_driver_uses_generated_document_deletes_it_and_preserves_global_rng) drives construct(), never construct_execution() or step().
    Why it matters: A regression in the real translation — e.g. inverting the observation.data.get("success") == TrinaryEnum.TRUE check, breaking the type(action) is native_type ordering in _project_native_action so a red exploit/impact action is misclassified as sleep, or passing wrong constructor kwargs in _native_blue_action — would not fail a single test, even though this is precisely the boundary the adapter exists to translate correctly for a security-research/RL environment.
    Fix: Add driver-level tests that construct a fake native handle (mirroring the existing FakeNative pattern) exposing step()/get_last_action(), and fake CybORG.Shared/.Actions/.Enums modules via monkeypatching driver_module.import_module (as already done in test_source_tree_is_verified_before_any_cyborg_import), so _native_blue_action, _project_turn, and _project_native_action run against real code for each supported action type plus the error branches (non-Results type, non-null error, unmapped native action type). Add a construct_execution test that exercises the unsupported-variant and agent-import-failure RuntimeError branches. Add an orchestrator-level test where FakeExecutionDriver.construct_execution raises, to cover the 'cyborg-backend.orchestration.session-construction-failed' path in CyborgOrchestrator._start_transaction, which is currently unreachable because every fake driver's construct_execution unconditionally succeeds.

  8. Brad-Edwards commented on Jul 31, 2026

    @Brad-Edwards
    CollaboratorAuthor

    gc_test_quality_review cycle 1 of 1 complete for issue #16 on branch '16-logical-step-control'. 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.

  9. Brad-Edwards commented on Jul 31, 2026

    @Brad-Edwards
    CollaboratorAuthor

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

    Reviewer: test-quality
    Cycle: 1

    Architectural read:

    The orchestration/participant-runtime layer added in this diff (orchestrator.py, participant_runtime.py, and the new provisioner.py execution-transaction methods) is tested to an unusually high standard: the state-machine transitions, quarantine-and-recovery flow, portable/native leak boundary (the HostileNative/HostileNativeHandle trick that fails the test if a native handle is ever str()'d or repr()'d), and the cross-thread locking guarantee (via real Threads synchronized with Events, not sleeps) are all exercised with assertions on actual return values and snapshot state, not just mock-call checks. That is the correct design seam for isolating this logic from CybORG, which the project clearly cannot assume is installed in CI (confirmed by the separate qualification/ledger tooling in tools/verify_cyborg_qualification.py and tests/test_cyborg_qualification_driver.py, which validate an offline reproduction evidence shape rather than running live). The gap is that the same seam that makes the orchestration layer testable also hides the ~150 new lines of native-translation logic this diff adds to driver.py (step, _native_blue_action, _project_turn, _project_native_action, and construct_execution's red-agent binding): FakeExecutionDriver in test_cyborg_execution.py independently re-derives the exact _NativeTurnResult/_NativeParticipantOccurrence values the real driver is supposed to compute from actual CybORG Results/Observation objects, rather than driving the real code. The repo already has the right pattern for closing this gap (test_driver_uses_generated_document_deletes_it_and_preserves_global_rng builds a FakeNative object and monkeypatches _native_binding to unit-test construct() without a real CybORG install); it just wasn't extended to step()/_project_turn() or to construct_execution()'s agent-resolution failure paths.

    Blocking findings: 1

    Finding 1 — class (6 instances)

    • ID: F1
    • Title: (no title)
    • Decision: fix
    • Rationale: Addressed by next cycle
    • Instances:
      • src/raes_adapters/cyborg/driver.py: SourceInstalledCyborgDriver.step (never invoked by any test)
      • src/raes_adapters/cyborg/driver.py: SourceInstalledCyborgDriver._native_blue_action (never invoked by any test)
      • src/raes_adapters/cyborg/driver.py: SourceInstalledCyborgDriver._project_turn (never invoked by any test)
      • src/raes_adapters/cyborg/driver.py: module-level _project_native_action mapping table (never invoked by any test)
      • src/raes_adapters/cyborg/driver.py: SourceInstalledCyborgDriver.construct_execution red-agent import/binding branch, including both RuntimeError paths (never invoked by any test)
      • src/raes_adapters/cyborg/orchestrator.py: CyborgOrchestrator._start_transaction's 'cyborg-backend.orchestration.session-construction-failed' branch (configure_execution() returning False is never triggered in tests/test_cyborg_execution.py because FakeExecutionDriver/BlockingExecutionDriver.construct_execution always succeeds)
  10. Brad-Edwards commented on Jul 31, 2026

    @Brad-Edwards
    CollaboratorAuthor

    Pre-PR base synchronization

    • Source: refs/remotes/origin/dev at c641cab7f0bb0664d54a9e7118686ac6887f49ca
    • Outcome: merged_clean
    • Published feature head: 0ba620942aef3f8fb98edf8271afcc5286d7b108
    • Verified tree: 8caa2edb25516552b65d927015ba76b9efa60468
  11. Brad-Edwards commented on Jul 31, 2026

    @Brad-Edwards
    CollaboratorAuthor

    Pre-PR base synchronization

    • Source: refs/remotes/origin/dev at c641cab7f0bb0664d54a9e7118686ac6887f49ca
    • Outcome: already_current
    • Published feature head: cbc0a2f77243d310238bd9f33ee40ac66df581d6
    • Verified tree: b943f6ddfb15a898ea45188c731c0803b431cddb
  12. Brad-Edwards commented on Jul 31, 2026

    @Brad-Edwards
    CollaboratorAuthor

    Ready for review — issue #16

    PR: #61
    Plan: #16 (comment)

    Outcome

    CAGE-2 workflows now execute one serialized aggregate native turn per admitted blue action, stop at the declared logical-step or source terminal boundary, project only bounded portable red/green/blue records, and reset all participants with the workflow and logical clock as one coordinated session.

    Implemented CAGE-2 logical step control through published RAES provisioner, orchestrator, participant, and time contracts; canonical verification, all 188 tests, CI, CodeQL, and SonarCloud pass.

    Files changed

    Added:

    • docs/decisions/cyborg-step-control-guardrails.md
    • src/raes_adapters/cyborg/orchestrator.py
    • src/raes_adapters/cyborg/participant_runtime.py
    • tests/test_cyborg_execution.py

    Modified:

    • README.md
    • mkdocs.yml
    • pyproject.toml
    • src/raes_adapters/cyborg/__init__.py
    • src/raes_adapters/cyborg/driver.py
    • src/raes_adapters/cyborg/manifest.py
    • src/raes_adapters/cyborg/provisioner.py
    • src/raes_adapters/cyborg/target.py
    • tests/test_cyborg_provisioner.py

    Reviews

    • codex: One pre-push production-readiness cycle completed; shared transaction ownership, realized-target closure, and logical-clock reset findings were fixed and verified.
    • test-quality: One pre-push test-quality cycle completed; concrete source-driver construction, action-binding, projection, RNG, and failure-path coverage was added and verified.

    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 Jul 31, 2026

    @Brad-Edwards
    CollaboratorAuthor

    gc workflow phase recorded: traceability_reconciled (issue #16). 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 Jul 31, 2026

    @Brad-Edwards
    CollaboratorAuthor

    Final report — issue #16 complete

    PR: #61
    Plan: #16 (comment)

    Outcome

    CAGE-2 workflows can now execute one serialized aggregate native turn per admitted blue action, stop at the declared logical-step or source terminal boundary, project only bounded portable red/green/blue records, and reset all participants with the workflow and logical clock as one coordinated session.

    Implemented CAGE-2 logical step control through published RAES provisioner, orchestrator, participant, and time contracts; canonical verification, all 188 tests, CI, CodeQL, and SonarCloud passed before merge.

    Files changed

    Added:

    • docs/decisions/cyborg-step-control-guardrails.md
    • src/raes_adapters/cyborg/orchestrator.py
    • src/raes_adapters/cyborg/participant_runtime.py
    • tests/test_cyborg_execution.py

    Modified:

    • README.md
    • mkdocs.yml
    • pyproject.toml
    • src/raes_adapters/cyborg/__init__.py
    • src/raes_adapters/cyborg/driver.py
    • src/raes_adapters/cyborg/manifest.py
    • src/raes_adapters/cyborg/provisioner.py
    • src/raes_adapters/cyborg/target.py
    • tests/test_cyborg_provisioner.py

    Reviews

    • codex: One pre-push production-readiness cycle completed; shared transaction ownership, realized-target closure, and logical-clock reset findings were fixed and verified.
    • test-quality: One pre-push test-quality cycle completed; concrete source-driver construction, action-binding, projection, RNG, and failure-path coverage was added and verified.

    Traceability reconciliation

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

    Issue #16 has no canonical in-scope Requirements section. Post-merge reverse lookups found no existing requirement links on the issue or any touched artifact, so reconciliation required no link changes.

    Status

    • CI: ✅ green
    • SonarCloud: ✅ passed
    • PR ready for user review and merge.
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

    in-progressAn agent is actively working this issue via /implement

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions