Skip to content

Extract runtime facade compatibility layer from PR 188 - #212

Closed
mindfn wants to merge 7 commits into
mainfrom
codex/pr188-runtime-facades
Closed

Extract runtime facade compatibility layer from PR 188#212
mindfn wants to merge 7 commits into
mainfrom
codex/pr188-runtime-facades

Conversation

@mindfn

@mindfn mindfn commented Mar 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • split the non-governance portion of PR Close governance traceability tasks and finish T0-4/T0-5 hardening #188 into a stacked follow-up branch
  • move public facade imports for embedding, event, hook, plan, security, and transport onto public defaults / adapters / impl modules instead of direct ._internal imports
  • restore the historical permissive DefaultSecurityBoundary export and add checkpoint facade compatibility helpers
  • add regression coverage for package initializer facade hygiene, security boundary compatibility, and checkpoint STM/message round-trips

Verification

  • .venv/bin/pytest -q
  • .venv/bin/pytest -q tests/unit/test_package_initializers_facade_pattern.py tests/unit/test_checkpoint_defaults.py tests/unit/test_security_boundary.py
  • .venv/bin/pytest -q tests/unit/test_transport_typed_payloads.py tests/unit/test_transport_channel.py tests/unit/test_react_agent_gateway_injection.py

Notes

Split the non-governance portion of PR 188 into a standalone change that keeps public package facades off direct _internal imports. Add explicit defaults/adapters modules for embedding, event, hook, plan, and transport, restore the historical permissive DefaultSecurityBoundary export, and document the min-surface facade rule.

