Skip to content

fix(feedback): publish the advisory mount's own typed state - #2724

Merged
ScriptedAlchemy merged 2 commits into
masterfrom
fleet/root-cause-advisory-mount
Sep 30, 2026
Merged

ScriptedAlchemy merged 2 commits into
masterfrom
fleet/root-cause-advisory-mount

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Root cause

The deferred project-open advisory mount published no state of its own. The pre-mount placeholder owner (ProjectOpenProximityReadOwnerV1::mount) guessed whether a mount was in progress from two scheduler reads: retained_text_owner_freshness_for_scope(..).is_some() || latest_feedback_generation_for_scope(..).is_some() (added by #2228, widened by #2668). A mount that failed terminally still left a sealed generation behind, so the placeholder kept reporting Mounting. Every advisory-cycle request then waited out its whole dispatch deadline. The ponytail: note on mount() admitted this.

Fix

  • The advisory mount now owns a watch channel, AdvisoryMountStateV1, with four states: AwaitingGeneration, Mounting, Mounted, and Failed(AdvisoryMountFailureV1). The owner holds it through AdvisoryMountPublisherV1. Every exit publishes a verdict. The direct mount at project open publishes Mounted. The deferred task publishes Mounted, or Failed with its cause (code index disabled, feedback cycle, LSP grant, LSP owner, or advisory owner). Dropping the publisher without a verdict (a cancelled task, or a closed scheduler channel) publishes Failed(Abandoned). This mirrors ProjectRuntimeBuildReservationLeaseV1.
  • The placeholder's mount() reads only that state. Its invoke() returns feedback.advisory-cycle.mount-failed (unavailable, retry never, contact_administrator) once the state is Failed.
  • DaemonAdvisoryCycleMountV1::Mounting now carries a future that resolves when the owner's state changes. answering_advisory_cycle_owner waits on both the registry publication and that future, so a failure ends the wait right away.
  • The deferred owner classifies its own wait once, when admission declines. A retained generation still proving its source (the fix(feedback): wait for a reopened generation's source proof #2668 window) counts as Mounting. Anything else is AwaitingGeneration, which keeps the immediate retryable warming answer on a cold checkout.
  • Deleted: the two-place probe, the placeholder's own code-index-disabled wait check, and the ponytail: note.

Proof

Fail before (origin/master 9b79b48 plus only the new test), cargo test -p tracedecay --lib -- daemon::production_harness::advisory_cycle_language_journey_test:

WARN ... deferred advisory owner could not mount ... reason=config error: project-open advisory hook notice authority is unavailable   (00:56:33.456)
event=advisory_deferred_attempt ... phase=classified_failure attempt=retry_partial_publication   (x2, then the task returned)
test ...::reopened_checkout_whose_advisory_mount_failed_names_the_failure ... FAILED
tool failed: ... reason_code=tool_dispatch_deadline_exceeded ... tool 'tracedecay_feedback_advisory_cycle' exceeded its absolute deadline before commit   (00:56:48)
test result: FAILED. 3 passed; 1 failed

Pass after (rebased on 51aa2b8):

test ...::reopened_checkout_whose_advisory_mount_failed_names_the_failure ... ok
test ...::first_advisory_cycle_after_a_reopen_answers_without_a_retry ... ok
test ...::typescript_only_checkout_runs_the_pull_request_advisory_cycle ... ok
test ...::checkout_without_indexable_source_names_why_the_advisory_cycle_cannot_run ... ok
test result: ok. 16 passed; 0 failed   (journey tests + project_open_owners::advisory_runtime + tests::feedback_impact)

The new test goes through the production composition harness. It holds a foreign live hook-notice queue for the reopened checkout's scope, which is the production registry conflict that makes register_production_advisory_owner fail on every attempt. It then asserts problem.code == "feedback.advisory-cycle.mount-failed", kind == "unavailable", legal_actions == ["contact_administrator"], and an elapsed time under half the capability's canonical dispatch ceiling.

Other focused suites:

  • cargo test -p tracedecay-daemon-service --lib -- feedback advisory: 14 passed.
  • cargo test -p tracedecay --test mcp_suite --features test-transport -- feedback_diagnostics_test feedback_list_test affected_tests_test: 4 passed.
  • cargo clippy -p tracedecay-daemon-service -p tracedecay --all-targets -- -D warnings: clean.
  • cargo fmt --all -- --check: clean.

Runtime journey with the debug tracedecay CLI (1.0.0-beta.63+1331a4b3d3), an isolated HOME and data dir, and one daemon at a time under systemd-run --user --scope -p MemoryMax=6G -p MemorySwapMax=1G. The steps were init, advisory cycle, stop, restart, then the first advisory call after the reopen:

daemon first pid=1586179
init exit=0
first open settled after 1 call(s)
daemon first stopped
daemon second pid=1587509
reopen first call exit=0 elapsed=.582334277s
{"contract":{"schema_id":"schema.application.feedback.advisory-cycle.result",...},"outcome":{"outcome":"evidence",...
advisory_provider_states producers: ['git_hub_review', 'ci_localization', 'proximity']
daemon second stopped
pgrep: none of mine running

The terminal-failure path has no CLI-reachable trigger; the harness test above covers it through the production owner path.

@changeset-bot

changeset-bot Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 2774b2c

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 a908b62 into master Sep 30, 2026
4 of 7 checks passed
@ScriptedAlchemy
ScriptedAlchemy deleted the fleet/root-cause-advisory-mount branch September 30, 2026 03:05
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