PROD-250: feat(sandbox): add governed command integration - #22
Open
salamisandwich77 wants to merge 126 commits into
Open
PROD-250: feat(sandbox): add governed command integration#22salamisandwich77 wants to merge 126 commits into
salamisandwich77 wants to merge 126 commits into
Conversation
Bumps [black](https://github.com/psf/black) from 23.12.1 to 24.3.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](psf/black@23.12.1...24.3.0) --- updated-dependencies: - dependency-name: black dependency-version: 24.3.0 dependency-type: direct:development ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [protobuf](https://github.com/protocolbuffers/protobuf) from 6.33.2 to 6.33.5. - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Commits](https://github.com/protocolbuffers/protobuf/commits) --- updated-dependencies: - dependency-name: protobuf dependency-version: 6.33.5 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
chore(deps-dev): bump black from 23.12.1 to 24.3.0
chore(deps): bump protobuf from 6.33.2 to 6.33.5
Bumps [SonarSource/sonarqube-scan-action](https://github.com/sonarsource/sonarqube-scan-action) from 5.3.0 to 6.0.0. - [Release notes](https://github.com/sonarsource/sonarqube-scan-action/releases) - [Commits](SonarSource/sonarqube-scan-action@v5.3.0...v6.0.0) --- updated-dependencies: - dependency-name: SonarSource/sonarqube-scan-action dependency-version: 6.0.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com>
…ithub/workflows/SonarSource/sonarqube-scan-action-6.0.0 chore(deps): bump SonarSource/sonarqube-scan-action from 5.3.0 to 6.0.0 in /.github/workflows
Feat/hook level governance
Bumps [pygments](https://github.com/pygments/pygments) from 2.19.2 to 2.20.0. - [Release notes](https://github.com/pygments/pygments/releases) - [Changelog](https://github.com/pygments/pygments/blob/master/CHANGES) - [Commits](pygments/pygments@2.19.2...2.20.0) --- updated-dependencies: - dependency-name: pygments dependency-version: 2.20.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
- constructor: state + span_processor wiring restored (main's completed-hook machinery), the governed-command branch preserved from the PR branch - _run_activity signature carries activity_input/session_id; the session id is read from the multi-agent header before the run - _handle_completion resolves the completed stop from the shared state and enforces HALT/patch before the completed event - _serialize_value handles pydantic-style model_dump before the fallback - tests: constructor calls + state attribute aligned with the merged API
…factory The moat shape: OpenBox integrates with Temporal through exactly one official surface — the native Worker plugins=[OpenBoxPlugin(...)] slot. User code never imports or calls any other OpenBox symbol. - remove openbox/worker.py (the create_openbox_worker factory and its internal runtime/telemetry plugins) and tests/test_worker.py - strip the factory export from openbox/__init__.py; OpenBoxPlugin is the sole public entry point - GovernedCommandDeployment builds OpenBoxPlugin(...) and returns the native Worker(..., plugins=[openbox_plugin]) - clean every SDK-internal reference to the factory (docstrings, the deployment seam, the public-API pin tests) Also lands the merge reconciliation accumulated on top: config/client/ activities/interceptor fixes aligned with the merged base SDK, plus the interceptor test expectations (state, session_id, completed_stop, pydantic serialization).
- _check_pending_verdicts reads the run-scoped signal verdict from the shared TemporalGovernanceState (stale-run safe), not the span buffer - REQUIRE_APPROVAL marks the state's pending-approval marker; retry polling checks and clears it through the state - tests: direct _ActivityInterceptor constructions pass the span_processor (the merged constructor requires it)
The moat shape allows exactly one public OpenBox surface: the OpenBoxPlugin initializer. GovernedCommandDeployment, its error type, and the manifest loader leave the public namespaces (openbox and openbox.sandbox lazy exports). They remain importable at their module path as internal machinery only.
…erged base SDK - _TemporalEvaluationClient delegates to the wrapped EvaluationClient (self._base) instead of super() — the direct GovernanceClient constructor path crashed on every evaluate/poll with a real base client - _run_activity binds the shared core ActivityContext (core_activity_scope) around user code; ActivityStarted/ActivityCompleted payloads carry the multi_agent_session_id from the header again - plugin creates one TemporalGovernanceState before the runtime and passes it to create_temporal_runtime and BOTH interceptors (signal-verdict / HITL-pending / completed-stop bridges) - create_temporal_runtime wires the shared state into the rich TemporalFrameworkAdapter (completed-hook stops + approval markers), using the shared core ContextStore - tests: httpx transport mocks support the async context-manager seam; plugin tests pin the merged create_temporal_runtime / run_worker / setup_opentelemetry_for_governance contract
…rence The moat shape: OpenBox appears only as OpenBoxPlugin in the native Worker plugins slot. The factory sections, parameters, and examples are replaced with the plugin shape.
create_sandbox_worker (openbox/sandbox/worker.py), OpenBoxSandboxPlugin (openbox/sandbox/plugin.py), GovernedCommandInterceptor (openbox/sandbox/interceptor.py), and the resolver module were defined but never imported, never tested, and never called. The plugin is the only surface; dead alternate shapes only confuse. Removed.
The plugin intercepts the user's own activity. ALLOW runs it on the host; CONSTRAIN routes it into the sandbox. The workflow stays clean. The pre-authorized and receipt compatibility modes are removed from the docs — they are stale manual paths.
Drop the disconnected command-only worker mode, the receipt-bearing request path, and the pre-authorized trust model: - TemporalSandboxConfig loses receipt_verifier and trust_application_agent (and their validation); SandboxConfig (create_openbox_worker-era dead config carrying the same trust fields) is deleted. - The activity interceptor always builds the connected governance client; the disconnected early-return branch is gone. _execute_governed_command no longer verifies receipts or dispatches via trusted/authorized paths. - OpenBoxPlugin always builds the connected composition; the command-only worker branch is deleted. - openbox/sandbox/receipts.py is deleted; GovernedCommandReceipt and the receipt field on GovernedCommandRequest are removed from the wire types. - README/CHANGELOG drop the compatibility-mode language.
…ntly The plugin's sandbox configuration now makes a CONSTRAIN verdict on any user activity route that activity into the sandbox: - execute_activity intercepts a CONSTRAIN ActivityStarted verdict before verdict enforcement when a sandbox is configured, and returns the sandbox result to the caller without running the activity on the host. - The routed path derives argv from the activity's single structured input (profile_id + named arguments) through the sandbox profile bundle, builds the GovernedCommand from genuine activity.info() identity, maps the verdict onto the dispatcher decision shape, and executes dispatch_with_decision(command, decision) with the shared heartbeat/cancellation/result-mapping machinery. - A CONSTRAIN verdict without a sandbox config keeps the existing unsupported error; a completed CONSTRAIN verdict after execution is a no-op when a sandbox is configured. - The registered governed-command Activity, its workflow helper, the activity-type constant, and the plugin registration are deleted — the interception path replaces them entirely. - docs/governed-commands.md is rewritten for the transparent routing design; README/CHANGELOG reference the new behavior. - Four routing tests cover sandbox dispatch, the no-sandbox error contract, host execution on ALLOW, and the completed-verdict no-op.
The SandboxConfig(registry=...) class is the plugin's one-stop sandbox configuration. The plugin builds the dispatcher, profile bundles, heartbeat sink, output limits, policy, and asset bundle internally from it. The previous cleanup classified it as dead and deleted it; the POC re-added it in the vendored copy, so the standalone SDK diverged.
- the TemporalSandboxConfig NAME_ERROR in plugin.py (TYPE_CHECKING-only import) - the absolute-path requirement in the sandbox config - the _deadlines() defaults exceeding the core SDK caps
The ExecutionMetadata carries sandbox_id but the completion event output was not including it. The FE and Core governance span attributes need the sandbox identifier for traceability.
The SDK lazily imports every database driver (governance hooks only activate when the app uses that database), but the install pulled psycopg2, asyncpg, mysql-connector, pymysql, pymongo, redis, and sqlalchemy plus their OTel instrumentation unconditionally — native builds made installation slow for apps that never touch a database. Base install now covers Temporal + HTTP instrumentation only. DB governance users install openbox-temporal-sdk-python[db].
The natural sandbox path where the wrapper owns the completion event previously dropped the typed result. It now carries schema_name and values alongside the execution metadata.
docs/governed-commands.md imports TemporalCommandProfileBundle from openbox.sandbox, and that import fails: the package builds its public surface from a curated map, and the bundle is not in it. The bundle is the declared type of TemporalSandboxConfig.profiles and a caller has to construct one, so it belongs in the public surface. TemporalHeartbeatSink is in the same position for the heartbeat_sink field. Both are exported now, which makes the documented import work. Also closes the Worker example in the README: plugins=[OpenBoxPlugin( was never closed, and the Worker options that follow sit inside the plugin call, so the snippet could not run. Three blocks showing a class hierarchy and elided signatures are marked text rather than python. 701 tests pass.
The interceptor records "sandbox_id": execution.sandbox_id when it emits a completion event, and the fake execution in this test did not carry that attribute, so the branch failed with AttributeError on SimpleNamespace while 684 other tests passed. The interceptor line is identical on the behavioral CONSTRAIN branch, which already carries this fixture value; only the fixture was missing here.
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.
Plugin-only OpenBox integration for Temporal. CONSTRAIN verdicts route the user's activity into the sandbox transparently.