Skip to content

Milestone 8: skills and MCP integration - #9

Merged
avitus merged 5 commits into
devfrom
codex-milestone-8
Aug 4, 2026
Merged

Milestone 8: skills and MCP integration#9
avitus merged 5 commits into
devfrom
codex-milestone-8

Conversation

@avitus

@avitus avitus commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Summary

  • add immutable content-addressed skill packages, session-pinned catalogs, and selective skill loading
  • add official-SDK MCP transports, discovery, authentication recovery, dynamic tools/prompts, and managed egress
  • route MCP calls through the centralized tool pipeline and add deterministic skill/MCP evaluation coverage
  • propose ADR-0044 for the implementation seams that require owner review

Verification

  • make check
  • 457 non-live tests passed against PostgreSQL 16 and Docker (2 credentialed provider smokes deselected)
  • 59 PostgreSQL integration and resilience tests passed
  • 140 cumulative hard gates active through Milestone 8
  • 25 initial and 15 follow-up local CodeRabbit findings remediated

Milestone 8 remains in progress until hosted CircleCI passes and all hosted CodeRabbit findings are addressed.

Summary by CodeRabbit

  • New Features

    • Added versioned, immutable skill packages with session-pinned catalogs, controlled loading, trust metadata, and archive storage.
    • Added MCP integrations for tool discovery, prompts, resources, authentication, stdio and HTTP connections, dynamic tools, and resilient disconnection recovery.
    • Added tenant-scoped configuration, auditing, egress controls, and transaction rollback protection.
    • Added comparative skill and MCP evaluation scenarios with deterministic outcomes.
  • Documentation

    • Documented Milestone 8 architecture decisions and implementation progress.
  • Tests

    • Activated comprehensive skill and MCP validation gates and integration coverage.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ae705e22-daa2-479a-ba6f-752ebb0897b6

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Milestone 8 adds immutable skill packages, session-pinned catalogs, MCP adapters and runtime handling, dynamic tools, persistence, deterministic evaluations, and activated validation gates.

Changes

Skills and MCP integration

Layer / File(s) Summary
Skill contracts, archives, catalogs, and persistence
src/agent_core/domain/skills.py, src/agent_core/skills/*, src/agent_core/adapters/skills/*, src/agent_core/adapters/persistence/*
Adds validated skill packages, deterministic archives, revision repositories, session catalogs, loading limits, and PostgreSQL persistence.
MCP contracts, adapters, and runtime
src/agent_core/domain/mcp.py, src/agent_core/adapters/mcp/*, src/agent_core/mcp/*
Adds MCP configuration models, scripted and SDK clients, discovery mapping, authentication, transport recovery, catalog persistence, prompts, and resource tools.
Session, context, tool pipeline, and composition wiring
src/agent_core/application/*, src/agent_core/context/*, src/agent_core/tools/*, src/agent_core/bootstrap.py
Wires skills and MCP into sessions, context plans, checkpoints, dynamic tool lookup, execution, activation, and teardown.
Evaluation fixtures and milestone gates
src/agent_core/evals/*, evals/*, tests/gates/*, tests/integration/*
Adds comparative evaluations, MCP and skill fixtures, disconnect handling, pipeline checks, persistence coverage, and concrete Milestone 8 gates.
Schema release and project records
migrations/versions/*, docs/*, pyproject.toml, scripts/architecture_checks.py
Adds database tables and row-level policies, runtime dependencies, SDK boundary checks, ADR-0044, and Milestone 8 status records.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • avitus/veetbot#2: Provides foundational runtime, registry, evaluation, bootstrap, persistence, and gate infrastructure.
  • avitus/veetbot#7: Introduces components extended here, including bootstrap, tool execution, persistence, and execution proxying.

Poem

A rabbit packed skills in a zstd-bound crate,
Then checked MCP paths through a guarded gate.
Catalog pins stayed firm, tools followed the plan,
Disconnects became outcomes the pipeline could scan.
“Milestone Eight,” said Bun, “the tests now run!”

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.19% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the pull request's primary change: Milestone 8 skills and MCP integration.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex-milestone-8

Comment @coderabbitai help to get the list of available commands.

@avitus

avitus commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@avitus

avitus commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai 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.

Actionable comments posted: 19

🧹 Nitpick comments (13)
tests/gates/test_tool_m8.py (1)

222-222: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Write the token_endpoint key as a plain literal. Both sites split the field name into "token_" + "endpoint". The concatenation produces the same string at runtime, so it changes nothing except readability, and it defeats any grep or secret scanner that the split was meant to avoid. If a lint rule flags the literal, suppress that rule explicitly at the line instead of hiding the string from it.

  • tests/gates/test_tool_m8.py#L222-L222: replace "token_" + "endpoint" with "token_endpoint" in the oauth_refresh config.
  • tests/gates/test_tool_m8.py#L384-L384: replace "token_" + "endpoint" with "token_endpoint" in the oauth config.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/gates/test_tool_m8.py` at line 222, Replace the split key expression
with the plain literal "token_endpoint" in both the oauth_refresh config at
tests/gates/test_tool_m8.py lines 222-222 and the oauth config at lines 384-384;
if lint rejects the literal, suppress that rule explicitly on each affected
line.
src/agent_core/skills/package.py (1)

195-212: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Move the MAX_FILES check so directory entries do not trigger it.

os.walk yields directory names in names. The loop appends a member only for a symlink or a regular file, but it evaluates the MAX_FILES guard for every candidate, including plain directories. A valid package that holds 64 files plus one subdirectory is therefore rejected with package.file_count, even though only 64 members are collected.

♻️ Proposed fix
         for name in [*names, *files]:
             candidate = directory_path / name
             relative = candidate.relative_to(root).as_posix()
-            if len(members) >= MAX_FILES:
-                raise _refuse("package.file_count", f"package must contain 1 to {MAX_FILES} files")
             if candidate.is_symlink():
+                if len(members) >= MAX_FILES:
+                    raise _refuse(
+                        "package.file_count", f"package must contain 1 to {MAX_FILES} files"
+                    )
                 members.append(SkillPackageMember(path=relative, kind="symlink"))
             elif candidate.is_file():
+                if len(members) >= MAX_FILES:
+                    raise _refuse(
+                        "package.file_count", f"package must contain 1 to {MAX_FILES} files"
+                    )
                 remaining = MAX_PACKAGE_BYTES - total_bytes
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agent_core/skills/package.py` around lines 195 - 212, Move the MAX_FILES
guard in the os.walk packaging loop so it runs only immediately before appending
a symlink or regular-file SkillPackageMember, not for plain directory entries.
Preserve the existing file-count error and directory traversal behavior.
src/agent_core/adapters/persistence/sqlalchemy_models.py (1)

511-537: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consider an index that supports latest-active-revision lookups.

PostgresSkillRepository.resolve and list_active filter skill_revisions by skill_id and status, then order by revision DESC. The only matching index is the unique (skill_id, revision) constraint, so PostgreSQL filters status after the index scan. An index on (skill_id, status, revision DESC) serves both queries directly.

__table_args__ = (
    UniqueConstraint("skill_id", "revision", name="uq_skill_revisions_skill_revision"),
    Index(
        "ix_skill_revisions_skill_status_revision",
        "skill_id",
        "status",
        text("revision DESC"),
    ),
)

Add the same index to migrations/versions/9a71c4e8d2f0_add_milestone_8_skills_and_mcp.py.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agent_core/adapters/persistence/sqlalchemy_models.py` around lines 511 -
537, Update SkillRevisionRow.__table_args__ to retain the existing uniqueness
constraint and add the ix_skill_revisions_skill_status_revision index on
skill_id, status, and descending revision. Add the corresponding index creation
to migration 9a71c4e8d2f0_add_milestone_8_skills_and_mcp.py so the database
schema matches the model.
src/agent_core/adapters/skills/stores.py (1)

78-96: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Consider flushing the staged archive before linking it.

NamedTemporaryFile closes the file at the end of the with block, so buffered bytes reach the kernel. The code does not call os.fsync on the file or on the parent directory. After a host crash, the store can hold a present-but-truncated archive at a content-addressed key. put then treats the key as immutable and rejects the correct bytes with immutable skill archive key already contains different bytes.

♻️ Proposed durability fix
         ) as staged:
             staged.write(archive)
+            staged.flush()
+            os.fsync(staged.fileno())
             temporary = Path(staged.name)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agent_core/adapters/skills/stores.py` around lines 78 - 96, Update the
staged archive flow in the put implementation around NamedTemporaryFile and
os.link to fsync the written temporary file before linking it, then fsync
target.parent after the link succeeds. Preserve the existing immutable-key
comparison and cleanup behavior while ensuring a reported successful put
survives host crashes without a truncated archive.
src/agent_core/domain/runs.py (1)

115-119: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Consider validating that pinned tool collections agree.

pinned_tool_names, pinned_tool_versions, and pinned_tool_specs carry the same tool identities in three places. No validator enforces agreement, so a partially populated checkpoint restores an inconsistent pin set. ContextPlan.tools_match_names already applies this pattern for plans.

♻️ Proposed consistency validator
`@model_validator`(mode="after")
def pinned_tools_match(self) -> RunCheckpoint:
    names = set(self.pinned_tool_names)
    if not set(self.pinned_tool_specs) <= names or not set(self.pinned_tool_versions) <= names:
        raise ValueError("checkpoint pinned tool metadata does not match its pinned tool names")
    return self
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agent_core/domain/runs.py` around lines 115 - 119, Add an after-model
validator to the RunCheckpoint model, alongside the pinned_tool_names,
pinned_tool_versions, and pinned_tool_specs fields, that verifies both metadata
mappings contain only names present in pinned_tool_names and raises a ValueError
when they do not. Preserve valid checkpoints and follow the existing
ContextPlan.tools_match_names validation pattern.
tests/gates/test_skill_m8.py (2)

272-281: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make the tool-registration gate recurse into subpackages.

glob("*.py") matches only files directly inside src/agent_core/skills. A future subpackage such as skills/loaders/registry.py would register tools without failing this gate. Use rglob so the gate covers the whole package.

♻️ Proposed change
-    for path in (ROOT / "src" / "agent_core" / "skills").glob("*.py"):
+    for path in (ROOT / "src" / "agent_core" / "skills").rglob("*.py"):
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/gates/test_skill_m8.py` around lines 272 - 281, Update
test_no_tool_from_skill to use recursive file discovery with rglob instead of
glob, ensuring Python files in all skills subpackages are checked for register
and register_dynamic calls while preserving the existing AST validation.

538-539: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive the token budget from configuration instead of the literal 6_000.

The assertion hard-codes the skill-body token budget. If the configured budget changes, this gate keeps passing against a stale number and stops proving the cap. Read the value from the same plan budget or configuration constant that SkillCatalogService enforces.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/gates/test_skill_m8.py` around lines 538 - 539, Update the token-sum
assertion in the test around SkillCatalogService to derive its limit from the
same plan budget or configuration constant enforced by SkillCatalogService,
replacing the hard-coded 6_000 while preserving the existing loaded-count
assertion.
src/agent_core/adapters/persistence/unit_of_work.py (1)

215-219: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Log rollback-callback failures instead of discarding them.

suppress(Exception) hides every compensation error. The PostgreSQL factory registers skill-archive cleanup through this registrar, so a failed callback leaves an orphaned archive object in the package store with no diagnostic record. Keep the suppression so one failure does not block the remaining callbacks, but emit a log entry.

♻️ Proposed change
     async def _run_rollback_callbacks(self) -> None:
         callbacks, self._rollback_callbacks = self._rollback_callbacks, []
         for callback in reversed(callbacks):
-            with suppress(Exception):
-                await callback()
+            try:
+                await callback()
+            except Exception:
+                logger.exception(
+                    "transaction_rollback_callback_failed",
+                    extra={"tenant_id": self._tenant_id},
+                )

This requires a module logger if one is not already defined in this file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agent_core/adapters/persistence/unit_of_work.py` around lines 215 - 219,
Update _run_rollback_callbacks to retain suppression so all callbacks continue
executing, but catch each callback exception and log it through a module-level
logger with rollback-callback context and exception details. Add the module
logger if this file does not already define one.
tests/contract/test_skill_catalog_contract.py (1)

27-35: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Exercise a non-empty catalog so the stability contract is actually asserted.

Both open calls return an empty catalog, so first == second holds for any implementation, including one with no pinning at all. Install at least one package before the first open, then install or archive a package between the two calls. The assertion then proves that the second open returns the pinned catalog rather than the current repository state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/contract/test_skill_catalog_contract.py` around lines 27 - 35, Update
the test around SkillCatalogService.open to seed at least one package before the
first open, then install or archive another package between the two open calls.
Keep the equality assertion and ensure the first catalog is non-empty so the
second result verifies the pinned catalog rather than an empty repository state.
src/agent_core/application/session_service.py (1)

45-58: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the activation obligation for create_in.

create calls activate after the transaction commits. create_in does not activate, so any caller that composes create_in into its own submission transaction must call activate after that transaction commits. State this in the docstring so the MCP session-activation step is not skipped.

The catalog is also opened here before uow.sessions.create(session) on line 69. See the consolidated comment about opening a catalog for an uncommitted session.

♻️ Proposed change
     async def create_in(self, uow: RepositoryUnitOfWork) -> UUID:
-        """Create a session inside a caller-owned submission transaction."""
+        """Create a session inside a caller-owned submission transaction.
+
+        The caller must call `activate` after that transaction commits.
+        """
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agent_core/application/session_service.py` around lines 45 - 58, Update
the create_in method docstring to state that callers composing it into their own
submission transaction must call activate after the transaction commits,
preserving the existing create behavior and explicitly covering the MCP
session-activation step. Do not change the catalog-opening logic here; address
only the documentation requested for create_in.
src/agent_core/domain/errors.py (1)

143-148: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Populate the ConflictError reason and details for SkillRevisionConflict.

ConflictError carries a structured reason and details payload. SkillRevisionConflict passes only a message, so consumers that read reason receive None and lose the revision value that is already available.

♻️ Proposed change
     def __init__(self, current_revision: int) -> None:
-        super().__init__("skill revision changed during installation")
+        super().__init__(
+            "skill revision changed during installation",
+            reason="skill_revision_conflict",
+            details={"current_revision": current_revision},
+        )
         self.current_revision = current_revision
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agent_core/domain/errors.py` around lines 143 - 148, Update
SkillRevisionConflict.__init__ to pass a structured ConflictError reason and
details payload when calling the superclass, using current_revision in the
details so consumers retain the winning revision; preserve the current_revision
attribute and conflict message behavior.
src/agent_core/bootstrap.py (1)

636-636: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Compare the transport enum, not its string value.

Line 636 tests config.transport.value == "http". Every other site compares the enum member, for example config.transport is MCPTransport.HTTP in src/agent_core/adapters/mcp/sdk.py line 375 and src/agent_core/mcp/configuration.py line 25. A string comparison silently stops matching if the enum value changes, and the proxy is then not started for HTTP servers.

♻️ Proposed refactor
-                if any(config.transport.value == "http" for config in effective_mcp_configs):
+                if any(
+                    config.transport is MCPTransport.HTTP for config in effective_mcp_configs
+                ):

Add MCPTransport to the import on line 160:

-from agent_core.domain.mcp import MCPServerConfig, ScriptedMCPServer
+from agent_core.domain.mcp import MCPServerConfig, MCPTransport, ScriptedMCPServer
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agent_core/bootstrap.py` at line 636, Update the transport check in the
effective MCP configuration logic to compare config.transport directly with the
MCPTransport.HTTP enum member instead of comparing its value to a string. Add
MCPTransport to the existing imports and preserve the surrounding any(...)
condition.
src/agent_core/mcp/mapping.py (1)

79-81: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer index keying over id() keying for the schema inspections.

schema_inspections maps id(tool) to the inspection result. This works only because remote_tools keeps every model alive for the whole function. If a later change accepts an iterable, or rebuilds the models, id() values can be reused and the lookup can return another tool's result. Key by position instead.

♻️ Proposed refactor
-    schema_inspections = {
-        id(tool): _inspect_schema(tool.input_schema, schema_maximum_depth) for tool in remote_tools
-    }
+    schema_inspections = {
+        index: _inspect_schema(tool.input_schema, schema_maximum_depth)
+        for index, tool in enumerate(remote_tools)
+    }
+    inspection_by_tool = {
+        index: schema_inspections[index] for index in range(len(remote_tools))
+    }

Then iterate with enumerate in both the declarations comprehension and the acceptance loop so each tool uses its own index.

Also applies to: 107-107

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agent_core/mcp/mapping.py` around lines 79 - 81, Replace id(tool)-based
keys in schema_inspections with positional indices, and use enumerate
consistently in the declarations comprehension and acceptance loop so each
remote tool retrieves its inspection by index.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@evals/gates/tool.yaml`:
- Around line 82-87: Align gate.tool.mcp_reauth_bounded with its actual coverage
by either extending test_mcp_reauth_bounded to submit and verify a run continues
through the re-authentication scenarios, or removing the untested “run that
continues throughout” requirement from the gate statement. Keep the existing
ladder invariants unchanged.

In `@src/agent_core/adapters/mcp/memory.py`:
- Around line 28-49: Ensure deterministic catalog-key conflict handling across
src/agent_core/adapters/mcp/memory.py:28-49 and
src/agent_core/adapters/mcp/persistence.py:93-123. In record_catalog, detect
duplicate keys within the input batch and reject conflicting records before
writing; preserve idempotency for equivalent duplicates. In the persistence
adapter, compare existing rows against the immutable payload and reject
mismatches instead of silently retaining the stored record. Add
repository-contract cases covering equivalent retries and both intra-batch and
existing-record conflicts.

In `@src/agent_core/adapters/mcp/sdk.py`:
- Around line 143-192: Require HTTPS for both credential-bearing MCP endpoints:
validate config.endpoint before authenticated MCP requests and
config.token_endpoint before the client-credentials request in
_exchange_client_token, rejecting non-HTTPS values before sending credentials.
Apply the corresponding endpoint validation in src/agent_core/bootstrap.py lines
494-502 as well.

In `@src/agent_core/adapters/models/fake.py`:
- Around line 169-178: Update _next_turn so the repeat_last fallback selects the
latest scripted turn whose context_contains is absent or present in
rendered_request, rather than unconditionally returning self._script.turns[-1].
Preserve the existing sequential scan behavior, and raise
ModelScriptExhaustedError when no matching or unconditional turn exists.

In `@src/agent_core/adapters/persistence/skills.py`:
- Around line 148-165: Update list_active to select only the newest active
revision per skill in SQL using DISTINCT ON with ORDER BY skill identity and
revision descending, then apply limit directly to the statement before
execution. Remove the Python latest dictionary and slicing while preserving the
existing domain conversion and tenant/status filters.

In `@src/agent_core/adapters/persistence/sqlalchemy_models.py`:
- Around line 533-535: The agent-authored revision provenance fields must remain
consistent when an authoring run is deleted. In
src/agent_core/adapters/persistence/sqlalchemy_models.py:533-535, update
SkillRevisionRow.authored_by_run_id to use ondelete="RESTRICT"; keep
src/agent_core/adapters/persistence/skills.py:205-207 and _to_domain strict with
no relaxation to provenance_matches_source. Add a regression test that deletes
an authoring run and then reads the skill revision, verifying the provenance
remains valid or deletion is rejected.

In `@src/agent_core/application/session_service.py`:
- Around line 45-58: The session creation flows in
src/agent_core/application/session_service.py lines 45-58 and
src/agent_core/application/public_services.py lines 235-240 must not publish
catalog/MCP state before the database transaction commits. Update create_in and
its sibling creation path to register catalog opening/publication with the
transaction lifecycle, committing on success and cleaning up MCP connections,
deferred events, preparation state, and catalog cache on rollback; preserve the
caller-owned transaction behavior.

In `@src/agent_core/context/builder.py`:
- Around line 245-248: Update _assemble so loaded-skill count and token cap
violations set a skill_bodies_over_cap pressure flag instead of raising
ContextOverflow, allowing measure() and assemble() to return ContextPressure
with fits=False for compaction. Include skill_bodies_over_cap in the fits and
reason calculation alongside working_state_over_cap, and replace the literal
two-skill limit with a plan budget field or named constant.

In `@src/agent_core/domain/mcp.py`:
- Around line 76-77: Update the OAuth validation around
MCPAuthScheme.OAUTH2_CLIENT and token_endpoint to require an absolute HTTPS URL,
rejecting missing, relative, or non-HTTPS endpoints before credentials can be
sent. Configure SDKMCPClient._exchange_client_token() to use
follow_redirects=False, and add coverage verifying redirects are not followed
and do not receive client credentials.
- Around line 57-58: Update the endpoint validation in the MCP configuration
path around the existing self.endpoint check to reject HTTP routes when the
authentication type is BEARER, HEADER, or OAUTH2_CLIENT, and require HTTPS for
token_endpoint values before storing configuration or constructing the client.
Also disable redirects or revalidate each redirect destination before forwarding
credentials, while preserving valid HTTPS behavior.

In `@src/agent_core/mcp/runtime.py`:
- Around line 604-615: Update close_session to isolate each connection’s
__aexit__ failure using the same handling pattern already used by prepare,
allowing all connections in that session to be attempted. Update close to
similarly isolate each close_session failure so teardown continues for every
remaining session.
- Line 124: Update ToolRegistry session lifecycle tracking to record which
dynamic MCP keys each session owns; in close_session, after closing clients,
remove keys whose final owning session has ended from both _registered and
StaticToolRegistry._dynamic_tools, while preserving entries still used by other
sessions.

In `@src/agent_core/runtime/executor.py`:
- Around line 355-361: Update _execute_running so resumed runs preserve the
restored pinned tool names, versions, and specs instead of overwriting them from
a newly planned epoch. Compare the restored pins with context_plan and reject
epoch mismatches, or continue using the original plan and pins; add a regression
test covering a pending tool call followed by context rotation.

In `@src/agent_core/skills/catalog.py`:
- Around line 126-146: Update the refs construction before the
uow.skills.resolve loop to deduplicate references by skill name while preserving
their first-seen order, then truncate the unique references to maximum_entries
before resolving. Record the names of references removed by deduplication or
truncation in dropped_names so callers retain existing dropped-skill visibility,
and leave the NotFoundError handling and resolution flow intact.

In `@src/agent_core/skills/package.py`:
- Around line 109-111: Before decompressing package data, use
zstandard.frame_content_size() to read and reject declared frame sizes above
MAX_PACKAGE_BYTES * 4, including handling unknown-size frames appropriately,
before any allocation or decompression. Update the package decompression logic
while preserving existing size validation, and pin zstandard to the exact
version required to keep content_sha256 stable across environments, updating the
lockfile accordingly.

In `@tests/gates/test_harness_m8.py`:
- Around line 43-59: Extend the I/O seal setup around the existing socket and
subprocess monkeypatches to block AnyIO’s subprocess path used by
mcp.client.stdio, preferably by patching anyio.open_process or the event loop’s
subprocess_exec and subprocess_shell methods with the existing deterministic-I/O
failure handler. Also monkeypatch socket.getaddrinfo to reject DNS resolution,
while preserving the current blocked socket, Popen, and open_connection
protections.

In `@tests/gates/test_tool_m8.py`:
- Line 493: Suppress Ruff S105 only for the assertion comparing
echoed["MCP_TOKEN"] to the test fixture value, using the project’s targeted
inline-noqa convention; leave the assertion logic and surrounding checks
unchanged.
- Around line 321-323: Strengthen test_mcp_sdk_confined by exercising the MCP
SDK architecture rule directly instead of relying only on filtering
architecture_errors output by the “MCP SDK” message. Extract and invoke a
dedicated checker or add a negative fixture that imports the restricted SDK and
assert that it produces the expected finding, while preserving the existing
passing behavior for compliant code.

In `@tests/integration/test_skill_mcp_persistence_m8.py`:
- Line 135: Update the rollback test around the existing archive path assertion
to first verify that the rollback archive exists under the skill-packages
directory while the transaction is active, then verify that the same path no
longer exists after rollback; preserve the existing rollback flow and use the
established rollback_key path.

---

Nitpick comments:
In `@src/agent_core/adapters/persistence/sqlalchemy_models.py`:
- Around line 511-537: Update SkillRevisionRow.__table_args__ to retain the
existing uniqueness constraint and add the
ix_skill_revisions_skill_status_revision index on skill_id, status, and
descending revision. Add the corresponding index creation to migration
9a71c4e8d2f0_add_milestone_8_skills_and_mcp.py so the database schema matches
the model.

In `@src/agent_core/adapters/persistence/unit_of_work.py`:
- Around line 215-219: Update _run_rollback_callbacks to retain suppression so
all callbacks continue executing, but catch each callback exception and log it
through a module-level logger with rollback-callback context and exception
details. Add the module logger if this file does not already define one.

In `@src/agent_core/adapters/skills/stores.py`:
- Around line 78-96: Update the staged archive flow in the put implementation
around NamedTemporaryFile and os.link to fsync the written temporary file before
linking it, then fsync target.parent after the link succeeds. Preserve the
existing immutable-key comparison and cleanup behavior while ensuring a reported
successful put survives host crashes without a truncated archive.

In `@src/agent_core/application/session_service.py`:
- Around line 45-58: Update the create_in method docstring to state that callers
composing it into their own submission transaction must call activate after the
transaction commits, preserving the existing create behavior and explicitly
covering the MCP session-activation step. Do not change the catalog-opening
logic here; address only the documentation requested for create_in.

In `@src/agent_core/bootstrap.py`:
- Line 636: Update the transport check in the effective MCP configuration logic
to compare config.transport directly with the MCPTransport.HTTP enum member
instead of comparing its value to a string. Add MCPTransport to the existing
imports and preserve the surrounding any(...) condition.

In `@src/agent_core/domain/errors.py`:
- Around line 143-148: Update SkillRevisionConflict.__init__ to pass a
structured ConflictError reason and details payload when calling the superclass,
using current_revision in the details so consumers retain the winning revision;
preserve the current_revision attribute and conflict message behavior.

In `@src/agent_core/domain/runs.py`:
- Around line 115-119: Add an after-model validator to the RunCheckpoint model,
alongside the pinned_tool_names, pinned_tool_versions, and pinned_tool_specs
fields, that verifies both metadata mappings contain only names present in
pinned_tool_names and raises a ValueError when they do not. Preserve valid
checkpoints and follow the existing ContextPlan.tools_match_names validation
pattern.

In `@src/agent_core/mcp/mapping.py`:
- Around line 79-81: Replace id(tool)-based keys in schema_inspections with
positional indices, and use enumerate consistently in the declarations
comprehension and acceptance loop so each remote tool retrieves its inspection
by index.

In `@src/agent_core/skills/package.py`:
- Around line 195-212: Move the MAX_FILES guard in the os.walk packaging loop so
it runs only immediately before appending a symlink or regular-file
SkillPackageMember, not for plain directory entries. Preserve the existing
file-count error and directory traversal behavior.

In `@tests/contract/test_skill_catalog_contract.py`:
- Around line 27-35: Update the test around SkillCatalogService.open to seed at
least one package before the first open, then install or archive another package
between the two open calls. Keep the equality assertion and ensure the first
catalog is non-empty so the second result verifies the pinned catalog rather
than an empty repository state.

In `@tests/gates/test_skill_m8.py`:
- Around line 272-281: Update test_no_tool_from_skill to use recursive file
discovery with rglob instead of glob, ensuring Python files in all skills
subpackages are checked for register and register_dynamic calls while preserving
the existing AST validation.
- Around line 538-539: Update the token-sum assertion in the test around
SkillCatalogService to derive its limit from the same plan budget or
configuration constant enforced by SkillCatalogService, replacing the hard-coded
6_000 while preserving the existing loaded-count assertion.

In `@tests/gates/test_tool_m8.py`:
- Line 222: Replace the split key expression with the plain literal
"token_endpoint" in both the oauth_refresh config at tests/gates/test_tool_m8.py
lines 222-222 and the oauth config at lines 384-384; if lint rejects the
literal, suppress that rule explicitly on each affected line.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 79e23f6b-fbcc-40be-b1f1-4c9adcbe3af2

📥 Commits

Reviewing files that changed from the base of the PR and between 5236348 and 07d78cc.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (85)
  • docs/adr/0044-milestone-8-skills-and-mcp-seams.md
  • docs/adr/index.md
  • docs/changelog.md
  • docs/plan/current-milestone.md
  • docs/status/project-state.yaml
  • evals/fixtures/mcp/docs_disconnect.yaml
  • evals/fixtures/mcp/docs_server.yaml
  • evals/fixtures/models/mcp_disconnect_then_continue.yaml
  • evals/fixtures/models/mcp_round_trip.yaml
  • evals/fixtures/models/skill_changes_outcome.yaml
  • evals/fixtures/skills/rel/SKILL.md
  • evals/gates/harness.yaml
  • evals/gates/skill.yaml
  • evals/gates/tool.yaml
  • migrations/versions/9a71c4e8d2f0_add_milestone_8_skills_and_mcp.py
  • mkdocs.yml
  • pyproject.toml
  • scripts/architecture_checks.py
  • src/agent_core/adapters/credentials.py
  • src/agent_core/adapters/mcp/__init__.py
  • src/agent_core/adapters/mcp/memory.py
  • src/agent_core/adapters/mcp/persistence.py
  • src/agent_core/adapters/mcp/scripted.py
  • src/agent_core/adapters/mcp/sdk.py
  • src/agent_core/adapters/models/fake.py
  • src/agent_core/adapters/persistence/revision.py
  • src/agent_core/adapters/persistence/skills.py
  • src/agent_core/adapters/persistence/sqlalchemy_models.py
  • src/agent_core/adapters/persistence/unit_of_work.py
  • src/agent_core/adapters/skills/__init__.py
  • src/agent_core/adapters/skills/memory.py
  • src/agent_core/adapters/skills/stores.py
  • src/agent_core/application/public_services.py
  • src/agent_core/application/run_service.py
  • src/agent_core/application/session_service.py
  • src/agent_core/bootstrap.py
  • src/agent_core/context/builder.py
  • src/agent_core/context/planner.py
  • src/agent_core/context/rendering.py
  • src/agent_core/domain/context.py
  • src/agent_core/domain/errors.py
  • src/agent_core/domain/mcp.py
  • src/agent_core/domain/messages.py
  • src/agent_core/domain/runs.py
  • src/agent_core/domain/skills.py
  • src/agent_core/domain/tools.py
  • src/agent_core/evals/cases.py
  • src/agent_core/evals/fixtures.py
  • src/agent_core/evals/runner.py
  • src/agent_core/execution/proxy.py
  • src/agent_core/mcp/__init__.py
  • src/agent_core/mcp/configuration.py
  • src/agent_core/mcp/mapping.py
  • src/agent_core/mcp/runtime.py
  • src/agent_core/ports/context.py
  • src/agent_core/ports/mcp.py
  • src/agent_core/ports/persistence.py
  • src/agent_core/ports/skills.py
  • src/agent_core/ports/tools.py
  • src/agent_core/runtime/executor.py
  • src/agent_core/skills/__init__.py
  • src/agent_core/skills/catalog.py
  • src/agent_core/skills/package.py
  • src/agent_core/tools/executor.py
  • src/agent_core/tools/messages.py
  • src/agent_core/tools/registry.py
  • src/agent_core/tools/skill_load.py
  • tests/contract/test_m_c_p_client_contract.py
  • tests/contract/test_m_c_p_client_factory_contract.py
  • tests/contract/test_m_c_p_server_repository_contract.py
  • tests/contract/test_skill_catalog_contract.py
  • tests/contract/test_skill_package_store_contract.py
  • tests/contract/test_skill_repository_contract.py
  • tests/eval_cases/27_skill_changes_outcome.yaml
  • tests/eval_cases/29_mcp_round_trip.yaml
  • tests/eval_cases/30_mcp_disconnect.yaml
  • tests/fixtures/mcp_stdio_environment_server.py
  • tests/gates/test_gate_registry.py
  • tests/gates/test_harness_m1.py
  • tests/gates/test_harness_m8.py
  • tests/gates/test_import_boundaries.py
  • tests/gates/test_skill_m8.py
  • tests/gates/test_tool_m8.py
  • tests/gates/test_trajectory_conversion_m3.py
  • tests/integration/test_skill_mcp_persistence_m8.py

Comment thread evals/gates/tool.yaml
Comment thread src/agent_core/adapters/mcp/memory.py Outdated
Comment thread src/agent_core/adapters/mcp/sdk.py
Comment thread src/agent_core/adapters/models/fake.py
Comment thread src/agent_core/adapters/persistence/skills.py Outdated
Comment thread src/agent_core/skills/package.py
Comment thread tests/gates/test_harness_m8.py
Comment thread tests/gates/test_tool_m8.py Outdated
Comment thread tests/gates/test_tool_m8.py Outdated
Comment thread tests/integration/test_skill_mcp_persistence_m8.py
@avitus

avitus commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

Addressed all 19 actionable CodeRabbit threads in 6a3311e and resolved each thread after posting the corresponding fix evidence. The review update also incorporates the summary nitpicks where applicable (SQL index/limit, rollback callback logging, catalog contract coverage, explicit transport enums, deterministic mapping indices, archive durability, and structured conflicts).

Validation: make check passed (268 static + 115 contract), all 74 PostgreSQL integration/resilience tests passed against a fresh migrated database, all 10 real-Docker sandbox tests passed, and the combined non-live suite passed 467/467. A final local CodeRabbit review found one additional persisted-empty-tool-pin edge case; that is fixed with an explicit initialization marker and regression in the same commit.

@avitus

avitus commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/gates/test_skill_m8.py (1)

574-596: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Test the token cap independently.

Line 587 rejects the third load at maximum_loaded == 2. The "one, two" assertion confirms that the item-count branch ran. The test never reaches sum(item.tokens for item in loaded) + tokens > self._maximum_body_tokens.

Set maximum_loaded to at least three. Set maximum_body_tokens between the cumulative totals. Assert the token-cap error.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/gates/test_skill_m8.py` around lines 574 - 596, Update test_body_cap to
configure maximum_loaded to at least three and maximum_body_tokens between the
cumulative token totals for the loaded skills, ensuring the third load reaches
the token-cap branch instead of the item-count limit. Assert the resulting
ConflictError from the token cap, while preserving the replacement-load
assertions.
🧹 Nitpick comments (2)
src/agent_core/application/session_service.py (1)

47-66: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the shared session bootstrap block.

PublicSessionService.create in src/agent_core/application/public_services.py repeats this exact sequence: generate the id, register the catalog discard callback, register the close-session callback, then open the catalog. Two copies of the cleanup ordering can drift, and the ordering is what makes rollback safe.

Extract a helper that takes the unit of work, the agent, and the principal, and returns the session id and the opened catalog. Call it from both paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agent_core/application/session_service.py` around lines 47 - 66, Extract
the shared session bootstrap sequence from SessionService.create_in and
PublicSessionService.create into a helper accepting the unit of work, agent, and
principal and returning the generated session ID plus opened catalog. Preserve
the existing rollback callback registration order—catalog discard before
close_session—and invoke this helper from both create paths.
src/agent_core/adapters/persistence/unit_of_work.py (1)

235-244: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Drain the callback list in place instead of rebinding it.

__aenter__ passes self._rollback_callbacks.append to the repository factory on line 182. That bound method captures the original list object. Line 236 rebinds self._rollback_callbacks to a new list, so the repositories keep appending to the orphaned list. Any callback registered after this point is never executed. The success path uses clear(), which keeps the binding intact, so the two paths behave differently.

Drain in place to keep one list identity for the lifetime of the unit of work.

♻️ Proposed change
     async def _run_rollback_callbacks(self) -> None:
-        callbacks, self._rollback_callbacks = self._rollback_callbacks, []
+        callbacks = list(self._rollback_callbacks)
+        self._rollback_callbacks.clear()
         for callback in reversed(callbacks):
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agent_core/adapters/persistence/unit_of_work.py` around lines 235 - 244,
In the `_run_rollback_callbacks` method, replace the rebinding pattern that
assigns a new empty list to `self._rollback_callbacks` with an in-place clear
operation to preserve the list identity that was captured by the bound `append`
method reference passed to repository factories during `__aenter__`. Extract the
callbacks to a local variable first by copying the current list, then call
`clear()` on `self._rollback_callbacks` to drain it in place, ensuring the same
list object remains bound for any callbacks registered after this method
executes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@tests/gates/test_skill_m8.py`:
- Around line 574-596: Update test_body_cap to configure maximum_loaded to at
least three and maximum_body_tokens between the cumulative token totals for the
loaded skills, ensuring the third load reaches the token-cap branch instead of
the item-count limit. Assert the resulting ConflictError from the token cap,
while preserving the replacement-load assertions.

---

Nitpick comments:
In `@src/agent_core/adapters/persistence/unit_of_work.py`:
- Around line 235-244: In the `_run_rollback_callbacks` method, replace the
rebinding pattern that assigns a new empty list to `self._rollback_callbacks`
with an in-place clear operation to preserve the list identity that was captured
by the bound `append` method reference passed to repository factories during
`__aenter__`. Extract the callbacks to a local variable first by copying the
current list, then call `clear()` on `self._rollback_callbacks` to drain it in
place, ensuring the same list object remains bound for any callbacks registered
after this method executes.

In `@src/agent_core/application/session_service.py`:
- Around line 47-66: Extract the shared session bootstrap sequence from
SessionService.create_in and PublicSessionService.create into a helper accepting
the unit of work, agent, and principal and returning the generated session ID
plus opened catalog. Preserve the existing rollback callback registration
order—catalog discard before close_session—and invoke this helper from both
create paths.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c62f7bc2-fede-4745-9b0d-7396274bb64e

📥 Commits

Reviewing files that changed from the base of the PR and between 07d78cc and 6a3311e.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (36)
  • migrations/versions/9a71c4e8d2f0_add_milestone_8_skills_and_mcp.py
  • pyproject.toml
  • src/agent_core/adapters/mcp/memory.py
  • src/agent_core/adapters/mcp/persistence.py
  • src/agent_core/adapters/mcp/sdk.py
  • src/agent_core/adapters/models/fake.py
  • src/agent_core/adapters/persistence/skills.py
  • src/agent_core/adapters/persistence/sqlalchemy_models.py
  • src/agent_core/adapters/persistence/unit_of_work.py
  • src/agent_core/adapters/skills/stores.py
  • src/agent_core/application/public_services.py
  • src/agent_core/application/session_service.py
  • src/agent_core/bootstrap.py
  • src/agent_core/context/builder.py
  • src/agent_core/domain/errors.py
  • src/agent_core/domain/mcp.py
  • src/agent_core/domain/runs.py
  • src/agent_core/mcp/configuration.py
  • src/agent_core/mcp/mapping.py
  • src/agent_core/mcp/runtime.py
  • src/agent_core/ports/persistence.py
  • src/agent_core/ports/skills.py
  • src/agent_core/ports/tools.py
  • src/agent_core/runtime/executor.py
  • src/agent_core/skills/catalog.py
  • src/agent_core/skills/package.py
  • src/agent_core/tools/registry.py
  • tests/contract/test_m_c_p_server_repository_contract.py
  • tests/contract/test_model_provider_contract.py
  • tests/contract/test_skill_catalog_contract.py
  • tests/gates/test_context_m7.py
  • tests/gates/test_harness_m8.py
  • tests/gates/test_runtime_m7.py
  • tests/gates/test_skill_m8.py
  • tests/gates/test_tool_m8.py
  • tests/integration/test_skill_mcp_persistence_m8.py
🚧 Files skipped from review as they are similar to previous changes (14)
  • pyproject.toml
  • src/agent_core/adapters/models/fake.py
  • src/agent_core/ports/persistence.py
  • src/agent_core/adapters/mcp/memory.py
  • tests/gates/test_harness_m8.py
  • src/agent_core/adapters/persistence/sqlalchemy_models.py
  • src/agent_core/domain/errors.py
  • src/agent_core/adapters/persistence/skills.py
  • src/agent_core/ports/skills.py
  • src/agent_core/ports/tools.py
  • src/agent_core/adapters/skills/stores.py
  • src/agent_core/bootstrap.py
  • src/agent_core/mcp/mapping.py
  • src/agent_core/mcp/runtime.py

@avitus

avitus commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

Addressed all three incremental summary findings in 0db9cff:

  • rollback callback queues are copied then cleared in place, preserving the list identity captured by repository append callbacks;
  • the rollback-safe session ID/catalog/MCP bootstrap sequence is centralized and shared by internal and public session creation;
  • test_body_cap now independently reaches and asserts the token-budget branch with maximum_loaded=3, while retaining the count-cap replacement assertions.

Validation: Ruff, strict mypy, make check (268 static + 115 contract), 42 focused session/skill/API gates, and five fresh-PostgreSQL skill/API integration tests all pass.

@avitus

avitus commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@avitus

avitus commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@avitus

avitus commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai rate limit

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

You're currently rate limited under our Fair Usage Limits Policy. Your recent PR review activity is in the 95th percentile or higher among CodeRabbit users, so adaptive limits apply. Your next review will be available in 40 seconds.

@avitus

avitus commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@avitus
avitus merged commit eeaac68 into dev Aug 4, 2026
6 checks passed
@avitus
avitus deleted the codex-milestone-8 branch August 10, 2026 22:17
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