Skip to content

docs(mc-host): document managed lifecycle architecture - #67

Merged
ahrav merged 6 commits into
feat/mc-host-daemon-lifecyclefrom
stack/mc-host-19-docs-tracking
Aug 29, 2026
Merged

docs(mc-host): document managed lifecycle architecture#67
ahrav merged 6 commits into
feat/mc-host-daemon-lifecyclefrom
stack/mc-host-19-docs-tracking

Conversation

@ahrav

@ahrav ahrav commented Aug 26, 2026

Copy link
Copy Markdown
Owner

Summary

Documents the shipped ck-mc-host lifecycle owner, fixed three-component composition, immutable Broca closures, managed demand behavior, CLI commands, and Synapse package contract. Commits the Beads task export used for this work.

Generated machine-specific mc-host qualification evidence is now ignored:

  • docs/evidence/mc-host-installed-release-evidence.json
  • docs/evidence/mc-host-release-qualification.json
  • docs/evidence/mc-host-synapse-smoke.json

Verification

  • Beads JSONL parses
  • bun run test:release: 136 passed
  • cargo test -p mc-host --test protocol_vectors: 18 passed
  • git diff --check

Stack

Layer 9 of 9 above #46. Parent: #66.

@coderabbitai

coderabbitai Bot commented Aug 26, 2026

Copy link
Copy Markdown

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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b2fbcfa7-572e-4520-b297-a73b4b5449b0

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

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

@claude

claude Bot commented Aug 26, 2026

Copy link
Copy Markdown

Review summary

This PR is docs + generated task-tracking data only (.beads/issues.jsonl, .gitignore, ARCHITECTURE.md, STRUCTURE.md, docs/mc-host-wire-protocol.md, docs/synapse-model-bundle.md) — no application code changed, so there's nothing new to assess for runtime bugs, security, or performance in this diff itself. Checked for accidentally-committed secrets in the beads export (task notes mention "secret redaction" as a feature, not an actual leaked value) — none found.

Doc-quality notes below (left as inline comments too):

  1. docs/mc-host-wire-protocol.md §7.6 heading vs. content — the section is still titled ### 7.6 \host.shutdown`but now documentshost.statusfirst. The §7.4 cross-reference list also cites(Section 7.6)forhost.shutdownbut gives no section pointer forhost.status`, even though it lives in the same section. Minor navigability nit.
  2. Broadened unavailable fallback-reason semantics — the old text distinguished dynamic unavailability (provider Recovering/Quarantined, admission pressure) from permanent absence (which selected TCP with no reason), and described a client "automatic re-upgrade probe" that could retry a non-TCP candidate after unavailable. This PR replaces both with a single flat definition ("no installed provider serves an offered non-TCP transport") and deletes the re-upgrade-probe paragraph entirely, with no leftover references. Since this is a docs-only PR, please confirm this matches the already-shipped client/host behavior (i.e., the re-upgrade probe was actually removed from the implementation, not just from the docs) rather than the doc silently dropping a real capability.

Everything else (payload_manifest_digest now required + validated, the two-lock lifetime-fence model, the daemon_ver length-prefix added to the HMAC proof preimage, credential_fingerprints, the new host.status operation, and the synapse-model-bundle.md pinned-model/tolerance additions) reads as internally consistent with the rest of the document.

Comment thread docs/mc-host-wire-protocol.md Outdated
@@ -518,7 +531,23 @@ Both languages MUST produce identical bytes: UTF-8 pass-through for non-ASCII, t

### 7.6 `host.shutdown`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This section heading is still ### 7.6 \host.shutdown`but now documentshost.statusfirst (lines 534-543) before getting tohost.shutdown. Consider retitling to ### 7.6 `host.status` and `host.shutdown`so the heading matches the content, and so a reader scanning the TOC/section list can findhost.status`'s home.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in ac75372. The heading is now ### 7.6 host.status and host.shutdown (with both operation names in code spans), so it names both operations documented under it.

Comment thread docs/mc-host-wire-protocol.md Outdated
### 7.4 Operation classification

`route.open`, `catalog.list`, `host.shutdown` (Section 7.6), and `transport.negotiate` (Section 7.7) are the only required channel-0 operations. Every other operation receives terminal `unsupported_operation`; host stays connected if framing remains valid. `transport.activate` and `transport.commit` are candidate-channel-only (Section 7.7): on a bootstrap connection they are unrecognized operations and receive terminal `unsupported_operation` like any other. A client health operation MUST NOT proxy handler health, which remains host-internal.
`route.open`, `catalog.list`, `host.shutdown` (Section 7.6), `host.status`, and `transport.negotiate` (Section 7.7) are the only required channel-0 operations. Every other operation receives terminal `unsupported_operation`; host stays connected if framing remains valid. `transport.activate` and `transport.commit` are candidate-channel-only (Section 7.7): on a bootstrap connection they are unrecognized operations and receive terminal `unsupported_operation` like any other. `host.status` reads the last completed host-owned health snapshot; it never invokes a handler callback on the requesting connection and exposes only closed component states and sanitized metrics, never handler detail text.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

route.open, catalog.list, and transport.negotiate each cite their defining section in this sentence, and so does host.shutdown (Section 7.6), but the newly-added host.status has no section pointer even though it's documented in that same Section 7.6. Suggest host.status (Section 7.6)`` for consistency.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in ac75372. The §7.4 sentence now reads host.status (Section 7.6) alongside host.shutdown (Section 7.6), so every operation in that list carries its defining section.

Comment thread docs/mc-host-wire-protocol.md Outdated
| `reason` | Meaning |
| --- | --- |
| `unavailable` | an installed, statically eligible non-TCP offer is dynamically unavailable: provider readiness (`Recovering`/`Quarantined`) or admission pressure. Permanent absence of a provider and statically ineligible offer parameters are NOT `unavailable`; they select TCP with no `reason` |
| `unavailable` | no installed provider serves an offered non-TCP transport |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This flattens the previous distinction between dynamic unavailability (provider Recovering/Quarantined, admission pressure) and permanent absence (previously: selects TCP with no reason at all), and the paragraph below no longer mentions the client's "automatic re-upgrade probe" that used to be authorized specifically by this unavailable reason. Since this is a docs-only PR, can you confirm the re-upgrade-probe behavior was actually removed from the client/host implementation (in an earlier layer of this stack) rather than just undocumented here? If the probe still exists in code, this doc change would silently misdocument real behavior.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Good catch, and the answer is the one that made this a doc bug rather than a doc-only change: the re-upgrade probe was not removed from the implementation. The doc had dropped a real capability.

Verified in crates/mc-host/src/connection.rs. The host still distinguishes the two cases explicitly:

  • PreflightEligibility::DynamicallyUnavailable sets dynamically_unavailable, which becomes Some(FallbackReason::Unavailable) — an installed, statically eligible provider whose readiness or admission pressure is transient.
  • permanent absence falls through the None => {} arm and yields a reasonless TCP selection.

The comment on the reason-selection block states the reason unavailable outranks capability_version_mismatch precisely because it is the only reason that authorizes a client re-upgrade probe.

Restored in ac75372. §7.7.3 again separates dynamic unavailability from permanent absence, and the re-upgrade-probe paragraph is back, scoped to exact unavailable.

@ahrav
ahrav marked this pull request as ready for review August 29, 2026 07:32
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

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

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const PAYLOAD_INDEX_PATH = "release/mc-host-payload-index.json";
const STOP_PROVENANCE_PATH = "release/mc-host-n-minus-one-stop.json";
const SHA256_RE = /^[0-9a-f]{64}$/;
const QUALIFICATION_WORKFLOW_PATH = ".github/workflows/mc-host-release-qualification.yml";

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 Add the workflow required by the GA verifier

Qualified evidence is now required to claim this exact workflow path and to match a successful GitHub run at the release HEAD, but a repo-wide search of .github/workflows finds no mc-host-release-qualification.yml. Consequently, no run satisfying workflow === QUALIFICATION_WORKFLOW_PATH can be produced from this revision, so --check cannot ever accept GA evidence until the referenced workflow is added or the verifier is pointed at an existing qualification workflow.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Confirmed as a fact, and it still holds at the base branch tip (7df4d4b): QUALIFICATION_WORKFLOW_PATH is .github/workflows/mc-host-release-qualification.yml, and .github/workflows/ contains only ci.yml, claude-code-review.yml, retrieval-benchmark.yml, and shm-hardening-optin.yml. No run can satisfy workflow === QUALIFICATION_WORKFLOW_PATH.

Not actionable in this pull request. scripts/verify-mc-host-release-evidence.ts is not part of this change — this branch touches six files (.github/workflows/ci.yml, .gitignore, ARCHITECTURE.md, STRUCTURE.md, docs/mc-host-wire-protocol.md, docs/synapse-model-bundle.md). The verifier arrived on the base branch in 8d7c12d, so the finding belongs to the release-qualification work, and authoring a GA qualification workflow here would be a large change well outside a documentation branch.

One mitigation worth recording: the verifier fails closed rather than silently. verify-mc-host-release-evidence.ts:836-838 checks existsSync on the path and reports qualification workflow ... does not exist in this checkout, so --check refuses GA evidence with a named reason instead of accepting unqualified evidence.

Comment thread .github/workflows/ci.yml Outdated
Comment on lines +185 to +187
mc-host-lifecycle:
name: mc-host lifecycle integration
runs-on: ubuntu-latest

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep the lifecycle binary covered by the macOS build

The lifecycle build/tests were removed from the existing Linux/macOS matrix and moved into this Ubuntu-only job. That leaves mc-module and ck-mc-host entirely uncompiled on macOS even though this change adds and modifies macOS-specific lifecycle paths such as /dev/fd execution and the close_range fallback; a Darwin-only compile or linkage regression can therefore pass CI and break the supported darwin-arm64 and darwin-x64 payloads.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Not reproduced — this reads a revision older than the branch point.

.github/workflows/ci.yml has a mc-host-lifecycle job whose matrix is os: [ubuntu-latest, macos-latest], and it carries a macOS-specific step that compiles exactly the binary you are asking about:

- name: Native lifecycle binary and CLI contract (macOS)
  if: runner.os == 'macOS'
  run: |
    cargo build -p mc-module --bin ck-mc-host
    cargo test -p mc-module --test lifecycle_cli

The step's own comment gives the reason you gave: the release ships darwin payloads and the GA evidence gate requires darwin target proofs, so the native lifecycle binary and its CLI contract need macOS proof alongside the Linux integration set. mc-module and ck-mc-host are therefore compiled on macOS, and a Darwin-only compile or linkage regression fails CI.

This branch's only ci.yml change is additive — six lines pinning dtolnay/rust-toolchain@stable in the plugin check job. It removes nothing from any matrix.

Comment on lines +279 to +281
if require_previous_credentials && changed {
return Err("restart cannot change the active harness selection");
}

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 Allow later managed owners to merge qualified harness closures

When one product has already started the shared daemon with its OpenCode closure and the Pi owner later supplies its qualified closure, the selection necessarily changes, so this new check rejects the only restart path that could retain both harnesses. The production demand factory builds an envelope only for its own parent package, while stop clears the prior selection; unlike the synthetic smoke's explicit mergedEnvelope, no installed owner can therefore stop and restart with both descriptors. The second harness remains unavailable, or replacing it makes the first unavailable, instead of converging on one shared daemon.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Addressed on the base branch, and out of scope here.

crates/mc-module/src/bin/ck_mc_host/serve.rs is not part of this change; this branch touches six documentation, CI, and ignore files. That file was last modified by d2e9a11, titled fix(mc-host): clear stale harness selections instead of wedging on them, which is the branch point for this pull request.

