Skip to content

feat(contracts): carry problem detail through edit, graph, diagnostics - #2289

Merged
ScriptedAlchemy merged 1 commit into
masterfrom
fleet/detail-followups-and-imports
Sep 27, 2026
Merged

ScriptedAlchemy merged 1 commit into
masterfrom
fleet/detail-followups-and-imports

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Follow-ups to #2259.

What was wrong

What changed

  • One renderer. The application-result renderer (render_application_result) moved from the root crate into tracedecay-mcp (application_output::tool_result). A new ApplicationRefusal { operation, binding_id, problem } keeps the daemon's whole problem envelope.
    • Source edit: run_source_edit returns Err(ApplicationRefusal) and render_source_edit_outcome renders it. Every source-edit refusal is now a semantic tool error carrying the record: problem beside the text, which MCP publishes as structuredContent.problem and --json prints. The CLI's mounting resend reads problem.owner_mount_resend_delay().
    • Graph tools: a refusal that carries detail renders whole. A code-only refusal stays the handler's argument or project-route error. Those problems are the owner's own flattening of handler errors (graph_tool_error_problem), with no kind or action of their own. graph_tool_error_problem now maps LockDeadline through ApplicationProblemDetailV1::from_lock_deadline.
  • New detail kinds, built only via ApplicationProblem::from_detail:
    • diagnostics_unsupported { file, searched: [{path, present}] }: unsupported, never retried, correct_request. Unsupported problems may now carry detail, in the record decoder, the wire, and the TS SDK.
    • diagnostics_pending { producer, generation }: unavailable, after_delay, retry. generation is the last generation the producer published, or null.
    • Labelled lines for text output: Diagnostics file, Searched tsconfigs, Diagnostics producer, Last published generation.
  • Producers migrated: no_tsconfig_problem and the pending arm of unpublished_diagnostics_problem. The producer path is now forward-slashed.
  • TS SDK: validates the two new kinds and allows detail on unsupported. It also validates reset_required detail: feat(contracts): carry the reset-required store as typed problem detail #2283 added that kind to Rust but not to the SDK, which refused it as malformed.
  • Regenerated dashboard/src/contracts, the schema bundle, and SDK types.ts. contracts:check is green.
  • The adoption census list gains the 17 composed capabilities.

Fail before / pass after

  • tracedecay-mcp handlers::edit::tests (reverting only render_source_edit_outcome to the flattening): 6 refusal tests fail with a refused source edit renders as a tool result: ... project route error (...). They pass with the fix. The new a_missed_writer_lock_deadline_reaches_mcp_as_its_typed_detail asserts detail == {kind: lock_deadline, resource: "source-edit writer lock", deadline_ms: 30000}, saturated, and ["retry"].
  • mcp_suite str_replace_behavior_test::str_replace_behind_a_held_writer_lock_reports_the_lock_deadline_detail holds the real source-edit-transactions-v1/source-edit.lock with an flock, then calls tracedecay_str_replace over MCP. It asserts structuredContent.problem = saturated / application.lock-deadline / after_delay / ["retry"] / detail.kind == "lock_deadline". On master the same call is a JSON-RPC error, so result is null.
  • daemon-service adoption_observation::tests::census_counts_each_composed_family_and_omits_uncomposed_families: on master it fails with composed but not listed: [("retrieval", "capability.application.primitive.affected", ...), ...], and passes here.
  • Diagnostics:
    • mcp_suite diagnostics_read_test::diagnostics_read_names_a_missing_producer_and_rejects_a_bad_scope pins the full problem record including detail. On master, detail is null.
    • typescript_file_no_tsconfig_owns_names_the_searched_paths asserts {kind: diagnostics_unsupported, file: "scripts/release.ts", searched: [{scripts/tsconfig.json,false},{tsconfig.json,false}]}.
    • typescript_producer_still_running_is_pending_with_typed_detail uses a held fixture compiler (new TypeScriptFixtureCompiler::Held) and asserts {kind: diagnostics_pending, producer: "node_modules/.bin/tsc", generation: null}.
    • CLI --json: core_cli_suite tool_diagnostics_json_carries_the_unowned_scope_detail and tool_diagnostics_json_carries_the_pending_producer_detail.
  • TS SDK, run against master's client.ts: surfaces the diagnostics detail an unsupported problem carries and surfaces the reset-required store detail fail (malformed response), and pass here.

Runtime journey (debug CLI from this branch)

Isolated HOME/TRACEDECAY_DATA_DIR, one daemon under systemd-run --user --scope -p MemoryMax=6G -p MemorySwapMax=1G.

$ flock <data>/source-edit-transactions-v1/source-edit.lock sleep 50 &
$ tracedecay tool tracedecay_str_replace --args '{"path":"src/lib.rs","old_str":"12","new_str":"40","dry_run":true}' --json   # 23:50:01 → 23:50:31, exit 1
 "kind": "saturated", "code": "application.lock-deadline", "retry": "after_delay", "legal_actions": ["retry"],
 "message": "The source-edit writer lock stayed busy past its 30000ms admission deadline; retry the operation.",
 "detail": {"deadline_ms": 30000, "kind": "lock_deadline", "resource": "source-edit writer lock"}
$ tracedecay tool diagnostics --args '{"scope":"file","path":"src/lib.rs"}' --json
{"kind": "unsupported", "code": "application.diagnostics.unsupported", "detail": {"kind": "diagnostics_unsupported", "file": "src/lib.rs", "searched": [{"path": "src/tsconfig.json", "present": false}, {"path": "tsconfig.json", "present": false}]}}
$ tracedecay tool diagnostics --args '{"scope":"workspace"}' --json
{"kind": "unsupported", "code": "application.diagnostics.unsupported", "detail": {"kind": "diagnostics_unsupported", "file": null, "searched": []}}
$ tracedecay tool diagnostics --args '{"scope":"file","path":"src/lib.rs"}'
- Problem: `application.diagnostics.unsupported`
- Diagnostics file: src/lib.rs
- Searched tsconfigs: src/tsconfig.json, tsconfig.json
# TypeScript project whose node_modules/.bin/tsc blocks until released:
$ tracedecay tool diagnostics --args '{"scope":"file","path":"src/index.ts"}' --json
 "kind": "unavailable", "code": "application.diagnostics.pending", "retry": "after_delay", "legal_actions": ["retry"],
 "detail": {"kind": "diagnostics_pending", "producer": "node_modules/.bin/tsc", "generation": null}

Local verification

Left open

Fixes #2255
Fixes #2246

The source-edit and graph-tool client adapters flattened a daemon problem
into a project-route error before MCP or CLI output, so a missed writer
lock deadline reached clients as a sentence without its typed detail,
retry directive, or legal actions. Source-edit refusals and detailed
graph-tool refusals now render the whole record through the one
application result renderer, which moved into tracedecay-mcp. The
graph-tool owner maps a lock deadline to its typed detail.

The TypeScript diagnostics producers wrote the unowned file, the searched
tsconfigs, and the pending producer into message text. They now build
diagnostics_unsupported and diagnostics_pending details through
ApplicationProblem::from_detail; unsupported problems may carry detail.

The adoption census test lists the graph lookup capabilities the
retrieval family composes.
@changeset-bot

changeset-bot Bot commented Sep 27, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 20dd81a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@ScriptedAlchemy
ScriptedAlchemy merged commit 0bbebcb into master Sep 27, 2026
1 check passed
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-27T03:19:41.064423Z 20dd81a PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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: 20dd81a8da

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

|| deadline.saturating_duration_since(Instant::now()) <= OWNER_MOUNT_RESEND_DELAY
{
break outcome?;
.await?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Restore cold-start retries for CLI graph tools

The new .await? propagates code-only graph refusals before this loop can inspect them. execute_graph_tool_surface still returns no-detail problems as an outer Err, and ApplicationProblem::runtime_mounting() has no detail, so a tracedecay tool graph command issued while a cold project is mounting now exits immediately instead of sleeping and retrying as the surrounding comment and previous implementation promise. Handle the outer mounting error in the loop or preserve it as an inner refusal.

AGENTS.md reference: AGENTS.md:L209-L212

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant