Skip to content

feat(runtime-host): constrain managed npm dependency provisioning - #1

Closed
zhiiw wants to merge 557 commits into
codex/managed-dependency-storage-authorityfrom
codex/managed-dependency-producer-boundary
Closed

zhiiw wants to merge 557 commits into
codex/managed-dependency-storage-authorityfrom
codex/managed-dependency-producer-boundary

Conversation

@zhiiw

@zhiiw zhiiw commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Summary

Runs npm dependency provisioning behind a constrained producer process. The producer uses a fixed protocol, hermetic environment, lifecycle-script rejection, bounded diagnostics, process-tree reaping, and a soft observed filesystem budget. Runtime identity remains an input capability supplied by the next slice.

Primary invariant

The producer must return only after its complete process tree and output handles are gone. It may write only to its staging tree, and unsupported runtimes or lockfile semantics fail before publication.

  • Owner: Runtime Host managed npm producer
  • Failure state: stable producer failure; staging remains unpublished and collectable
  • Rollback: remove the producer without affecting the storage artifact authority

Stack

  1. durable dependency artifact authority
  2. This PR: constrained npm producer
  3. bundled npm runtime attestation
  4. cancellable managed admission authority
  5. production managed inspection task + crash/replay proof
中文说明

本切片只实现受约束 npm producer:固定协议、hermetic 环境、拒绝 lifecycle scripts、有界诊断、完整进程树回收,以及明确标注为 soft postcondition 的文件系统预算。

主要不变量:producer 只有在完整进程树和输出句柄都退出后才能返回;只能写 staging;不支持的 runtime 或 lockfile 语义必须在 publication 前失败。

@zhiiw zhiiw changed the title feat(runtime-host): constrain managed npm provisioning feat(runtime-host): gate managed npm provisioning on runtime attestation Aug 8, 2026
@zhiiw
zhiiw force-pushed the codex/managed-dependency-storage-authority branch from b9dfd30 to 708acc1 Compare August 9, 2026 01:33
@zhiiw
zhiiw force-pushed the codex/managed-dependency-producer-boundary branch from aa0692b to b58a6db Compare August 9, 2026 01:33
@zhiiw zhiiw changed the title feat(runtime-host): gate managed npm provisioning on runtime attestation feat(runtime-host): constrain managed npm dependency provisioning Aug 9, 2026
@zhiiw
zhiiw force-pushed the codex/managed-dependency-storage-authority branch from 708acc1 to 6b9943a Compare August 9, 2026 02:59
@zhiiw
zhiiw force-pushed the codex/managed-dependency-producer-boundary branch from b58a6db to 4dfd049 Compare August 9, 2026 02:59
@zhiiw
zhiiw force-pushed the codex/managed-dependency-storage-authority branch from 6b9943a to 0d1f349 Compare August 10, 2026 00:48
@zhiiw
zhiiw force-pushed the codex/managed-dependency-producer-boundary branch 4 times, most recently from 07771db to a3a0333 Compare August 12, 2026 04:47
M4n5ter and others added 7 commits August 15, 2026 21:47
The generic 80% docstring threshold produces warnings that do not reflect this TypeScript and mixed-language repository's documentation standards. Disable only that built-in pre-merge check while leaving the remaining checks unchanged.

Generated-by: Codex
* feat(runtime): add Codex server-side history compaction

Use provider-native opaque checkpoints for Codex subscription models by default
while retaining text checkpoints for other providers. Keep trigger injection
scoped to the dedicated compactor transport and preserve checkpoint identity
across replay, model switches, and rolling compaction.

The provider boundary lowers settled hosted tools into paired wire items and
redacts only the exact compaction payload, avoiding dangling outputs and
collateral telemetry mutation.

Generated-by: Codex

* fix(runtime): bound Codex compaction inputs

Pass the active history budget through the generic compactor contract and keep provider-native requests within it by omitting oversized historical tool payloads without breaking call/result pairs. Fail open before dispatch when the remaining projection cannot fit.

Generated-by: Codex

* refactor(runtime): use AI SDK compaction trigger

The upgraded OpenAI provider exposes compactionTrigger as a request-scoped option, so the dedicated fetch mutation layer is no longer needed.\n\nSet the option only in the Codex history compactor and retain wire-contract coverage that ordinary requests remain unchanged.

Generated-by: Codex
Remove the test-only AgentRun.execute flow so RuntimeKernel remains the sole execution authority.

Preserve projection-failure and tool-mode coverage through production execution paths while deleting duplicate and ineffective test guards.

Fixes apache#3034

Generated-by: Codex
…e#3080)

Remove the unused timestamp-based Session read path so Runtime Host message identity remains the sole read-state mutation authority.

Generated-by: Codex
* fix(desktop): hand terminal replies to durable transcript

* fix(desktop): retry terminal transcript handoff

* test(desktop): await terminal handoff condition

* test(desktop): synchronize conversation remount

* test(desktop): remove remount observer race

* test(desktop): capture remount session before completion

* test(desktop): await stopped run authority

* test(desktop): remount from a fresh observer

* test(desktop): preserve terminal remount boundaries

Generated-by: Codex
…time (apache#3031)

Bound the pre-connection lifetime of ephemeral Runtime Host candidates independently of composition startup and residencies. Keep one timer authority per lifecycle phase, defer the initial deadline under in-flight handshakes with a bounded limit, and cover the boot-residency, hung-startup, and silent-handshake paths with regression tests.

Fixes apache#2943

Generated-by: Claude Code
* fix(runtime): join PTY finalization after a racing persist

writeStdin decided whether to join finalization when persistObservation
was first called. A real PTY can exit while that persist is still in
flight, so the control returned a running snapshot. Re-join if
finalization has started, and wait for a terminal observation in the
queued-cut test instead of assuming the first snapshot is already done.

Fixes apache#3077

Generated-by: Grok

* test(runtime): assert writeStdin is terminal after a delayed persist

Hold the writeStdin persist until PTY finalization has started, then
assert the control result itself is completed. The existing queued-cut
test can still wait for a later snapshot.

Generated-by: Grok
@zhiiw
zhiiw force-pushed the codex/managed-dependency-producer-boundary branch from a3a0333 to 69cd4ec Compare August 16, 2026 03:31
1625567290 and others added 11 commits August 16, 2026 13:28
* fix(desktop): persist rail grouping across restarts

viewMode was a hard-coded useState('conversation'), so the rail grouping
control reset to time-order on every launch. Hydrate and persist it
through the same localStorage seam as the rail's collapsed flag.

Fixes apache#3060

Generated-by: Grok

* test(desktop): keep rail grouping after a renderer reload

Click 按项目, wait for the localStorage write, reload the Electron
renderer, and assert the control is still project-grouped.

Generated-by: Grok
Preserve explicitly selected nested directories as their own Projects while retaining repository identity for Git roots, linked worktrees, and historical session paths.

Make registered project locations authoritative when recording usage, and cover nested registration, selection, relinking, and Session reassignment.

Closes apache#2660

Generated-by: Grok
* refactor(eval): make framework selection single-source

Validate the run_trial.py framework argument and install it before
Harbor, Pier, or the relay import. Stop injecting or reserving
MAKA_EVAL_FRAMEWORK from TypeScript so argv is the only authority.

Fixes apache#3036

Generated-by: Grok

* test(eval): assert launched trials omit MAKA_EVAL_FRAMEWORK

Replace the source-text scan with a harness launch that records the
child environment after the parent sets the unused selector.

Generated-by: Grok
Move the shared TurnOrigin type and decoder into a leaf module and use it from stored-message and RuntimeEvent decoding.

Generated-by: Codex
…track of

设置 · 活动 · 导入任务 shipped in apache#3033 as a relocation and never got a pass
over how it reads. The import behaviour itself is unchanged.

The page kept source ids where it needed records. A bare id is a pointer into
a list the user is free to replace -- filter it, switch source, retry a failed
page -- so everything built on one either went mute or had to be protected by
freezing the list. Both places that held ids now hold `ImportAttempt`
(`{ adapterId, sourceSessionId, name }`).

- The unconfirmed banner names the conversations to go look for, and stays
  true after their rows are gone.
- The in-flight import is named above the list rather than only on its row,
  so no catalog control has to be frozen to keep it identifiable. The freezes
  the archived filter (apache#3033), the source switch and 重试/加载更多 carried are
  deleted, along with the cross-adapter id collision they were closing.
- The page-level lock engages at all: Astryx runs `clickAction` inside a React
  19 async transition, which holds state updates until the action settles, so
  the state committed only after the import was over. Plain `onClick` runs
  outside the transition.
- `加载更多` is a full-width `secondary` button rather than a centred ghost
  label that read as a caption under the list.
- The no-adapter empty state says what to do instead of repeating its title.

Reviewed by M4n5ter; the in-flight-import refactor was raised by CodeRabbit
finding the same freeze rule at a third and fourth control.

Refs apache#2984
* fix(runtime): persist provider failure diagnostics

Provider failures previously collapsed to a generic status before the durable model-call record was written, leaving history-compaction failures unexplained after restart.

Persist a bounded structured failure fingerprint and the selected history-compaction route, while preserving Codex OAuth HTTP facts without storing provider messages, response bodies, or request data. Fail-open behavior remains unchanged.

Generated-by: Codex

* fix(inspector): surface compaction failure diagnostics

Preserve the selected history-compaction route and bounded provider failure facts through the Session Trace projection so persisted failures remain actionable after restart. Show the terminal attempt structured fingerprint in the existing Inspector row without adding a second diagnostic authority.

Generated-by: Codex
… message (apache#3038)

* fix(runtime): group parallel tool calls into one summarizer assistant message

replayPlanItemsToModelMessages emitted every tool_call replay item as its
own assistant message, so a step with parallel tool calls produced a
second assistant message while the first's tool calls were still
unanswered. Strict OpenAI-compatible providers reject that shape (DeepSeek
400: an assistant message with tool_calls must be followed by tool
messages responding to each tool_call_id), so history compaction could
never succeed for sessions on such providers — the provider_error
fail-open loop behind the apache#3029 incident.

Collect a run of tool calls uninterrupted by text or results into one
assistant content array, mirroring the step-merge invariant the primary
replay path gets from the materializer. Skipped thinking items do not
break the run. The primary replay path is untouched.

Fixes apache#3030

Generated-by: Claude Code

* fix(runtime): keep a tool step open across interleaved results

Review follow-up: adjacency-based grouping closed the assistant message at
the first tool_result, so the production-legal interleaved ordering
(call A, call B, result A, call C, result B, result C) still emitted a
second assistant message while call B was unanswered — the same strict
provider rejection this PR set out to fix.

Group by assistant step instead: a step's calls share one assistant
message and its results are buffered to the step boundary. Membership
follows the stamped stepId when both sides carry one; legacy items
without a stepId join while the open step still has unsettled calls,
which is exactly the interleaving case, and a call arriving after the
open step fully settled opens its own message so distinct steps are
never merged.

Generated-by: Claude Code
…#3095)

* refactor(runtime-host): trim dead export surface from barrels

Remove unconsumed server, client, protocol, and adapter barrel exports while moving package-internal consumers to their leaf modules.

Generated-by: Codex

* fix(runtime-host): restore host kernel test formatting

Restore the Biome-approved wrapping for three assert.rejects calls so the PR typecheck workflow can pass.

Generated-by: Codex
Restore pointer interaction for task action menus while their native popover is open, and add focused Electron regression coverage.

Generated-by: Codex
…pache#3464)

app.getFileIcon() was the wrong identity source: macOS reduces the path
to its UTType and returns the generic application icon, never this
app's own. Load the canonical 1024px assets/icon.png (shipped since the
window-icon fix) through desktopAssetPath + nativeImage.createFromPath
instead, resolved lazily at the card payload so non-darwin starts never
pay the decode.

This retires the whole native icon chain: both getFileIcon() calls,
BUNDLE_ICON_OPTIONS, loadNativeBundleIcon() and its packaged-only gate,
the drag-time native fallback, and the unbounded icon await in start().
Dev builds now show the icon too instead of a deliberate blank.

The PNG is now a load-bearing packaged resource, so
assertPackagedResources requires it on current builds, gated off for
the legacy Windows upgrade baseline that predates it.

Implements the follow-up agreed in apache#3455 review. Refs apache#3352.

Generated-by: Claude Code
* fix(desktop): recover from main renderer loss

Observe unexpected termination of the main BrowserWindow renderer while the Electron main process is still alive. Reuse the native diagnostic dialog and existing report formatter so users can copy bounded Desktop evidence before choosing to relaunch or exit, without querying an unrelated Runtime Host.

Generated-by: Codex

* fix(desktop): preserve renderer recovery during initial load

Route every window creation through the quit coordinator so rejected initial loads are reported instead of escaping as unhandled rejections. Test the real render-process-gone observation boundary while preserving the existing native recovery decision flow.

Generated-by: Codex

* refactor(desktop): centralize renderer recovery diagnostics

Route Renderer-loss copying through the existing Desktop diagnostic authority so collection, Host attribution, redaction, and clipboard behavior keep one owner. Align the shutdown regression test with the production register-then-abort lifecycle.

Generated-by: Codex
…#3504)

apache#3495 branched before apache#3397 landed, so its two new files arrived without the
ASF header and its CI predates the `audit` job. `check:asf-headers` runs in
`ci.yml`, so main is currently failing that check and every open PR inherits
the failure.

`npm run write:asf-headers` produced this; nothing else was touched.

Generated-by: Claude Code
…s permission mode (apache#3396)

Session creation no longer sends a client-cached permission mode back to the
Host: ordinary creation omits the field and the Host resolves its own
chatDefaults.permissionMode, while a real per-Session override is sent once
and never writes back to the default. The TUI reads the Host default for its
prospective-mode display instead of hardcoding Auto, and refreshes it across
/new.

Also retires the `execute` permission mode (same boundary as `ask`) behind a
single decodePersistedPermissionMode fold at every durable read boundary, and
bumps RUNTIME_HOST_COMPATIBILITY_EPOCH to 38.

Generated-by: Claude Code
* fix(desktop): stabilize burst follow-up messages

Route consecutive submissions through the Runtime Host queue authority, expose Queue and Steer controls, restore retracted message content, and deduplicate identical active error toasts.

Generated-by: Codex

* fix(desktop): scope queue and toast actions
Chapter 1 still described a file-backed run store and the removed
per-call permission model. Both languages carried the same drift, so
`translation_status: synced` was no longer true.

Corrected against the code:

- `PermissionEngine` and its allow/block/prompt evaluation no longer
  exist; a tool that crosses the sandbox boundary returns
  `sandbox_boundary_required` with a concrete expansion, and the model
  raises the request through `request_sandbox_boundary`.
- The decision is routed by `RuntimeKernel.respondToSandboxBoundary()`,
  not `respondToPermission()`.
- Durable state is SQLite, not `sessions/<id>/runs/<id>/*.jsonl`.
  RuntimeEvents land in `runtime_events`, ordered by `event_seq` under a
  `(invocation_id, event_seq)` uniqueness constraint.
- `maxSteps` is `number | undefined` and `undefined` is unbounded; the
  documented default of 50 appears nowhere in the repository.
- `packages/storage/src/__tests__/agent-run-store.test.ts` no longer
  exists; the ledger repair coverage is
  `packages/runtime/src/__tests__/runtime-ledger-repair.test.ts`.
- Startup recovery and continuation are now stated as the two distinct
  paths they are, naming `safe_boundary_continuation`,
  `continuationSource` and `runAdmittedRuntimeContinuation`.

Also records that `AiSdkFlow` rejects the four legacy permission events
at the mapping boundary, and that the boundary decision fact carries
`role=system` with `author=user`.

No new sections or examples: this change only brings existing statements
back in line with the implementation.

Refs apache#3522

Generated-by: Claude Opus 5 via Claude Code
…3501)

`.git-blame-ignore-revs` documents that a reformat PR adds its landed hash
here in a follow-up right after the squash, because an entry that names a
hash the clone has never seen makes `git blame` error out instead of
degrading. apache#3397 left the placeholder; this fills it in with the squash
commit that landed on main.

The sweep touched 2679 files without changing a line of their content, so
without this entry every one of them blames to that commit.

Generated-by: Claude Code
* refactor(desktop): establish the workbar feature boundary

Generated-by: Codex

* refactor(desktop): isolate workbar IO behind services

Generated-by: Codex

* refactor(desktop): move workbar lifecycle out of AppShell

Generated-by: Codex

* fix(desktop): preserve Workbar dispatch semantics

Generated-by: Codex

* fix(desktop): contain narrow Side Chat composer

Generated-by: Codex

* fix(desktop): retry failed terminal cleanup

Generated-by: Codex
* docs: restyle README header and correct stale facts

Center the latest Desktop icon with the project name, and replace the
mixed CI/license/docs badges with a coordinated star, download, license,
and platform row. Platform badges split support (macOS arm64), unsigned
preview (Windows), and not-yet (Linux) instead of one combined pill.

Also un-drift the body: sandbox boundary instead of permission engine,
align the Chinese Windows sentence with English, replace 外部竞品 with
external subjects, and point the Windows roadmap issue at apache/maka.

* docs: put (Incubating) back in the README title

* docs: plain-language README, drop maintainer dump

Rewrite Why Maka and the capability lists without internal slogans.
Name Computer Use and IM bots as optional/experimental. Collapse the
SQLite backup, Phase 2/3, and vault-mode handbook into a short data
and recovery section with links to SECURITY, privacy, and resume docs.
* feat(desktop): expose computer use activity progress

* chore: format computer use observability changes

* fix(runtime): share tool progress codec
…unavailable (apache#3371) (apache#3373)

The ctx segment only rendered when both the model context window and a
token_usage contextRemaining were present, leaving connections whose
provider does not report per-step input tokens (e.g. opencode-go) with
no context indicator at all — indistinguishable from 'not measured yet'.

When the window is known but contextRemaining is not, render a dim
'ctx ?/<window>' fallback (pi-mono footer style). Unknown-window
behavior is unchanged: no segment.

Generated-by: Maka
…itch input (apache#3475)

* fix(cli): give rewind selections visible feedback and preserve mid-switch input

A rewind selection closed the picker and then stayed silent for the whole
branch-and-switch chain (~6-8 serialized runtime-host round trips), while
control-busy renders nothing in the TUI body (apache#3383):

- push a 正在回退到该轮之前… notice synchronously on selection; the transcript
  replacement wipes it on success and the failure path splices it out so only
  the error remains;
- keep an editor draft typed while the switch is in flight instead of letting
  editor.setText clobber it — the picker's neutral-editor guarantee only holds
  at open time; the rewound prompt stays recoverable from the editor history;
- refuse a selection with an error notice when busy is already held (e.g. a
  Goal auto-continuation started while the picker was open) — matching /goal's
  explicit refusal instead of runControl's silent early return.

Generated-by: Maka

* test(cli): cover rewind feedback, draft preservation, and busy refusal

- an in-progress notice appears while the branch is being created and is gone
  once the branch lands;
- a draft typed while the switch is in flight survives completion (Enter stays
  swallowed by disableSubmit) instead of being overwritten by the refilled
  prompt;
- selecting a target while another action holds busy surfaces the refusal
  notice instead of dropping the rewind silently.

Generated-by: Maka

* fix(cli): record rewound prompts in editor history and respect buffered pastes

Review follow-ups on apache#3475 (apache#3383):

- add the rewound prompt to the editor history unconditionally: prompts
  submitted in this TUI process were already covered (addToHistory dedupes
  consecutive duplicates), but a session entered via startup resume or
  /resume had no entry, so the draft-preserved notice promised a ↑ recovery
  it could not deliver;
- treat a bracketed paste still being buffered as newer user input too:
  between the start and end markers getText() stays empty, so the refill
  check saw an empty editor and the completing paste would have been
  appended to the refilled prompt. The global input listener now tracks
  paste buffering with the same per-chunk marker matching the editor uses,
  and the refill requires no pending paste;
- reword the draft-preserved notice to state exactly what happened (input
  kept, prompt recorded in history).

Tests: ↑ recalls the rewound prompt after a draft-preserving rewind even
with no in-process submission, and a paste split across the rewind
completion lands alone instead of being concatenated onto the refill.

Generated-by: Maka
…e#3364)

* fix: keep relative timestamps on just now for the first minute

Sidebar and other RelativeTime rows were counting seconds under one
minute, which made the suffix flicker. Hold a single just-now label
until the minute boundary, and only refresh then.

Generated-by: Maka

* refactor(core,ui): trim verbose comments and drop brittle test assertions

Generated-by: Maka

* refactor(core): collapse redundant branch in formatCompactTimestamp

Generated-by: Maka

* chore: add the ASF header to the relative-time tests

The new test file is covered by check:asf-headers after apache#3397.

Generated-by: Grok

* fix: bound relative-time refresh age

---------

Co-authored-by: Ubuntu <ubuntu@localhost.localdomain>
…pache#3534)

fix test name finite mismatch - covers Infinity/NaN

Generated-by: Muse Spark

Co-authored-by: Ubuntu <ubuntu@localhost.localdomain>
…from TERM (apache#3537)

PowerShell and cmd.exe set no TERM at all, and the detector returned 0 for an
unset TERM before considering anything else — so the TUI emitted no ANSI at all
on native Windows shells while WSL and Git Bash stayed coloured. The consoles do
support truecolor; only the detection was wrong. The same early return also
swallowed an explicit COLORTERM whenever TERM was unset, on every platform.

The function's own benchmark, `supports-color`, avoids this by returning on
process.platform === 'win32' before it reads TERM. Node's getColorDepth()
already implements that check, so ask it rather than keeping a second copy of
the ladder. NO_COLOR, TERM=dumb and the explicit truecolor upgrade still win,
and piped output — which has no stream to ask — keeps the old TERM ladder.

Generated-by: Claude Code
…3466)

keep the Agent Graph chevron pointing down while the panel is expanded
rotate the chevron only when aria-expanded is false so the collapsed state points up
cover disclosure state, collapsed state, and controlled content synchronization

generated by: Codex
Astryx's Item draws `outline: 2px solid accent` at `:has(:focus-visible)`
unconditionally. That is correct only for a clickable row, where Item
renders an invisible <button>/<a> as a direct child and the row outline is
that button's only focus indicator. Everywhere else the focusable thing is
a real control that rings itself, so the row drew a second outline around
the entire label + description + control band.

Opening the 默认模型 picker showed it at its worst. A Selector popup is a
native `popover`: the top layer moves where it paints, not where it sits in
the DOM, so it stays a descendant of the row. Focus moves to the popup's
search input on open, `:has()` walks up to the row, and the row rings while
the trigger — a sibling of the popup — does not.

A tab sweep over all 16 settings pages in a real window found the same
doubled ring on 通用, 工作区, 记忆, 每日回顾, 权限与能力 and 关于; the ring
is now narrowed to the row's own tab stop, which leaves the clickable rows
on 模型 and 远程接入 (and the SelectableCard on 外观) untouched.

The rule is gated on `forced-colors: none`. Under Windows High Contrast the
premise does not hold: forced-colors drops box-shadow and repaints every
border in one system color, so an Astryx field reads the same focused as
resting, and outline — which forced-colors preserves — is the only
indicator left. There the row keeps its ring.


Generated-by: Claude Code
Claude-Session: https://claude.ai/code/session_01EbmGcSNMLJMVinv5FWit3d

Co-authored-by: Claude <noreply@anthropic.com>
* feat(desktop): manage remote runtime host credentials

Add Host-local credential metadata, safe Desktop credential rotation, and explicit revocation through the existing SSH management plane. Credential mutation remains owned by Runtime Host access authority, while secrets stay out of renderer and terminal projections.

Generated-by: Codex

* fix(runtime-host): harden managed access controls

Advertise credential management through the persisted operator contract so Desktop never exposes actions an older deployment cannot execute. Keep metadata inspection read-only and align framed access operations around one action-discriminated protocol.

Generated-by: Codex

* fix(runtime-host): preserve managed access authority

Derive Desktop credential replacements from the active Host credential and fence access management while pairing recovery is unresolved. Negotiate operator capabilities per invocation so upgraded operators remain compatible with older Desktop clients.

Generated-by: Codex

* fix(runtime-host): make credential rotation atomic

Derive replacement credentials from the active Host-owned authority record and guard rotation and revocation with atomic credential state checks. Reject disabled-profile rotation before remote mutation so the UI cannot leave an unusable pending credential.

Generated-by: Codex

* fix(desktop): bound credential maintenance transitions

Require explicit confirmation before credential rotation reconnects a managed Host, and bound pairing finalization through the dispatched request. Simplify guarded revocation around stable credential identity while preserving recoverable unknown outcomes.

Generated-by: Codex

* fix(runtime-host): make credential revocation converge

Treat an already-absent target as a satisfied revocation while retaining an atomic active-credential fence. Keep Desktop identity validation at the SSH adapter boundary so the Host protocol stays client-agnostic.

Generated-by: Codex

* fix(desktop): clarify credential maintenance behavior

Warn before revocation disconnects clients using an active credential, and exercise rotation recovery through the real profile service path. Reuse the shared UTF-8 truncation boundary for framed access errors.

Generated-by: Codex

* fix(desktop): preserve credential rotation authority

Bind a prepared replacement to the exact managed profile, service, and current credential resolved before SSH begins. Pairing recovery now preserves unreadable journals and reconnects a valid pre-rebind target in the same startup, while revoke confirmation consistently warns about interruption.

Generated-by: Codex

* fix(runtime-host): preserve remote credential compatibility

Keep existing credential operation codecs at epoch 36 and move rotation-only guards into separately named additive operations. This preserves ordinary mixed-version Remote Host connections while keeping credential rotation atomic and Host-authoritative.

Also prevent unfinished pairing recovery from overriding profile lifecycle choices and disable access mutations while the managed service is offline.

* fix(desktop): preserve pairing recovery during uninstall

Persist the managed-service uninstall intent before invoking the remote operator, and retry the idempotent uninstall before cleanup. This keeps credential pairing recovery authoritative and prevents an interrupted uninstall from discarding the binding needed for repair.

Generated-by: Codex

* chore: add ASF headers to credential sources

Apply the repository source-header policy to credential management files introduced by this branch after rebasing onto the new audit gate.

Generated-by: Codex

* fix(desktop): recover interrupted host management

Persist a cleanup-only phase after remote service uninstall so retries never depend on an operator that cleanup may have removed. Bind management dialog state to its Host and use urgent busy updates so stale async results cannot cross profiles.

Generated-by: Codex

* fix(runtime-host): serialize managed deployment cleanup

Route deferred cleanup through the remote service authority so a stale Desktop retry cannot remove a reinstalled Host. Setup and cleanup now share the lifecycle lock, and cleanup refuses to delete while service state remains.

Restore visible management progress and align the compatible protocol declaration with the rebased epoch.

Generated-by: Codex
…e#3543)

* feat(desktop): preserve bounded main-process recovery evidence

Persist a redacted latest-log snapshot at a bounded cadence so the next launch can offer copyable diagnostics when the prior main process did not complete a clean shutdown. Keep the signal intentionally cause-agnostic, local-only, and disposable after the first recovery prompt.

Generated-by: Codex

* fix(desktop): preserve recovery evidence until handled

Treat the active journal file as the sole unclean-run marker, removing it synchronously on clean shutdown. Keep pending evidence when the native recovery dialog cannot be presented so a later launch can retry.

Generated-by: Codex

* fix(desktop): anchor recovery retention to discovery

Start the seven-day retention window when an interrupted run becomes pending, using one metadata update instead of a periodic heartbeat. Keep corrupt high-cardinality records bounded and centralize the present-before-discard policy so failed native prompts remain retryable.

Generated-by: Codex
@zhiiw
zhiiw force-pushed the codex/managed-dependency-producer-boundary branch from 69cd4ec to 2c37025 Compare August 23, 2026 05:24
@zhiiw

zhiiw commented Aug 30, 2026

Copy link
Copy Markdown
Owner Author

Closed as an obsolete extraction/integration slice. The current delivery path is the merged Gitoxide foundation followed by #40 -> #41 -> #42. This PR remains available as design and implementation history.

中文说明

该 PR 属于旧 extraction/integration 栈,现已失去独立合并价值。当前交付路径是已合并的 Gitoxide 基础,然后依次审查 #40#41#42。这里继续保留设计、实现与审查历史。

@zhiiw zhiiw closed this Aug 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.