At that revision and at the base tip, merge_selection does merge rather than reject: it starts from previous.clone() and layers each supplied descriptor over it, so an owner that supplies only its own harness retains the other. The rejection you quoted is now gated on require_previous_credentials, which is the restart path, and the second rejection fires only when a new owner cannot preserve the active credential source.

If a merge path is still missing for a specific installed-owner sequence, that belongs on the lifecycle branch that owns serve.rs rather than this one.

@kilo-code-bot

kilo-code-bot Bot commented Aug 29, 2026

Copy link
Copy Markdown

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (6 files)
  • .github/workflows/ci.yml
  • .gitignore
  • ARCHITECTURE.md
  • STRUCTURE.md
  • docs/mc-host-wire-protocol.md
  • docs/synapse-model-bundle.md
Previous Review Summaries (4 snapshots, latest commit fbc5204)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit fbc5204)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (6 files)
  • .github/workflows/ci.yml
  • .gitignore
  • ARCHITECTURE.md
  • STRUCTURE.md
  • docs/mc-host-wire-protocol.md
  • docs/synapse-model-bundle.md

Previous review (commit ac75372)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (7 files)
  • .beads/issues.jsonl
  • .github/workflows/ci.yml
  • .gitignore
  • ARCHITECTURE.md
  • STRUCTURE.md
  • docs/mc-host-wire-protocol.md
  • docs/synapse-model-bundle.md

Previous review (commit 7678eae)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (7 files)
  • .beads/issues.jsonl
  • .github/workflows/ci.yml
  • .gitignore
  • ARCHITECTURE.md
  • STRUCTURE.md
  • docs/mc-host-wire-protocol.md
  • docs/synapse-model-bundle.md

Previous review (commit dbacfee)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (6 files)
  • .beads/issues.jsonl
  • .gitignore
  • ARCHITECTURE.md
  • STRUCTURE.md
  • docs/mc-host-wire-protocol.md
  • docs/synapse-model-bundle.md

Reviewed by gemini-3.7-flash · Input: 109.6K · Output: 5.9K · Cached: 412.3K

Base automatically changed from stack/mc-host-18-ci-dependency-warmup to feat/mc-host-daemon-lifecycle August 29, 2026 21:15
ahrav added 2 commits August 29, 2026 21:28
The check-plugin job invokes cargo twice: the dependency-graph warm-up
step, and the same `cargo tree -p mc-host` that test:release shells out
to. Neither had a toolchain installed, so both resolved to whatever
rustc the ubuntu-latest image happens to ship. A runner-image change
would then surface as a cargo failure inside an otherwise TypeScript
job, with nothing pointing at a missing toolchain.

Install stable before the first cargo call, matching shm-crash-recovery,
shm-source-build, and mc-host-lifecycle.
@ahrav
ahrav force-pushed the stack/mc-host-19-docs-tracking branch from 7678eae to b71bf85 Compare August 29, 2026 21:33

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

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread .beads/issues.jsonl Outdated
{"_type":"issue","id":"magic-context-ymc.7","title":"T6: Wakeup protocol: spin-then-park, eventfd/futex, missed-wakeup proof","description":"BUILD-path only. Latency path: bounded spin (N iterations with spin_loop hint) then park on futex (Linux) — waker only issues syscall when peer state says parked. The hard part is the missed-wakeup race: sleeper must (1) publish 'parking' flag, (2) re-check ring non-empty predicate, (3) then futex_wait with expected value — classic check-flag-check ordering. Document the interleaving argument.\n\nSkills: /performance:linux-event-loop-runtime-mechanics (wake predicates, missed-wakeup prevention), /low-level-systems:scalable-concurrency (spin-then-park construction, spin budget), /low-level-systems:rust-memory-ordering (flag/predicate ordering).\n\nTests: (1) loom model of the park/wake protocol — must include the torn interleaving where wake races park; (2) stress test: producer bursts with random gaps, assert no consumer hang (timeout = fail); (3) SIGSTOP/SIGCONT the consumer mid-run, assert recovery; (4) latency bench: spin-hit path vs park path measured separately.\nAcceptance: loom green; 24h-equivalent stress (nextest slow-timeout bounded) with zero hangs; spin budget tunable via config.","status":"closed","priority":1,"issue_type":"task","owner":"ahravdutta02@gmail.com","created_at":"2026-08-22T23:14:21Z","created_by":"AhravDutta","updated_at":"2026-08-22T23:19:36Z","closed_at":"2026-08-22T23:19:36Z","close_reason":"Superseded by ADOPT iceoryx2 (ymc.2): Event/Listener/WaitSet owns wakeup; no custom futex protocol.","dependencies":[{"issue_id":"magic-context-ymc.7","depends_on_id":"magic-context-ymc","type":"parent-child","created_at":"2026-08-22T23:14:21Z","created_by":"AhravDutta","metadata":"{}"},{"issue_id":"magic-context-ymc.7","depends_on_id":"magic-context-ymc.2","type":"blocks","created_at":"2026-08-22T23:14:32Z","created_by":"AhravDutta","metadata":"{}"},{"issue_id":"magic-context-ymc.7","depends_on_id":"magic-context-ymc.5","type":"blocks","created_at":"2026-08-22T23:14:34Z","created_by":"AhravDutta","metadata":"{}"}],"dependency_count":2,"dependent_count":1,"comment_count":0}
{"_type":"issue","id":"magic-context-ymc.2","title":"T1 DECISION (RESOLVED): ADOPT iceoryx2 — do not hand-roll shm transport","description":"DECISION: ADOPT iceoryx2. Research 2026-08-22, evidence:\n\n- iceoryx2 v0.9.3 (crates.io, actively maintained, Eclipse project), MIT OR Apache-2.0, MSRV-friendly.\n- Request/response messaging pattern supported natively (plus pub/sub, events).\n- macOS: tier 2, 'done' — removes our memfd-is-Linux-only fallback problem; same transport both platforms, TCP fallback stays for remote/degraded only.\n- Dynamic payload sizes supported (loan_slice / dynamic data) — kills the large-payload task.\n- Lock-free zero-copy queues + segment lifecycle + decentralized crash cleanup built in — exactly the code where hand-rolled bugs live (T3-T7 deleted).\n- Published latency: sub-µs, payload-size-independent; MUST be re-verified on our hardware (T0 budget, T9 verdict).\n\nKnown gap: no async API (roadmap issue #47). Integration = dedicated listener thread bridging iceoryx2 events to the tokio runtime. Accepted cost, scoped in the integration task.\n\nPonytail-review verdict on the build path: T3/T4/T6/T8/T10 native (iceoryx2 owns it), T5 shrinks to service config, T7 shrinks to our-layer hardening. Net: -5 tasks, ~-2500 LoC of unsafe/concurrency code never written.\n\nESCAPE HATCH: reopen build path only if (a) T9 bench on our hardware misses the T0 budget materially, or (b) the sync-bridge integration proves untenable in practice. Closed build tasks retain full specs for that case.","status":"closed","priority":1,"issue_type":"decision","owner":"ahravdutta02@gmail.com","created_at":"2026-08-22T23:13:28Z","created_by":"AhravDutta","updated_at":"2026-08-22T23:19:35Z","closed_at":"2026-08-22T23:19:35Z","close_reason":"Decision resolved: ADOPT iceoryx2. Evidence + escape hatch recorded in description. T0 baseline still runs — it feeds the T9 verdict, no longer gates this decision.","dependencies":[{"issue_id":"magic-context-ymc.2","depends_on_id":"magic-context-ymc","type":"parent-child","created_at":"2026-08-22T23:13:27Z","created_by":"AhravDutta","metadata":"{}"},{"issue_id":"magic-context-ymc.2","depends_on_id":"magic-context-ymc.1","type":"blocks","created_at":"2026-08-22T23:14:32Z","created_by":"AhravDutta","metadata":"{}"}],"dependency_count":1,"dependent_count":7,"comment_count":0}
{"_type":"issue","id":"magic-context-ymc.3","title":"T2: Transport seam: swappable channel trait + TCP fallback + runtime negotiation","description":"Abstract the client↔daemon channel behind one small trait (send/recv frames) so TCP and shm are interchangeable. Runtime negotiation: client requests shm, daemon grants or falls back to TCP (macOS, remote, version mismatch). No behavior change in this task — TCP impl only, seam + negotiation protocol.\n\nNeeded regardless of T1 verdict. Keep the trait minimal: no async-trait gymnastics beyond what subc-transport already forces (/design-review:async-api-contract-design only if the seam gets contentious).\n\nTests (/testing:test-strategy): CONTRACT test suite written against the trait, run against the TCP impl now, every future impl later — same suite proves swap safety. Negotiation matrix test: grant, fallback, version-mismatch → TCP.\nAcceptance: existing mc-host integration tests green through the seam; contract suite in place.","notes":"Implementation plan: docs/plans/2026-08-23-2129-feat-transport-channel-negotiation-plan.md. Non-interactive document review applied 3 mechanical fixes; 8 decisions and 11 proposed fixes remain available for walkthrough.","spec_id":"docs/plans/2026-08-23-2129-feat-transport-channel-negotiation-plan.md","status":"closed","priority":1,"issue_type":"task","assignee":"AhravDutta","owner":"ahravdutta02@gmail.com","created_at":"2026-08-22T23:13:28Z","created_by":"AhravDutta","updated_at":"2026-08-24T13:41:22Z","started_at":"2026-08-24T05:07:48Z","closed_at":"2026-08-24T13:41:22Z","close_reason":"Closed","dependencies":[{"issue_id":"magic-context-ymc.3","depends_on_id":"magic-context-ymc","type":"parent-child","created_at":"2026-08-22T23:13:27Z","created_by":"AhravDutta","metadata":"{}"}],"dependency_count":0,"dependent_count":3,"comment_count":0}
{"_type":"issue","id":"magic-context-ymc.3","title":"T2: Transport seam: swappable channel trait + TCP fallback + runtime negotiation","description":"Abstract the client↔daemon channel behind one small trait (send/recv frames) so TCP and shm are interchangeable. Runtime negotiation: client requests shm, daemon grants or falls back to TCP (macOS, remote, version mismatch). No behavior change in this task — TCP impl only, seam + negotiation protocol.\n\nNeeded regardless of T1 verdict. Keep the trait minimal: no async-trait gymnastics beyond what subc-transport already forces (/design-review:async-api-contract-design only if the seam gets contentious).\n\nTests (/testing:test-strategy): CONTRACT test suite written against the trait, run against the TCP impl now, every future impl later — same suite proves swap safety. Negotiation matrix test: grant, fallback, version-mismatch → TCP.\nAcceptance: existing mc-host integration tests green through the seam; contract suite in place.","notes":"Implementation plan: docs/plans/2026-08-23-2129-feat-transport-channel-negotiation-plan.md. Non-interactive document review applied 3 mechanical fixes; 8 decisions and 11 proposed fixes remain available for walkthrough.","spec_id":"docs/plans/2026-08-23-2129-feat-transport-channel-negotiation-plan.md","status":"open","priority":1,"issue_type":"task","owner":"ahravdutta02@gmail.com","created_at":"2026-08-22T23:13:28Z","created_by":"AhravDutta","updated_at":"2026-08-23T21:49:40Z","dependencies":[{"issue_id":"magic-context-ymc.3","depends_on_id":"magic-context-ymc","type":"parent-child","created_at":"2026-08-22T23:13:27Z","created_by":"AhravDutta","metadata":"{}"}],"dependency_count":0,"dependent_count":3,"comment_count":0}

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 Preserve the completed Beads state

This replacement rewinds magic-context-ymc.3 from closed at 2026-08-24 to open with an older timestamp and drops its closed_at, close_reason, started_at, and assignee fields. The same export also rolls back progress or dependency metadata for ymc.8, ymc.10, ymc.12, and a52, so the committed tracking snapshot falsely presents completed or claimed work as available again; regenerate the export from the current Beads state while adding the new c50 issues.

AGENTS.md reference: AGENTS.md:L3-L10

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Confirmed, and your list was complete — I found one more of the same kind while checking it.

Verified against the live Beads database rather than either committed export. bd show magic-context-ymc.3 reports CLOSED, updated 2026-08-24, close reason Closed, assignee AhravDutta, which matches the branch point and contradicts the export committed here. Comparing every record against the branch point found exactly five rewound timestamps — the five you named — plus:

  • magic-context-c50.4: dependent_count 3 → 2 at an unchanged updated_at, where live Beads reports 3.

Field losses were also real: ymc.3 dropped closed_at, close_reason, started_at, and assignee; ymc.8, ymc.10, and ymc.12 dropped spec_id, assignee, started_at, and in two cases notes and acceptance_criteria.

Resolved in f8968ae by taking the base branch's export, which the merge conflict forced a decision on anyway. That file is the current one: 223 records against this branch's 196, no issue id lost, and no updated_at moved backwards. This branch no longer carries a .beads/issues.jsonl diff at all — its net change is now six documentation, CI, and ignore files — so the rewind is gone at the source rather than patched.

One note on spec_id, since a straight regeneration does not preserve it: that field is absent from all 241 records in the live database, so it is stale metadata in the committed exports rather than live state. Reintroducing it would commit data the source of truth does not hold.

Comment thread docs/mc-host-wire-protocol.md Outdated
| `reason` | Meaning |
| --- | --- |
| `unavailable` | an installed, statically eligible non-TCP offer is dynamically unavailable: provider readiness (`Recovering`/`Quarantined`) or admission pressure. Permanent absence of a provider and statically ineligible offer parameters are NOT `unavailable`; they select TCP with no `reason` |
| `unavailable` | no installed provider serves an offered non-TCP transport |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restore the dynamic meaning of unavailable

For an offered non-TCP transport with no installed provider, the implementation deliberately returns reasonless TCP (connection.rs:943-944, asserted by transport_negotiation.rs:850-867); unavailable is emitted only for an installed, statically eligible provider that is temporarily unavailable (connection.rs:931-954) and is what authorizes a later re-upgrade probe. This new definition states the opposite and removes that recovery rule, leaving the normative protocol incompatible with the shipped host and potentially causing independently implemented clients or hosts to retry permanent absence while suppressing recovery from transient outages.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Correct, and your code references are accurate — thank you for citing them, they made this quick to confirm.

Verified in crates/mc-host/src/connection.rs: PreflightEligibility::DynamicallyUnavailable is what produces Some(FallbackReason::Unavailable), permanent absence falls through the None => {} arm to a reasonless TCP selection, and the comment on the reason-selection block states that unavailable outranks capability_version_mismatch because it is the only reason authorizing a client re-upgrade probe.

Fixed in ac75372, which landed at 21:50 UTC, about eleven minutes after this comment. §7.7.3 now reads:

unavailable — an installed, statically eligible non-TCP offer is dynamically unavailable: provider readiness (Recovering/Quarantined) or admission pressure. Permanent absence of a provider and statically ineligible offer parameters are NOT unavailable; they select TCP with no reason

and the re-upgrade-probe rule is restored, scoped to exact unavailable, with reasonless selections and every other fallback reason explicitly excluded from starting or extending the probe window.

Three review findings in the wire protocol doc, all cases where the
prose contradicted the code it specifies.

Section 7.7.3 had redefined `unavailable` as "no installed provider
serves an offered non-TCP transport", which is the case the host
excludes: permanent absence falls to `None` in negotiate_transport and
yields a reasonless TCP selection, and `FallbackReason::Unavailable` is
set only from `PreflightEligibility::DynamicallyUnavailable`. The
`unprovided_non_tcp_offer_selects_reasonless_tcp` test pins that
behavior. The same hunk had dropped the paragraph authorizing the client
re-upgrade probe, including its bounded-episode deadline and MUST NOT
list, while that behavior ships in McHostClient (`startRecovery` gates
on exactly `unavailable`) and connection.rs cites the section by number.
Both are restored verbatim, so no code-cited requirement is left without
a normative basis.

Section 7.6 specified the sanitized `host.status` metrics as only the
three component state fields, omitting the `metrics.epochs` object the
host emits for `magic-context`. That field is mandatory downstream:
evaluateEpochCompatibility requires all five epochs present, numeric,
and equal, and an absent object collapses to `{}` and fails the gate
with `incompatible_epochs`. Document the five names, the u32 bound, the
all-or-nothing sanitization, and that a sixth epoch is a breaking change
on both sides.

Also retitle 7.6, which specified `host.status` under a heading naming
only `host.shutdown`, and give `host.status` the section pointer 7.4
already gives every neighbor.

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

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread docs/synapse-model-bundle.md Outdated
Comment on lines +206 to +207
only from that revalidated generation. macOS uses the host-only lane and
reports exact readiness `unsupported` / `synapse_unsupported`.

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 Make the advertised macOS host lane startable

On every macOS target, ck-mc-host serve still constructs BrocaComponent (serve.rs:881-889), the composite requires every child initializer to succeed (composite.rs:217-227), and Broca explicitly returns Err on non-Linux platforms (broca/mod.rs:369-383). Consequently the daemon exits before publication and can never reach the newly documented unsupported / synapse_unsupported readiness state. Either supply a non-Linux unavailable Broca component or stop advertising this macOS host-only lane.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Confirmed, and your three code references are exact.

Verified:

  • serve.rs:946-954 builds the composite with BrocaComponent::new_with_credentials(...) as an unconditional child. Note the contrast with synapse_component immediately above it, which is #[cfg(target_os = "macos")]-gated and returns SynapseComponent::unsupported("synapse_unsupported"). Broca has no such gate.
  • broca/mod.rs SecondaryComponent::initialize returns Err(InitError("broca requires Linux: crash-ownership records and sweeps depend on /proc process identity")) under cfg!(not(target_os = "linux")).
  • composite.rs initialize uses tokio::try_join! across primary, secondary, and tertiary, so any child error fails the whole initialization.

So a macOS daemon exits before publication, and the readiness value the doc advertised is unobservable over the wire even though the component computes it.

Fixed in fbc5204, taking your second option — stop advertising a startable macOS lane. The bundle doc now states that the Synapse component carries unsupported / synapse_unsupported on macOS, that no client observes it because the daemon never reaches publication, and names the reason (Broca refuses off Linux, the composite requires every child initializer to succeed). It also records the precondition for serving the lane there: a Broca component that initializes off Linux as unavailable rather than failing.

Comment thread docs/mc-host-wire-protocol.md Outdated
A separate owner-only directory, `${dataDir}/cortexkit/lifecycle`, carries the cross-process lifecycle transaction lock on its directory inode: mutating lifecycle transactions (launcher start/stop, owned by downstream packaging work) take it exclusively, and probes take it shared when it exists. It is distinct from the runtime-directory instance lock, which only the daemon holds for its whole incarnation.
Cross-process lifecycle serialization lives in a fixed, version-neutral, owner-only directory directly under the data root: `${dataDir}/.mc-host-coordination/`, containing the never-renamed regular files `transaction.lock` and `lifetime.lock`. Supported code never renames, replaces, or unlinks the directory or either file; same-UID replacement of that root or an ancestor is trusted-policy replacement outside this boundary.

- `transaction.lock` carries the exclusive flock serializing mutating lifecycle transactions (launcher start/stop, owned by downstream packaging work); probes take it shared, without creating anything, when it exists.

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 Document the probe's lock-contention fallback

When a normal start, stop, or restart holds transaction.lock longer than the four 25 ms acquisition attempts, LifecycleTransactionLock::acquire_shared returns None and probe_lifecycle deliberately proceeds on evidence alone (lifecycle.rs:486-507, 834-837). Therefore probes do not always take the shared lock when it exists as stated here, and a status/doctor invocation can observe an intermediate lifecycle state while a mutation remains in progress. Specify the bounded evidence-only fallback, or require the probe to retain serialization for the full observation.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Confirmed. The wording did read as unconditional serialization, and the retry bound you cite is exact.

Verified: instance.rs defines LOCK_RETRY_ATTEMPTS = 4 and LOCK_RETRY_DELAY = 25ms, and flock_bounded returns Ok(false) when a holder outlasts every attempt — its own doc comment says the caller decides, "a mutator reports AlreadyRunning, a probe degrades to evidence-only". LifecycleTransactionLock::acquire_shared maps that to Ok(None), and probe_lifecycle discards it with let _root = ...? before continuing into the bounded reread loop.

Fixed in fbc5204, specifying the bounded fallback rather than requiring the probe to hold the lock for the full observation. The bullet now states that shared acquisition is bounded and not awaited — four attempts 25 ms apart — that a probe which still finds a mutator holding the lock samples on evidence alone, that it therefore does not always exclude an in-flight mutation and may observe an intermediate lifecycle state, and that the bounded reread loop rather than the lock is what makes such a sample coherent. Lock absence is named as degrading the same way, since a probe never creates the coordination root.

ahrav added 2 commits August 29, 2026 22:01
The base branch advanced past this branch's point and rewrote
.beads/issues.jsonl, which conflicted with the export committed here.

Resolve the conflict in favor of the base branch's export. That file is the
current one: it carries 223 records against this branch's 196, loses no
issue id, and moves no updated_at backwards. The export committed on this
branch had rewound six records relative to the branch point -- ymc.3 from
closed back to open with its closed_at, close_reason, started_at, and
assignee dropped; ymc.8, ymc.10, and ymc.12 from in_progress back to open;
a52 to a stale revision that predates its supersede decision; and c50.4 to
a stale dependent_count -- so the committed snapshot presented finished and
claimed work as available again.

Taking the base branch's export removes this branch's task-tracking diff
entirely, so the change now touches only documentation, CI, and .gitignore.

The base export omits spec_id on ymc.8, ymc.10, and ymc.12. That field is
absent from all 241 records in the live Beads database, so it is stale
export metadata rather than live state, and reintroducing it would commit
data the source of truth does not hold.
Two statements described behavior the implementation does not have.

The bundle doc claimed macOS serves the host-only lane and reports exact
readiness `unsupported` / `synapse_unsupported`. The Synapse component does
carry that readiness on macOS, but no client can observe it, because
`ck-mc-host serve` composes Synapse alongside Broca, Broca refuses to
initialize off Linux (its crash-ownership records and sweeps read `/proc`
process identity), and the composite requires every child initializer to
succeed. A macOS daemon therefore exits before publication. Describe that,
and name the precondition for serving the lane there: a Broca component
that initializes off Linux as unavailable rather than failing.

The wire protocol claimed probes take `transaction.lock` shared whenever it
exists, which reads as unconditional serialization against a mutator.
Shared acquisition is bounded to four attempts 25 ms apart and then samples
on evidence alone, so a probe may observe an intermediate lifecycle state
while a mutation is in flight. State the bound and name the bounded reread
loop, not the lock, as what makes such a sample coherent.
@ahrav

ahrav commented Aug 29, 2026

Copy link
Copy Markdown
Owner Author

Review round resolved

All ten inline comments answered individually. Summary, with the verification behind each verdict:

Fixed

  • §7.6 heading and the §7.4 section pointer for host.status (ac75372).
  • The unavailable fallback reason (ac75372). Both @claude and @chatgpt-codex-connector were right, and the doc — not the implementation — was wrong: the client re-upgrade probe still exists in connection.rs, which distinguishes PreflightEligibility::DynamicallyUnavailable from permanent absence and states that unavailable outranks capability_version_mismatch because it alone authorizes the probe. §7.7.3 now restores the dynamic/permanent split and the probe rule.
  • The macOS Synapse lane (fbc5204). The bundle doc advertised a readiness state no client can observe: serve.rs builds BrocaComponent as an ungated composite child, Broca refuses to initialize off Linux, and the composite's try_join! requires every child to succeed, so a macOS daemon exits before publication. The doc now says so and names the precondition for changing it.
  • The probe's transaction.lock contract (fbc5204). Shared acquisition is bounded to four attempts 25 ms apart (LOCK_RETRY_ATTEMPTS, LOCK_RETRY_DELAY) and then samples on evidence alone, so the doc no longer implies unconditional serialization against a mutator.

Task export rewind — resolved at the source

Verified against the live Beads database, not either committed export, and the finding was correct. bd show magic-context-ymc.3 reports CLOSED with its closed_at, close_reason, started_at, and assignee intact, contradicting the export committed here. A full comparison against the branch point found the five named records plus one more: magic-context-c50.4 had a stale dependent_count (3 → 2) at an unchanged updated_at.

The base branch had independently rewritten the same file, which made this a merge conflict as well. Resolved in f8968ae by taking the base branch's export — 223 records against this branch's 196, no id lost, no updated_at moved backwards. This branch no longer carries a .beads/issues.jsonl diff; its net change is six documentation, CI, and ignore files. The pull request is MERGEABLE again.

Not reproduced

The macOS lifecycle-coverage finding reads a revision older than the branch point. ci.yml has a mc-host-lifecycle job with matrix [ubuntu-latest, macos-latest] and an explicit macOS step running cargo build -p mc-module --bin ck-mc-host and cargo test -p mc-module --test lifecycle_cli. This branch's only ci.yml change is six additive lines pinning dtolnay/rust-toolchain@stable.

Out of scope, still open upstream

  • The missing .github/workflows/mc-host-release-qualification.yml is real and still true at the base tip. scripts/verify-mc-host-release-evidence.ts is not in this branch — it arrived on the base branch in 8d7c12d — so it belongs to the release-qualification work. The verifier does fail closed on it (existsSync check, named error) rather than accepting unqualified evidence.
  • The merge_selection finding was addressed by d2e9a11, this branch's own branch point: merge_selection layers supplied descriptors over previous.clone(), and the rejection is gated on require_previous_credentials.

Local gates: cargo fmt --check and cargo check -p mc-module --bins pass; bd import --dry-run validates the export. bun is unavailable in this environment, so the TypeScript tier is covered by CI only.

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

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

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread docs/synapse-model-bundle.md Outdated
configuration pins. A nominally matching library with a different hash is
refused by design.

The qualified Linux lane uses the pinned

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 Remove the premature Linux qualification claim

At this revision, release/mc-host-production-inputs.lock.json explicitly has production_qualified: false because the offline oracle has not been recorded against the locked model/runtime bytes, and the Linux entry in release/mc-host-payload-index.json is likewise unqualified and unpublished. Calling this the “qualified Linux lane” therefore makes the normative bundle documentation assert the opposite of the release gate; describe it as the pinned candidate until the qualification evidence exists and the gate passes.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Confirmed, and the wording was mine — this branch introduced both that sentence and the oracle claim below it.

Verified against the gate artifacts you cite:

  • release/mc-host-production-inputs.lock.json: production_qualified: false, with unqualified carrying oracle: offline semantic-oracle evidence not yet recorded against real locked ORT/model bytes.
  • release/mc-host-payload-index.json: the linux-x64-gnu entry is qualified: false, published: false (as are both darwin targets).

So the doc asserted the opposite of the gate on two counts, not one: "the qualified Linux lane", and "The offline oracle passes with networking disabled on glibc 2.28" — that second sentence names precisely the evidence the lock file says is unrecorded.

Fixed in d31444d, taking your framing. The lane is now "the pinned Linux lane", the oracle "runs" rather than passes, qualified manifest in the §5 bullet became pinned manifest, and a new paragraph states that the pins define the candidate the gate evaluates and points at both artifacts as the authority for the verdict.

One deliberate choice: the new paragraph names the gate artifacts and fields rather than restating their present values. Quoting production_qualified: false here would make the doc wrong again the moment the oracle evidence lands, whereas pointing at the gate stays correct in both directions.

| V52 | First post-auth request is application traffic or another control operation | Host retires setup generation; zero application dispatch and no TCP continuation |
| V53 | Negotiation receives `unsupported_operation`, `connection_in_use`, malformed response, version mismatch, or unoffered selection | Client retires generation; no application request continues on TCP |
| V54 | Optional candidate unavailable or capability version mismatched | Valid negotiation explicitly selects offered TCP with the matching fallback reason; generation may continue |
| V55 | Authenticated `host.status` during post-publication activation | One route-free response reports separate closed component states; no routed application body is sent, handler detail is omitted, and starting storage refreshes until ready or unavailable |

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 Seed component status before advertising V55

For a client that authenticates immediately after publication, HostShared.health_snapshot is initialized with an empty components object and the listener is already published before the asynchronously spawned health task completes its first callback. Such a host.status request can therefore omit all component states and epochs, contrary to this activation vector; managed-policy.ts performs the compatibility status read once and treats those missing epochs as incompatible_epochs, so a fresh compatible daemon can transiently fail its managed lifecycle gate. Seed the initial closed component snapshot before accepting connections, or make the compatibility path wait for the first completed snapshot.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Confirmed as a real defect, with one disagreement about where the fix belongs.

Verified, and your reading of the ordering is exact:

  • runtime.rs:841-845 initializes health_snapshot as HealthReport { status: Degraded, detail: None, metrics: Some(json!({"components": {}})) } — an empty component map.
  • runtime.rs:798-812 binds the listener and calls .publish(port, &config.daemon_ver) inside setup.
  • runtime.rs:884-886 then runs spawn_activation_task, spawn_health_task, accept_loop — so publication strictly precedes the health task.
  • runtime.rs:1072 is the only writer of health_snapshot, and it runs after the first probe completes.

And the consequence is as you describe: compatibility.ts evaluateEpochCompatibility returns incompatible_epochs with detail epoch <name> is missing or nonnumeric when a value is absent, so a compatible fresh daemon can transiently fail its managed gate with a remediation of align_versions — misleading advice for a daemon that is merely young.

Where I differ: I am not changing V55, because it is not a description. §14 opens with "Every scenario has one required outcome. These are review vectors" — the V-table is normative. V55 requiring separate closed component states is the correct contract, and it is exactly the gate that catches this ordering bug. Weakening it to "may report an empty component map" would delete the requirement that makes the defect visible, and leave managed-policy.ts consuming a contract that permits the failure it trips over.

So the doc stands and the code is non-conforming. Your first option is the right one: seed the closed component snapshot before accepting connections, which satisfies V55 without making the compatibility path wait. That change belongs in crates/mc-host/src/runtime.rs on the lifecycle branch — it is not in this branch, which touches only documentation, CI, and .gitignore.

- `transaction.lock` carries the exclusive flock serializing mutating lifecycle transactions (launcher start/stop, owned by downstream packaging work); probes take it shared, without creating anything, when it exists. Shared acquisition is bounded, not awaited: four attempts 25 ms apart, after which a probe that still finds a mutator holding the lock samples on evidence alone. A probe therefore does not always exclude an in-flight mutation, and may observe an intermediate lifecycle state; the bounded reread loop, not the lock, is what makes such a sample coherent. Lock absence degrades the same way, since a probe never creates the coordination root.
- `lifetime.lock` carries the daemon's whole-incarnation exclusive flock, acquired before the runtime-directory instance lock and held through publication cleanup, component shutdown, and callback reaping.

Neither the runtime-directory inode nor a lock on the managed `${dataDir}/cortexkit/lifecycle` directory prevents replacement-induced overlap on its own: both live inside the replaceable managed subtree, so renaming `run`, `lifecycle`, or the whole `cortexkit` tree would let a successor anchor fresh inodes under the same names. Because the coordination files sit outside that subtree and are never renamed, replacing the managed subtree cannot split either lock: a mutator still contends on the same `transaction.lock` inode, and a successor daemon still blocks on the same `lifetime.lock` inode until the displaced incarnation fully tears down. The runtime-directory instance lock remains as the descriptor-relative publication/cleanup fence. A mutator holding `transaction.lock` must additionally anchor named-namespace mutations to retained `cortexkit`/child descriptors and abort its named-namespace result when parent or child identity drifts.

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 Anchor the stop command's selection cleanup

When the managed cortexkit subtree is renamed or replaced while daemon stop is in flight, cmd_stop is the one lifecycle mutator that never captures or verifies a NamespaceAnchor; after shutdown it calls clear_active_selection(), which resolves the canonical path afresh and can therefore delete active.json from the replacement namespace and still report a committed stop. This contradicts the new requirement here and can erase a successor owner's harness selection, so stop must retain and verify the namespace identity around its named cleanup just as start and restart do.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Confirmed, and the same reasoning as the V55 thread applies: the requirement is right and cmd_stop is the non-conforming party.

Verified:

  • ck-mc-host.rs:1183 cmd_stop acquires LifecycleTransactionLock::acquire_exclusive and then never captures a NamespaceAnchor. The only two captures in the file are at :1007 (cmd_start) and :1285 (restart).
  • It calls serve::clear_active_selection() at :1212 and :1241.
  • clear_active_selection (serve.rs:684) resolves the path afresh — runtime_dir_path(None), up two parents, then join("cortexkit").join("mc-host-harness-closures") — with no identity check, so it deletes active.json from whatever namespace currently answers to those names.
  • For contrast, start_phase takes anchor: &NamespaceAnchor and calls anchor.verify() before the spawn commit, failing closed on drift.

So a rename or replacement of the managed subtree while stop is in flight can erase a successor owner's selection and still report a committed stop, which is what the sentence at line 160 forbids. I am keeping that sentence: it states the correct rule, and softening it to match cmd_stop would retract the invariant that start and restart already honor.

I am not landing the code fix in this branch, and not only for scope reasons. crates/mc-module/src/bin/ck-mc-host.rs is not part of this change, and the fix needs a design decision this branch should not make unilaterally: what a drifted stop reports. start_phase answers drift with wedged, but cmd_stop carries an explicit comment rejecting wedged for a genuinely stopped host, because it "would route remediation at a daemon process that is not there". Picking the reason means picking from the closed vocabulary in the release contract that managed-policy.ts consumes, and this environment has no bun, so I cannot exercise the TypeScript side of that contract. That belongs with the lifecycle branch that owns both halves.

The bundle doc called this the qualified Linux lane and stated that the
offline oracle passes. The release gate says otherwise:
release/mc-host-production-inputs.lock.json carries
`production_qualified: false` with the blocking reason "offline
semantic-oracle evidence not yet recorded against real locked ORT/model
bytes", and the `linux-x64-gnu` entry in release/mc-host-payload-index.json
is `qualified: false, published: false`.

Describe the pins as the candidate the gate evaluates, say that the oracle
runs rather than that it passes, and point at the two gate artifacts as the
authority for the qualification verdict. Naming the artifacts instead of
restating their current values keeps the doc correct as the gate advances.
@ahrav
ahrav merged commit c1dc56f into feat/mc-host-daemon-lifecycle Aug 29, 2026
18 of 21 checks passed
@ahrav
ahrav deleted the stack/mc-host-19-docs-tracking branch August 29, 2026 22:21
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