Also add checkpoint facade compatibility helpers plus regression tests for checkpoint message round-trips, security boundary behavior, and package initializer import hygiene, while aligning the transport facade exports with the current main-branch transport API.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3bb47069ab

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +117 to +121
Message(
role=item.get("role", "user"),
text=item.get("text") or item.get("content", ""),
name=item.get("name"),
metadata=dict(item.get("metadata") or {}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve message kind/data in STM checkpoint restore

The new StmContributor restore path reconstructs each message with only role, text, name, and metadata, so any checkpoint containing non-chat STM entries (for example tool_call/tool_result messages with structured data or attachments) is silently downgraded to default chat messages and loses payload fields on restore. This corrupts checkpoint round-trips for realistic agent sessions that rely on typed message state after resume.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handled in 22cb1da.

StmContributor now serializes/restores kind, attachments, data, mark, and id in addition to the legacy text/name/metadata fields, so typed STM entries survive checkpoint round-trips instead of being downgraded to default chat messages. Added a regression in tests/unit/test_checkpoint_defaults.py that round-trips attachments plus a TOOL_CALL message with structured payload.

Comment on lines +265 to +266
# Preserve historical facade behavior where `DefaultSecurityBoundary` is permissive.
DefaultSecurityBoundary = LegacyDefaultSecurityBoundary

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep config-based constructor on impl default boundary

Rebinding dare_framework.security.impl.DefaultSecurityBoundary to the legacy permissive class removes the from_config(...) classmethod that existed when this symbol aliased PolicySecurityBoundary, so callers that instantiate via DefaultSecurityBoundary.from_config(...) now fail with AttributeError. Because this commit positions impl as the public export layer for facade imports, this is a backward-incompatible API regression for existing consumers of that module path.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handled in 22cb1da.

dare_framework.security.impl.DefaultSecurityBoundary is now a permissive compatibility class with a from_config(...) classmethod that delegates to PolicySecurityBoundary.from_config(...), preserving the old constructor contract for callers that instantiate through the impl module path. Added coverage in tests/unit/test_security_boundary.py to assert from_config(...) still returns a policy-configured boundary.

mindfn added 2 commits March 10, 2026 14:10
Preserve structured tool-call payloads during deduplication by incorporating message kind, name, attachments, and data into the compression identity key, so auto-compression no longer drops distinct assistant tool-call history with empty text.

Also re-append the transient SmartContext reflection prompt after re-assembling messages for auto-compression, and add regression coverage for both review findings.
Preserve typed STM checkpoint round-trips by serializing message kind, attachments, data, mark, and id in the compatibility contributor instead of downgrading everything to plain chat messages on restore.

Also restore the config-driven constructor contract on dare_framework.security.impl.DefaultSecurityBoundary so callers using from_config(...) still receive a policy-configured boundary, and add regression coverage for both compatibility paths.
@mindfn

mindfn commented Mar 10, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the Codex review findings in 22cb1da.

Handled:

Verification:

  • .venv/bin/pytest -q tests/unit/test_checkpoint_defaults.py tests/unit/test_security_boundary.py
  • .venv/bin/pytest -q

@mindfn

mindfn commented Mar 10, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 22cb1da13e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

value.filename,
_freeze_value(getattr(value, "metadata", {})),
)
return value

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Normalize unhashable payload values before dedup

In the dedup_then_truncate path, _dedup_messages stores keys containing _freeze_value(msg.data) in a set, but _freeze_value falls through to return value for unsupported types. If a message payload contains an unhashable value (for example a set or an unhashable dataclass instance), compression raises TypeError during set membership and aborts the run. This is reachable in production when auto-compression is enabled with the default dedup_then_truncate strategy and tool outputs include non-JSON-native Python values.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handled in f32e736.

_freeze_value() now normalizes sets/frozensets, dataclass instances, and other unhashable objects before they participate in the dedup identity key, so dedup_then_truncate no longer raises TypeError on non-JSON-native payload values. Added a regression in tests/unit/test_context_compression.py that deduplicates messages containing set payloads.

Comment thread dare_framework/checkpoint/defaults.py Outdated
],
"data": _clone_payload(m.data),
"name": m.name,
"metadata": dict(getattr(m, "metadata", {}) or {}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Deep-copy checkpoint metadata to keep snapshots stable

StmContributor.serialize deep-copies data and attachment metadata but only shallow-copies message metadata via dict(...). Nested metadata objects therefore stay aliased to live context objects, so mutating metadata after save() can silently mutate stored checkpoint contents and break point-in-time restore semantics. Checkpoint payload construction should deep-copy metadata the same way as other nested fields.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handled in f32e736.

StmContributor.serialize() now deep-copies message metadata the same way it already deep-copies attachment metadata and data, so saved checkpoint payloads are no longer aliased to live nested metadata objects. Added a regression in tests/unit/test_checkpoint_defaults.py that mutates the source message metadata after save() and verifies the restored checkpoint still contains the original nested value.

mindfn added 2 commits March 10, 2026 14:41
Make the default in-memory STM treat max_messages=0 as full eviction instead of relying on Python's -0 slice behavior, which previously retained the entire history.

Add a Context-level regression test so the canonical basic compression path guarantees explicit zero-message requests clear the default STM.
Normalize non-JSON-native payload values such as sets and unhashable dataclass-like objects before deduplication so auto-compression cannot raise TypeError while building message identity keys.

Also deep-copy nested checkpoint metadata during STM serialization to preserve point-in-time snapshot semantics, and add regressions for both behaviors.
@mindfn

mindfn commented Mar 10, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f32e736e55

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread dare_framework/checkpoint/defaults.py Outdated
def get(self, checkpoint_id: str) -> dict[str, Any] | None:
if checkpoint_id not in self._store:
return None
return dict(self._store[checkpoint_id])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Deep-copy payloads returned from checkpoint store

MemoryCheckpointStore.get() returns only a shallow dict(...) copy, so nested structures remain shared with _store. In practice this corrupts checkpoints: after restore(), mutating nested message metadata in the restored context can mutate the stored snapshot, and a second restore returns the mutated state instead of the original checkpoint. Checkpoint snapshots should be immutable once saved, so this path needs a deep clone on read (and ideally on write as well).

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handled in 5362ee5.

MemoryCheckpointStore now deep-copies payloads on both put() and get(), so nested checkpoint state cannot be mutated through restored objects or repeated reads. Added a regression in tests/unit/test_checkpoint_defaults.py that restores the same checkpoint twice and verifies mutating the first restored context does not affect the second restore.

mindfn added 2 commits March 10, 2026 15:22
Make budget_remaining treat zero-valued limits as finite instead of falling back to infinity, so strict zero-token runs correctly degrade retrieval instead of fetching LTM/knowledge under an exhausted budget.

Also make compression dedup keys robust for unhashable payload values, and add regressions for both the zero-budget retrieval path and non-JSON-native payload deduplication.
Clone payloads on both write and read in MemoryCheckpointStore so nested checkpoint state cannot be mutated through restored objects or repeated reads.

Add a regression that restores the same checkpoint twice and verifies mutating the first restored context does not affect the second restore.
@mindfn

mindfn commented Mar 10, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5362ee5a8a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +173 to +174
if "current_milestone_idx" in payload and hasattr(state, "current_milestone_idx"):
state.current_milestone_idx = payload["current_milestone_idx"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore task/run identifiers from session-state payload

SessionStateContributor.serialize() persists task_id and run_id, but deserialize_and_apply() only reapplies current_milestone_idx, so checkpoint restore loses the saved session identity fields. In any resume flow that expects full session-state round-tripping (for example, restoring into a fresh context object), the restored state will keep whatever task_id/run_id happened to be on the target object, which can desynchronize traceability and subsequent orchestration logic from the checkpoint that was actually loaded.

Useful? React with 👍 / 👎.

Base automatically changed from codex/main-baseline-fixes to main March 10, 2026 08:22
@mindfn

mindfn commented Mar 10, 2026

Copy link
Copy Markdown
Collaborator Author

Superseded by #213.

#212 was stacked on an outdated pre-merge #211 base (codex/main-baseline-fixes) and still carried obsolete compression/context commits from that old stack.

The replacement PR has been re-cut directly on top of main and keeps only the intended runtime facade/checkpoint scope:

@mindfn mindfn closed this Mar 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant