Skip to content

Make MCP responses bounded and agent-native - #233

Open
flyingrobots wants to merge 33 commits into
mainfrom
cycle/agent-native-campaigns
Open

Make MCP responses bounded and agent-native#233
flyingrobots wants to merge 33 commits into
mainfrom
cycle/agent-native-campaigns

Conversation

@flyingrobots

@flyingrobots flyingrobots commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Summary

  • Default MCP receipts to a compact, exact-byte-accounted projection while preserving explicit full audit receipts.
  • Make doctor and activity diagnostics summary-first, with bounded full-detail opt-ins.
  • Publish bounded MCP output schemas, native structured content, and a seven-family capabilities discovery surface.
  • Distinguish baseline repository state from observed movement and preserve durable causal-history evidence truth.

Compatibility

  • MCP output contracts advance to version 2.0.0 for the intentional compact-default and structured-output changes.
  • CLI diagnostic JSON remains structurally identical to version 1.0.0 through an explicit nested compatibility projection checked against frozen pre-v2 schema digests.
  • Existing MCP tool names and CLI commands remain unchanged.
  • Optional legacy WARP identityId data remains accepted until that compatibility path is explicitly migrated.

Pre-PR Code Lawyer repairs

The complete branch review found three P1, three P2, and one P4 issue. This head repairs the concrete defects: transition occurrence identity, stale Git evidence, reflog baseline freshness, CLI-v1 shape preservation, legacy WARP response validation, pre-write graft_edit body validation, and agent-onboarding documentation consistency. The design packet and retro contain the RED/GREEN evidence for each repair.

Broader two-phase plan/validate/commit semantics for daemon control-plane mutations are explicitly filed as follow-up debt; this PR does not claim that future architecture as shipped.

Validation

  • pnpm typecheck
  • pnpm lint
  • pnpm exec vitest run --maxWorkers 2: 253 files, 1,910 tests passed
  • git diff --check origin/main...HEAD
  • Clean linked repair worktree at the validated runtime head

Evidence

  • Design: docs/design/SURFACE_agent-working-set-control-plane.md
  • Retro: docs/method/retro/SURFACE_agent-working-set-control-plane/SURFACE_agent-working-set-control-plane.md
  • Verification witness: docs/method/retro/SURFACE_agent-working-set-control-plane/witness/verification.md

Summary by CodeRabbit

  • New Features
    • Added MCP capability discovery, with summaries and family-specific tool details.
    • Added compact and full receipt options, including receipt correlation identifiers.
    • Added summary and full-detail modes for diagnostic and activity views.
    • MCP results now expose structured content alongside compatible text output.
  • Bug Fixes
    • Improved repository transition and conflict-state detection.
    • Prevented invalid tool responses from being applied.
    • Improved daemon status compatibility across receipt schema versions.

@flyingrobots

Copy link
Copy Markdown
Owner Author

@codex review please

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@flyingrobots, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 44 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9409ae9c-ad27-492d-abe8-c022717c872d

📥 Commits

Reviewing files that changed from the base of the PR and between 78174d2 and 2724f66.

⛔ Files ignored due to path filters (5)
  • docs/design/warp-lawpack-v0/README.md is excluded by !**/*.md
  • docs/design/warp-lawpack-v0/lowering-evidence.md is excluded by !**/*.md
  • docs/design/warp-lawpack-v0/pinned-epoch-evidence.md is excluded by !**/*.md
  • docs/design/warp-lawpack-v0/shape-isolation-evidence.md is excluded by !**/*.md
  • docs/design/warp-lawpack-v0/teaching-a-new-action-research.md is excluded by !**/*.md
📒 Files selected for processing (10)
  • docs/design/warp-lawpack-v0/authority-facts.json
  • docs/design/warp-lawpack-v0/evidence/echo-pinned-edict-build-failure.txt
  • docs/design/warp-lawpack-v0/evidence/edict-c75-pinned-run-output.txt
  • docs/design/warp-lawpack-v0/evidence/edict-c75-shape-isolation-output.txt
  • docs/design/warp-lawpack-v0/evidence/edict-main-lowering-output.txt
  • docs/design/warp-lawpack-v0/harness-c75-pinned.rs
  • docs/design/warp-lawpack-v0/harness-c75-shape-isolation.rs
  • docs/design/warp-lawpack-v0/harness.rs
  • docs/design/warp-lawpack-v0/record-symbol-change.c75-pinned.edict
  • docs/design/warp-lawpack-v0/record-symbol-change.edict
📝 Walkthrough

Walkthrough

This PR adds MCP capability discovery, structured output schemas, compact/full receipts, summary/full diagnostic responses, semantic transition evidence tracking, response validation before writes, and CLI v1 compatibility projections. It also updates integration, unit, and playback coverage for these contracts and flows.

Changes

MCP contracts and output schemas

Layer / File(s) Summary
Capability discovery and schema contracts
src/contracts/capabilities.ts, src/contracts/mcp-capability-discovery.ts, src/contracts/mcp-discovery-output-schemas.ts
Adds the capabilities tool, bounded family discovery responses, deterministic tool ordering, and projected per-tool output schemas.
Receipt, diagnostic, and versioned output contracts
src/contracts/output-schema-*.ts, src/contracts/causal-ontology.ts, src/contracts/diagnostic-*.ts
Adds compact/full receipt unions, MCP and CLI schema versions, bounded activity summaries, diagnostic response variants, and legacy CLI schemas.
MCP registration and validation wiring
src/mcp/server.ts, src/mcp/tool-input-controls.ts, src/mcp/server-invocation.ts, src/mcp/receipt.ts
Registers shared input/output schemas, resolves receipt modes, validates response bodies, and propagates receipt identifiers through worker and runtime paths.

Diagnostics and repository observations

Layer / File(s) Summary
Summary-first diagnostic tools
src/mcp/tools/doctor.ts, src/mcp/tools/activity-view.ts, src/mcp/tools/diagnostic-*.ts
Adds summary/full detail controls, bounded activity strings, diagnostic evidence gaps, and full-detail diagnostic responses.
Semantic transition evidence
src/mcp/repo-state-*.ts, src/mcp/persisted-local-history*.ts
Adds observation bases, refines merge/rebase/conflict classification, rejects stale reflog evidence, and persists transition evidence with legacy defaults.

Compatibility and validation

Layer / File(s) Summary
Structured MCP and CLI compatibility
src/api/tool-bridge.ts, src/cli/daemon-status.ts, src/cli/peer-command.ts, src/mcp/server-tool-access.ts
Prefers structured MCP content, conditionally requests full receipts, and projects MCP v2 payloads into CLI v1 shapes.
Integration and regression coverage
test/**, tests/playback/**
Covers capability discovery, structured payload parity, output-schema budgets, receipt modes, diagnostic projections, semantic transitions, worker propagation, and CLI compatibility.

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

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.63% 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 matches the PR’s main change: bounded MCP outputs and more agent-native structured responses.
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
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cycle/agent-native-campaigns

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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: 5a5a4c22ab

ℹ️ 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 src/cli/daemon-status.ts Outdated

Copy link
Copy Markdown
Owner Author

Review repair activity summary

Issue Severity Repair Evidence Outcome
New CLI sent MCP-v2 receipt controls to strict pre-v2 daemon tools P2 55d21c14 RED/GREEN unit compatibility cases; current-daemon CLI integration; typecheck; lint; diff check Daemon status now negotiates the control from each advertised input schema
Review-repair witness lagged the implementation Documentation d1f5f14d Design, retro, and verification witness addendum Current validation and remaining CI gate are stated explicitly

The exact inline thread has been answered and resolved. The backlog-card retirement was also followed by generated DAG repair 61209153; its executable DAG test passed.

@codex review please — please review current head d1f5f14d.

@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: d1f5f14d38

ℹ️ 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".

},
{
family: "diagnostic",
openingCall: "doctor",

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 Advertise a daemon-callable diagnostic opener

In an unbound daemon session, capabilities is callable before workspace binding, but this advertises doctor as the diagnostic openingCall; server-tool-access.ts only allows the daemonAlwaysAvailableTools set through before binding and then throws WorkspaceBindingRequiredError when !isBound, and doctor is not in that set. An agent following the advertised opener from daemon capability discovery will immediately fail until it binds a workspace, so use an always-available diagnostic opener such as causal_status/explain, or make summary doctor callable while unbound.

Useful? React with 👍 / 👎.

Comment thread src/mcp/server.ts
Comment on lines +258 to +259
const inputSchema = { ...(def.schema ?? {}), ...COMMON_TOOL_INPUT_SCHEMA };
const zodSchema = toolInputSchema(def.schema);

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 Accept omitted arguments for optional-only tools

When an MCP client omits arguments for tools whose only inputs are optional (stats, workspace_status, capabilities, or default doctor), these lines still register a non-optional object schema because every tool now gets the optional receipt control. MCP's schema reference allows CallToolRequest.params.arguments to be omitted, and that undefined is rejected against the registered object schema before the handler runs; previously tools without schemas used the zero-arg registration path and worked. Normalize missing args to {} or avoid adding an input schema for truly no-input tools.

Useful? React with 👍 / 👎.

@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: 3

🧹 Nitpick comments (4)
test/integration/mcp/server.test.ts (1)

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

Duplicated structured-content/schema-validation helper across two integration test files. Both files reimplement the same "parse structuredContent, assert it matches the text payload, validate against MCP_OUTPUT_SCHEMAS[tool]" logic; the root cause is the absence of one shared, exported helper.

  • test/integration/mcp/server.test.ts#L95-L109: keep (or move) the generic structuredPayload(result, tool: McpToolName) here as the canonical implementation, e.g. relocated into test/helpers/mcp.ts alongside extractText so both integration suites can import it.
  • test/integration/mcp/daemon-bridge.test.ts#L18-L36: replace structuredSafeRead and structuredCapabilities with calls to the shared generic helper (structuredPayload(result, "safe_read") / structuredPayload(result, "capabilities")) instead of maintaining two hardcoded copies.
🤖 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 `@test/integration/mcp/server.test.ts` around lines 95 - 109, The
structured-content and schema-validation logic is duplicated across the MCP
integration tests. In test/integration/mcp/server.test.ts lines 95-109, move or
retain structuredPayload(result, tool) as the shared exported helper, preferably
alongside extractText in test/helpers/mcp.ts; in
test/integration/mcp/daemon-bridge.test.ts lines 18-36, remove
structuredSafeRead and structuredCapabilities and replace their uses with
structuredPayload(result, "safe_read") and structuredPayload(result,
"capabilities").
src/cli/peer-command.ts (1)

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

Hardcoded detail-mode tool list is a future sync hazard.

requiresFullDetail hardcodes "doctor"/"activity_view" as the only tools needing detail: "full" for CLI v1 compatibility. Correct today, but if another diagnostic tool later gains a bounded summary/full split (matching the "diagnostic" family in mcp-capability-discovery.ts), it's easy to forget updating this list, silently degrading that tool's CLI peer output to summary-only data.

Consider deriving this list from a single shared, exported constant (colocated with the tools that implement the detail split) rather than duplicating tool names here.

🤖 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/cli/peer-command.ts` around lines 115 - 120, Replace the locally
hardcoded requiresFullDetail tool-name check in the peer command with a shared
exported constant defined alongside the diagnostic tools’ detail-mode
implementations, such as the source in mcp-capability-discovery.ts. Use that
constant to determine whether to send detail: "full", preserving the existing
receipt behavior and CLI compatibility.
src/contracts/output-schemas.ts (1)

223-268: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Duplicated contract definitions risk silent drift. Receipts, doctor, activity-view, semantic-transition, and mcpOutputBodySchemas are defined here and again in src/contracts/output-schema-fragments.ts / src/contracts/output-schema-mcp.ts. The code_find divergence flagged above is a concrete instance of this class of drift. Consider having one module import from the other (or a shared fragment) rather than re-declaring, so a change can't land in one copy only.

🤖 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/contracts/output-schemas.ts` around lines 223 - 268, Consolidate the
receipt schema definitions in output-schemas.ts with their duplicates in
output-schema-fragments.ts and output-schema-mcp.ts so there is a single source
of truth. Update the affected imports and consumers around fullReceiptSchema,
compactReceiptSchema, receiptSchema, and legacyCliReceiptSchema to reuse shared
definitions, preserving the existing validation behavior and public exports.
src/mcp/server-tool-access.ts (1)

120-133: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Fall back to text-block parsing when structuredContent is present but invalid.

If result.structuredContent is defined but tryParseJsonObject fails to validate it (e.g. non-object structured content from some other MCP caller), the function returns null immediately instead of falling back to parsing the content text block, which may still contain valid, parseable JSON. This is a narrow regression versus the prior behavior which always attempted the text-block parse.

♻️ Proposed fix
 export function parseToolPayload(result: McpToolResult): JsonObject | null {
-  if (result.structuredContent !== undefined) {
-    return tryParseJsonObject(result.structuredContent);
-  }
+  if (result.structuredContent !== undefined) {
+    const structured = tryParseJsonObject(result.structuredContent);
+    if (structured !== null) {
+      return structured;
+    }
+  }
   const textBlock = result.content.find((entry) => entry.type === "text");
   if (textBlock === undefined) {
     return null;
   }
   try {
     return parseJsonTextObject(textBlock.text, "MCP tool result");
   } catch {
     return null;
   }
 }
🤖 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/mcp/server-tool-access.ts` around lines 120 - 133, Update
parseToolPayload so an invalid structuredContent result from tryParseJsonObject
does not return immediately; when validation fails, continue to the existing
text-block lookup and parseJsonTextObject fallback, preserving the null result
when no valid text-block JSON is available.
🤖 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 `@src/contracts/output-schemas.ts`:
- Around line 1278-1296: Update the code_find schema in output-schema-mcp.ts to
match the union defined in output-schemas.ts, including the
structuralRefusalSchema extension with projection set to "refused" alongside the
existing strict object shape. Keep both validation paths synchronized so refused
code_find bodies are accepted consistently.

In `@src/mcp/persisted-local-history.ts`:
- Line 757: Update readTransitionEventFromGraph() so legacy transition nodes
missing observationBasis receive the schema-compatible default or migration
value before transitionEventSchema.safeParse() runs. Preserve the existing value
for nodes that provide observationBasis, ensuring older persisted events remain
in state.transitionEvents.

In `@src/mcp/tools/doctor.ts`:
- Around line 94-124: Update the summary health calculation in the detail ===
"summary" branch so the permanently seeded
"structural_history_readiness_unknown" evidence remains an informational
degradedReasons entry but does not force health to "degraded"; derive health
from the other actionable degradation reasons while preserving the existing
response shape and deduplication.

---

Nitpick comments:
In `@src/cli/peer-command.ts`:
- Around line 115-120: Replace the locally hardcoded requiresFullDetail
tool-name check in the peer command with a shared exported constant defined
alongside the diagnostic tools’ detail-mode implementations, such as the source
in mcp-capability-discovery.ts. Use that constant to determine whether to send
detail: "full", preserving the existing receipt behavior and CLI compatibility.

In `@src/contracts/output-schemas.ts`:
- Around line 223-268: Consolidate the receipt schema definitions in
output-schemas.ts with their duplicates in output-schema-fragments.ts and
output-schema-mcp.ts so there is a single source of truth. Update the affected
imports and consumers around fullReceiptSchema, compactReceiptSchema,
receiptSchema, and legacyCliReceiptSchema to reuse shared definitions,
preserving the existing validation behavior and public exports.

In `@src/mcp/server-tool-access.ts`:
- Around line 120-133: Update parseToolPayload so an invalid structuredContent
result from tryParseJsonObject does not return immediately; when validation
fails, continue to the existing text-block lookup and parseJsonTextObject
fallback, preserving the null result when no valid text-block JSON is available.

In `@test/integration/mcp/server.test.ts`:
- Around line 95-109: The structured-content and schema-validation logic is
duplicated across the MCP integration tests. In
test/integration/mcp/server.test.ts lines 95-109, move or retain
structuredPayload(result, tool) as the shared exported helper, preferably
alongside extractText in test/helpers/mcp.ts; in
test/integration/mcp/daemon-bridge.test.ts lines 18-36, remove
structuredSafeRead and structuredCapabilities and replace their uses with
structuredPayload(result, "safe_read") and structuredPayload(result,
"capabilities").
🪄 Autofix (Beta)

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 52a03186-8bf0-4be1-88a9-2a1a0ca57d9f

📥 Commits

Reviewing files that changed from the base of the PR and between c3885da and 78174d2.

⛔ Files ignored due to path filters (21)
  • CHANGELOG.md is excluded by !**/*.md
  • README.md is excluded by !**/*.md
  • docs/MCP.md is excluded by !**/*.md
  • docs/SETUP.md is excluded by !**/*.md
  • docs/TECHNICAL_TEARDOWN.md is excluded by !**/*.md
  • docs/design/SURFACE_agent-working-set-control-plane.md is excluded by !**/*.md
  • docs/design/WARP_local-causal-history-graph-schema.md is excluded by !**/*.md
  • docs/design/versioned-json-output-schemas.md is excluded by !**/*.md
  • docs/feedback/2026-07-14-codex-agent-native-campaigns.md is excluded by !**/*.md
  • docs/invariants/versioned-output-schemas.md is excluded by !**/*.md
  • docs/method/backlog/bad-code/CLEAN_mcp-output-schema-authority-duplicated-and-drifted.md is excluded by !**/*.md
  • docs/method/backlog/bad-code/CLEAN_method-drift-can-silently-pass-with-zero-playback-questions.md is excluded by !**/*.md
  • docs/method/backlog/bad-code/CLEAN_mutating-tools-need-prepared-response-contract.md is excluded by !**/*.md
  • docs/method/backlog/cool-ideas/SURFACE_git-graft-enhance-expanded-git-subcommands.md is excluded by !**/*.md
  • docs/method/backlog/cool-ideas/SURFACE_on-demand-exact-mcp-output-contracts.md is excluded by !**/*.md
  • docs/method/backlog/dependency-dag.dot is excluded by !**/*.dot
  • docs/method/backlog/dependency-dag.svg is excluded by !**/*.svg
  • docs/method/retro/SURFACE_agent-working-set-control-plane/SURFACE_agent-working-set-control-plane.md is excluded by !**/*.md
  • docs/method/retro/SURFACE_agent-working-set-control-plane/witness/verification.md is excluded by !**/*.md
  • docs/plans/graft-source-architecture-and-managed-workspace-convergence.md is excluded by !**/*.md
  • docs/three-surface-capability-matrix.md is excluded by !**/*.md
📒 Files selected for processing (80)
  • src/api/tool-bridge.ts
  • src/cli/daemon-status.ts
  • src/cli/peer-command.ts
  • src/contracts/capabilities.ts
  • src/contracts/causal-ontology.ts
  • src/contracts/diagnostic-evidence-gap.ts
  • src/contracts/diagnostic-summary-bounds.ts
  • src/contracts/mcp-capability-discovery.ts
  • src/contracts/mcp-discovery-output-schemas.ts
  • src/contracts/output-schema-cli.ts
  • src/contracts/output-schema-fragments.ts
  • src/contracts/output-schema-mcp.ts
  • src/contracts/output-schema-meta.ts
  • src/contracts/output-schemas.ts
  • src/mcp/burden.ts
  • src/mcp/context.ts
  • src/mcp/persisted-local-history-graph.ts
  • src/mcp/persisted-local-history-policy.ts
  • src/mcp/persisted-local-history.ts
  • src/mcp/receipt.ts
  • src/mcp/repo-state-git.ts
  • src/mcp/repo-state-observation.ts
  • src/mcp/repo-state-transition.ts
  • src/mcp/repo-state-types.ts
  • src/mcp/repo-state.ts
  • src/mcp/repo-tool-job.ts
  • src/mcp/repo-tool-worker-context.ts
  • src/mcp/runtime-observability.ts
  • src/mcp/semantic-transition-summary.ts
  • src/mcp/server-context.ts
  • src/mcp/server-invocation.ts
  • src/mcp/server-tool-access.ts
  • src/mcp/server.ts
  • src/mcp/tool-input-controls.ts
  • src/mcp/tool-registry.ts
  • src/mcp/tools/activity-view.ts
  • src/mcp/tools/capabilities.ts
  • src/mcp/tools/diagnostic-detail.ts
  • src/mcp/tools/diagnostic-models.ts
  • src/mcp/tools/doctor.ts
  • src/mcp/tools/graft-edit.ts
  • test/helpers/mcp.ts
  • test/integration/mcp/daemon-bridge.test.ts
  • test/integration/mcp/daemon-server.test.ts
  • test/integration/mcp/server.test.ts
  • test/unit/api/tool-bridge.test.ts
  • test/unit/cli/daemon-status.test.ts
  • test/unit/cli/peer-command.test.ts
  • test/unit/contracts/capabilities.test.ts
  • test/unit/contracts/causal-ontology.test.ts
  • test/unit/contracts/mcp-capability-discovery.test.ts
  • test/unit/contracts/mcp-discovery-output-schemas.test.ts
  • test/unit/contracts/output-schemas.test.ts
  • test/unit/library/index.test.ts
  • test/unit/mcp/capabilities.test.ts
  • test/unit/mcp/changed.test.ts
  • test/unit/mcp/context-guard.test.ts
  • test/unit/mcp/daemon-worker-pool.test.ts
  • test/unit/mcp/diagnostic-summary.test.ts
  • test/unit/mcp/graft-edit.test.ts
  • test/unit/mcp/layered-worldline.test.ts
  • test/unit/mcp/per-call-workspace-route.test.ts
  • test/unit/mcp/persisted-local-history.test.ts
  • test/unit/mcp/project-root-resolution.test.ts
  • test/unit/mcp/receipt-builder.test.ts
  • test/unit/mcp/receipt.test.ts
  • test/unit/mcp/repo-state-observation.test.ts
  • test/unit/mcp/repo-state-transition.test.ts
  • test/unit/mcp/runtime-observability.test.ts
  • test/unit/mcp/semantic-transition-guidance.test.ts
  • test/unit/mcp/server-tool-access.test.ts
  • test/unit/mcp/tools.test.ts
  • tests/playback/0058-system-wide-resource-pressure-and-fairness.test.ts
  • tests/playback/0078-three-surface-capability-baseline-and-parity-matrix.test.ts
  • tests/playback/0088-target-repo-git-hook-bootstrap.test.ts
  • tests/playback/CORE_migrate-path-ops-to-port.test.ts
  • tests/playback/CORE_v060-bad-code-burndown.test.ts
  • tests/playback/SURFACE_agent-dx-governed-edit.test.ts
  • tests/playback/SURFACE_capability-matrix-truth.test.ts
  • tests/playback/SURFACE_governed-write-tools.test.ts

Comment on lines +1278 to +1296
code_find: z.union([
z.object({
query: z.string(),
kind: z.string().nullable(),
matches: z.array(precisionSymbolMatchSchema).optional(),
total: z.number().int().nonnegative().optional(),
path: z.string().optional(),
projection: z.literal("refused").optional(),
reason: z.string().optional(),
reasonDetail: z.string().optional(),
next: z.array(z.string()).optional(),
actual: actualSchema.optional(),
source: z.enum(["warp", "live"]),
layer: worldlineLayerSchema,
}).strict(),
structuralRefusalSchema.extend({
projection: z.literal("refused"),
}).strict(),
]),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Compare the two code_find definitions and locate all mcpOutputBodySchemas definitions/imports.
rg -nP -C2 '\bcode_find\s*:' src/contracts/output-schemas.ts src/contracts/output-schema-mcp.ts
echo '--- definitions ---'
rg -nP 'mcpOutputBodySchemas\s*(:|=)' src/contracts
echo '--- importers ---'
rg -nP "import[^;]*mcpOutputBodySchemas" src

Repository: flyingrobots/graft

Length of output: 2339


🏁 Script executed:

sed -n '1,220p' src/contracts/output-schema-cli.ts
printf '\n---\n'
sed -n '1120,1650p' src/contracts/output-schemas.ts
printf '\n---\n'
sed -n '120,310p' src/contracts/output-schema-mcp.ts

Repository: flyingrobots/graft

Length of output: 33941


code_find schema drift breaks CLI validationsrc/contracts/output-schemas.ts accepts the refused variant, but src/contracts/output-schema-mcp.ts still defines code_find as the plain object shape. That leaves symbol_find on the CLI path rejecting refused code_find bodies while the other validation path accepts them. Keep both definitions in sync.

🤖 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/contracts/output-schemas.ts` around lines 1278 - 1296, Update the
code_find schema in output-schema-mcp.ts to match the union defined in
output-schemas.ts, including the structuralRefusalSchema extension with
projection set to "refused" alongside the existing strict object shape. Keep
both validation paths synchronized so refused code_find bodies are accepted
consistently.

payload: {
semanticKind: asString(node.props["semanticKind"]),
authority: asString(node.props["authority"]),
observationBasis: asString(node.props["observationBasis"]),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect transitionEventSchema's observationBasis field and asString's undefined handling.
rg -nP -A5 'transitionEventSchema\s*=' src/mcp/persisted-local-history.ts
rg -nP -A8 'function asString\(' src/mcp/persisted-local-history.ts

Repository: flyingrobots/graft

Length of output: 469


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '700,820p' src/mcp/persisted-local-history.ts | cat -n

Repository: flyingrobots/graft

Length of output: 6085


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find all references to observationBasis and the transition event schema.
rg -n -A4 -B4 'observationBasis|transitionEventSchema|eventKind:\s*"transition"' src/mcp/persisted-local-history.ts

Repository: flyingrobots/graft

Length of output: 4152


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the transition event schema and any import/migration logic around observationBasis.
sed -n '340,430p' src/mcp/persisted-local-history.ts | cat -n
printf '\n---\n'
sed -n '1030,1075p' src/mcp/persisted-local-history.ts | cat -n
printf '\n---\n'
sed -n '1488,1510p' src/mcp/persisted-local-history.ts | cat -n

Repository: flyingrobots/graft

Length of output: 5990


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the transition event schema definition and the write path that populates observationBasis.
rg -n -A20 -B10 'const transitionEventSchema|transitionEventSchema\s*=' src/mcp/persisted-local-history.ts
printf '\n---\n'
rg -n -A20 -B10 'createTransitionEvent\(|observationBasis' src/mcp/persisted-local-history.ts

Repository: flyingrobots/graft

Length of output: 156


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '80,170p' src/mcp/persisted-local-history.ts | cat -n
printf '\n---\n'
sed -n '1180,1265p' src/mcp/persisted-local-history.ts | cat -n
printf '\n---\n'
rg -n -A8 -B8 'createTransitionEvent|observationBasis' src/mcp/persisted-local-history.ts

Repository: flyingrobots/graft

Length of output: 10631


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the transition event schema and the createTransitionEvent implementation.
rg -n -A25 -B12 'const transitionEventSchema|export type TransitionEvent|createTransitionEvent\(' src/mcp/persisted-local-history.ts src/mcp/persisted-local-history-policy.ts

Repository: flyingrobots/graft

Length of output: 5961


Make observationBasis backward-compatible for legacy transition nodes. readTransitionEventFromGraph() passes asString(node.props["observationBasis"]) into transitionEventSchema.safeParse(), so older persisted nodes without this property are rejected and their transition events are dropped from state.transitionEvents. Add a fallback/migration if pre-existing graph data is still supported.

🤖 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/mcp/persisted-local-history.ts` at line 757, Update
readTransitionEventFromGraph() so legacy transition nodes missing
observationBasis receive the schema-compatible default or migration value before
transitionEventSchema.safeParse() runs. Preserve the existing value for nodes
that provide observationBasis, ensuring older persisted events remain in
state.transitionEvents.

Comment thread src/mcp/tools/doctor.ts
Comment on lines +94 to +124
if (detail === "summary") {
const localHistory = localHistorySummary(status, persistedLocalHistory);
const repoConcurrencyGap = concurrencyGap(repoConcurrency);
const degradedReasons: DiagnosticEvidenceGap[] = [
"structural_history_readiness_unknown",
...(status.bindState === "unbound" ? ["workspace_unbound" as const] : []),
...(localHistory.readiness === "unavailable"
? ["local_history_unavailable" as const]
: localHistory.readiness === "degraded"
? ["local_history_inactive" as const]
: []),
...(workspaceOverlayFooting === null
? []
: [workspaceOverlayFooting.degradedReason]),
...(repoConcurrencyGap === null
? []
: [repoConcurrencyGap]),
];
const response: DoctorSummaryResponse = {
health: degradedReasons.length === 0 ? "healthy" : "degraded",
workspace: workspaceSummary(status),
history: {
structural: {
readiness: "unknown",
reason: "not_observed",
},
local: localHistory,
},
degradedReasons: [...new Set(degradedReasons)],
recommendedNextAction,
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

health can never be "healthy" — a permanent degraded reason is always included.

degradedReasons unconditionally seeds "structural_history_readiness_unknown" (Line 98) regardless of actual state, and DoctorSummaryResponse.history.structural.readiness is typed as the literal "unknown" only, confirming structural evidence is never tracked today. Since health is derived purely from degradedReasons.length === 0, every summary response will report health: "degraded", making the "healthy" branch of the type unreachable in practice. This undermines the bounded, at-a-glance health signal that this new summary mode is meant to provide.

Consider excluding structural-history-unknown from the health computation (treat it as an informational caveat, not a health-degrading condition) until structural readiness tracking actually exists.

🩺 Proposed fix
-        const degradedReasons: DiagnosticEvidenceGap[] = [
-          "structural_history_readiness_unknown",
-          ...(status.bindState === "unbound" ? ["workspace_unbound" as const] : []),
+        const informationalGaps: DiagnosticEvidenceGap[] = ["structural_history_readiness_unknown"];
+        const degradedReasons: DiagnosticEvidenceGap[] = [
+          ...(status.bindState === "unbound" ? ["workspace_unbound" as const] : []),
           ...(localHistory.readiness === "unavailable"
             ? ["local_history_unavailable" as const]
             : localHistory.readiness === "degraded"
               ? ["local_history_inactive" as const]
               : []),
           ...(workspaceOverlayFooting === null
             ? []
             : [workspaceOverlayFooting.degradedReason]),
           ...(repoConcurrencyGap === null
             ? []
             : [repoConcurrencyGap]),
         ];
         const response: DoctorSummaryResponse = {
           health: degradedReasons.length === 0 ? "healthy" : "degraded",
           workspace: workspaceSummary(status),
           history: { ... },
-          degradedReasons: [...new Set(degradedReasons)],
+          degradedReasons: [...new Set([...informationalGaps, ...degradedReasons])],
           recommendedNextAction,
         };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (detail === "summary") {
const localHistory = localHistorySummary(status, persistedLocalHistory);
const repoConcurrencyGap = concurrencyGap(repoConcurrency);
const degradedReasons: DiagnosticEvidenceGap[] = [
"structural_history_readiness_unknown",
...(status.bindState === "unbound" ? ["workspace_unbound" as const] : []),
...(localHistory.readiness === "unavailable"
? ["local_history_unavailable" as const]
: localHistory.readiness === "degraded"
? ["local_history_inactive" as const]
: []),
...(workspaceOverlayFooting === null
? []
: [workspaceOverlayFooting.degradedReason]),
...(repoConcurrencyGap === null
? []
: [repoConcurrencyGap]),
];
const response: DoctorSummaryResponse = {
health: degradedReasons.length === 0 ? "healthy" : "degraded",
workspace: workspaceSummary(status),
history: {
structural: {
readiness: "unknown",
reason: "not_observed",
},
local: localHistory,
},
degradedReasons: [...new Set(degradedReasons)],
recommendedNextAction,
};
if (detail === "summary") {
const localHistory = localHistorySummary(status, persistedLocalHistory);
const repoConcurrencyGap = concurrencyGap(repoConcurrency);
const informationalGaps: DiagnosticEvidenceGap[] = ["structural_history_readiness_unknown"];
const degradedReasons: DiagnosticEvidenceGap[] = [
...(status.bindState === "unbound" ? ["workspace_unbound" as const] : []),
...(localHistory.readiness === "unavailable"
? ["local_history_unavailable" as const]
: localHistory.readiness === "degraded"
? ["local_history_inactive" as const]
: []),
...(workspaceOverlayFooting === null
? []
: [workspaceOverlayFooting.degradedReason]),
...(repoConcurrencyGap === null
? []
: [repoConcurrencyGap]),
];
const response: DoctorSummaryResponse = {
health: degradedReasons.length === 0 ? "healthy" : "degraded",
workspace: workspaceSummary(status),
history: {
structural: {
readiness: "unknown",
reason: "not_observed",
},
local: localHistory,
},
degradedReasons: [...new Set([...informationalGaps, ...degradedReasons])],
recommendedNextAction,
};
🤖 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/mcp/tools/doctor.ts` around lines 94 - 124, Update the summary health
calculation in the detail === "summary" branch so the permanently seeded
"structural_history_readiness_unknown" evidence remains an informational
degradedReasons entry but does not force health to "degraded"; derive health
from the other actionable degradation reasons while preserving the existing
response shape and deduplication.

Records the first Graft-domain Edict action (graft.warp@1 /
recordSymbolChange) proven to compile to real Core IR and lower to real
Echo Target IR (echo.dpo@1) via Edict's actual compiler, plus the
negative-control evidence confirming the custom compiler-context facts
genuinely participate in resolution.

Also captures a real dependency-drift finding: this experiment's harness
does not build against Echo's currently pinned Edict revision, because
of the TargetIrArtifact intents->actions rename in the very next Edict
commit. Not yet checked against Echo's own build/test suite directly.

Explicitly not validated: stale-basis enforcement, bundle assembly, WASM
execution, or persistence semantics. Those are tracked as next steps.
Extends the warp-lawpack-v0 spike per explicit instruction to hold
Echo's pin rather than regenerate its digest-locked provider package.

Mechanically derives an intent-keyword variant of the canonical
action-keyword source (single-token transform, checked to change
nothing else), compiles it under Edict c75c3f5 (Echo's actual pinned
revision, via a local worktree), and drives it through real invocation
of Echo's checked, unmodified lowerer.echo-dpo.component.wasm.

Result: the WASM component executes for real and returns a genuine
typed refusal (UnsupportedSemantics), not a crash or validation
failure -- the native oracle lowerer accepts this action shape but the
real, narrower checked component does not. Cross-version comparison
confirms the intent->action rename is a pure terminology migration
with no semantic drift in Core/Target IR for this action shape.

Bundle assembly, full runtime execution, persistence semantics, and
stale-basis enforcement remain unproven and out of scope for this
rung. The Echo compiler-epoch migration (rewriting the checked fixture,
regenerating the digest-locked package via echo-wesley-gen) remains a
separate, not-yet-started, explicitly scoped Echo-side change.
…shape

Answers the question raised before committing to the Echo intent->action
migration: is the checked lowerer component's UnsupportedSemantics
refusal about the vocabulary epoch, action shape, or something else?

Three variants under the identical pinned epoch and pipeline: the
exact original fixture (positive control), a single-field Graft
action matching the original's exact shape but with different names,
and the four-field recordSymbolChange from the prior rung.

Result: the original fixture is accepted; both the shape-matched and
shape-different Graft actions are refused identically. The checked
component currently accepts exactly one action identity -- the
original fixture it was built and reviewed against -- not a general
class of target.replace-shaped actions, regardless of intent/action
vocabulary.

Also records a harness bug found and fixed mid-experiment (a
hardcoded core-coordinate label caused a false negative on the
positive control) rather than silently discarding it.

Reframes the recommended next step: the Echo epoch migration alone
would not unblock new-action execution against this component. The
real gating question is what it takes to teach the checked component
a second action identity at all, which likely needs the same
echo-wesley-gen regeneration machinery either way.
… costs

Research-only, no Echo changes. Traces the UnsupportedSemantics refusal
to its exact source: echo-edict-provider-lowerer/-verifier hardcode the
original fixture's coordinate, type shapes, action name, profile, and
budget as literal Rust constants and equality checks -- not a
data-driven allowlist, real control flow compiled into the WASM
component.

Documents the two paths to change it (hand-written second recognizer
branch vs. genuinely generalizing into a real interpreter, the latter
complicated by the lowerer crate deliberately not depending on
edict-syntax) and the downstream cost either way: WASM recompile via
the existing xtask pipeline, echo-wesley-gen regeneration, re-
verification, and updating Echo's own digest-pinned tests.

Recommends neither path yet -- establishes cost, not a decision.
…ation

Full re-verification pass on docs/design/warp-lawpack-v0/: recomputed
every SHA-256 claim, diffed the two source variants against the
mechanical-derivation claim, cross-checked every quoted refusal/
diagnostic against its raw evidence file, re-read the hardcoded
constants directly from Echo's current source, and independently
rebuilt and reran all three harnesses end-to-end -- all three
reproduced byte-for-byte identical output to what's checked in.

Found and fixed:
- All three harness*.rs files had incorrect include_str! paths left
  over from their original scratch-directory layout (one referenced a
  nonexistent filename, two referenced a relative path that doesn't
  exist in this repo at all). Fixed to reference the actual checked-in
  record-symbol-change.edict, and reverified by rebuilding against the
  corrected paths.
- teaching-a-new-action-research.md understated the xtask packaging
  tool's size as "2,100+ lines" when it's actually 2,868.
- README's file table was missing two real files
  (record-symbol-change.c75-pinned.edict, teaching-a-new-action-
  research.md).
- Added a "Reproducing these results" section documenting exactly what
  a rebuild requires, since no Cargo.toml is checked in.

Everything else verified accurate: all SHA-256 hashes, the mechanical
action->intent diff, all quoted compiler diagnostics and refusals, and
all hardcoded-constant claims against Echo's live source.
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