# Agent readiness audit Tracking: [#73](https://github.com/cloga/dsh-github-copilot/issues/73). Audited baseline: `d9d0954` (2026-09-05). This is an evidence inventory, not a blanket certification of live Copilot behavior. ## Scope and method Read the instruction entrypoints, source ownership boundaries, test/TypeScript configuration, tooling, packaging, CI/release workflows and public Remote contracts. Two independent native DSH reviewers examined verification/release and runtime/diagnostics. All reproduction uses synthetic fixtures. No production credential inspection, sign-in, sign-out, model/search calls, profile installation or DSH restart is part of this audit. The existing project already has useful architecture boundaries, strict Remote codecs, exact Core pins, credential normalization, broad unit tests, bilingual docs and an immutable-release policy. The gaps were mostly between what an agent could observe and what it was allowed to claim. ## Improvements with executable evidence | Area | Audit evidence at baseline | Change / acceptance check | |---|---|---| | Onboarding/discovery | Commands scattered across prose; no structured task-to-files map | `agent-contract.json`, `scripts/agent.mjs describe/plan`, `verify:agent` check real paths and scripts | | Safe preflight | `status()` and `inspectGitHubCopilotProviderProfile()` sound read-only but reconcile settings | Separate repository-only `agent:doctor`; output lists `notChecked`, actionable checks and stable exit codes; Node tooling tests cover missing dependencies | | Attribution | AGENTS/CONTRIBUTING required unspecified co-author; historical bot was copied | Actual-tool `Assisted-by`; DSH example does not invent a bot email; contract validator guards against mandatory copied co-author policy | | Runtime floor | Declared Node >=22.0.0 but locked pi-ai requires >=22.19.0 | Align package/baseline to >=22.19.0; recommend Node 24 LTS; CI still uses 24, so the minimum is a dependency floor, not a live Node22 certification | | Compiler evidence | `tsconfig.json` included only src; expanded compilation found 17 diagnostics in tests/configs | `typecheck:tests`, accurate mock/brand/narrowing fixes; no blanket ignores; real Core fixture remains checked only in its pinned checkout | | Vacuous assertion | Cordis DisposableList was indexed as an array, allowing undefined UID comparisons | Use iterable conversion and assert defined identity before equality in loader tests | | Build evidence | verify:package checked Host existence, then claimed built Host verified | Import real built Host without the Vitest Typert alias; assert exported contract and label the evidence as import-only | | Archive delivery | Pack/checksum did not check actual exported files or README media; images were omitted | `verify:tarball` inspects bounded tar data without extraction; rejects paths/links/duplicates/unexpected files/trailing payload; compares the full normalized manifest and every packed file to the checkout/build; checks exports and README media; CI/release run it after packing | | Desktop shared-package ownership | Alpha.18-alpha.20 tarballs privately declared Host-owned authorization and schema packages, so native Desktop profile validation rejected the graph | Alpha.21 makes both required peers with development-only copies; the packed-manifest gate audits every runtime/peer declaration against hash-pinned 0.1.5 actual descriptor and 0.1.6 generated package-set projections | | Client external ownership | Alpha.21 left React as a required nonshared Node peer, so packaged Desktop with `autoInstallPeers: false` stopped before receipts/state | Alpha.22 declares `dsh.client.external: [\"react\"]`, removes the root React peer, retains a dev copy, and verifies the actual built Client requests exactly the shell-owned React singleton | | External verifier safety | Fixed fixture path could overwrite then delete a pre-existing upstream file | Exclusive create, pinned clean tracked sources, physical-path containment, identity/byte-guarded cleanup and bounded child; twelve synthetic failure/sentinel tests including a Windows junction | | Release prerequisite | Tag workflow could run independently of complete platform/Core matrix | Reuse CI through workflow_call; publish job needs matrix success on tag revision; contract verifier checks the dependency | | Session model correctness | Inline preflight checked provider but plan sends default-route candidate.model | Historical guard matched provider AND model and delegated mismatches; alpha.8 replaces default-derived planning with initiating-owner/request identity (see #99 below) | | Documentation freshness | Contribution/PR/bug forms omitted alpha.1; build/activation/install evidence blurred | Updated templates, exact approval boundaries, evidence matrix and retry/rollback guidance | | Workspace hygiene | Broad lib ignore hid scripts/lib; root packs/.env not ignored | Root-anchor generated /lib, ignore tarballs/secrets; .editorconfig specifies new-file formatting without rewriting history | Run `pnpm verify` for the local checks. Then `pnpm pack --pack-destination artifacts` and `pnpm verify:tarball -- artifacts/dsh-github-copilot-.tgz`. The task planner emits the actual path from package.json. Consult the PR's final results for commands actually executed and current CI status; this document does not hard-code a perpetual passing test count. ## Evidence ladder 1. **Source markers** prove required seams/test names are present, not semantics. 2. **Typechecks and synthetic tests** verify local assumptions, not installed account eligibility. 3. **Built import/loader checks** prove modules load without test aliases; they do not call apply(). 4. **Pinned upstream config fixtures** prove configuration against those sources, not the entire companion Host on every runtime. 5. **Archive/checksum inspection** proves shipped bytes and packaging consistency, not authenticated requests. 6. **Explicit live acceptance** must separately check installed versus loaded version, actual session model, normal stream/tools, and selected search provider. A page returning HTTP 200 proves only reachability. Missing evidence is `notChecked`, not success. Repository CLI plans do not grant permission to merge, publish, change credentials or install. ## Runtime lifecycle follow-up (#75) - **Read-only status delivered:** `status()` and `describeGitHubCopilotProviderProfile()` now share pure planning and return minimal authentication/catalog/route state without mutation, OAuth refresh or network. Explicit `reconcile()` and its UI control perform stored-snapshot repair. Errors preserve configured authentication and expose classified route diagnostics. Tests prove repeated reads are write/network-free, repair is explicit, and conflicts/failures do not leak raw errors. - **Proof invalidation delivered:** both search surfaces listen to the exact Copilot credential key and evict successful/failed plans lazily. Attach/settings/events make no network requests. Cancellation and generation-bound candidates prevent stale in-flight proof, account replacement during final auth, fallback-spelling clones and delayed streams from using another generation's authorization. Disposal aborts pending probes and prevents further spelling attempts. Tests use actual synthetic grant changes, not only event emission. - **Ownership checks delivered:** raw settings preimages/postimages, validated version-2 journal, revision-checked writes, exact created-profile shape checks, and leaf-only header cleanup protect user additions/edits. Resolved schema defaults do not count as user edits. Tests cover interrupted writes, manual edits, marker loss, legacy markers, process restart and same-process registration resets. - **Deliberate recovery limit:** Core does not expose durable registration identity or a cross-namespace transaction. Revision integers/recorded process epochs cannot authorize replay after a namespace reload. Therefore a later invocation never reapplies an unresolved prepared activation/restoration; it keeps the journal and reports conflict for review. Steady postimages may prepare fresh restoration; already-restored targets may clear a marker without rewriting the route. Legacy journals and custom model preimages also fail conservatively rather than guessing ownership. Post-write marker checks detect interference but do not create atomic route/marker/credential commits. ## Provider-integrated account controls follow-up (#93) The provider integration introduced in `0.4.0-alpha.5` replaces the older independent-footer-only layout, not the underlying route architecture. The lifecycle from `0.4.0-alpha.6` (#95) remains unchanged in planned `0.4.0-alpha.7`; #97 adds only last-success timestamp presentation: - **Surface ownership:** when an existing configured canonical `github-copilot` provider card is mounted, embed login/status/**Refresh models**/**Manage** there and suppress the separate footer account controller. If no such row is mounted, keep footer fallback usable; old Core retains its settings section. The shared account-state owner, including its in-flight authorization state, survives transfer only while another eligible surface remains mounted. Last-surface unmount or nonoverlapping declaration replacement stops polling. A later controller reads status and separately ensures missing/idle/stale/error/loading signed-in metadata without replaying the old forced-login action. Manual **Refresh models** now lives in **Manage**; errors retain a visible **Retry**. - **Discovery triggers (#95):** successful explicit UI **Start sign-in**/account switch forces one bounded discovery after immediate or polled completion. Opening Models separately calls non-forcing `ensureModels()` once for missing/idle/stale/error/loading signed-in metadata; error re-entry retries only after shared cooldown with no same-mount loop; loading joins the existing Host flight to observe completion without extra network. Fresh ready cache needs no request, and true unavailable/empty models do not auto-retry. Status/details remain network-free, not the entire opening flow. Background credential/reset events clear Client state and read status, not force discovery on every token event; next open/use ensures metadata. - **Freshness and authority (#95):** `github-copilot` settings default to `accountModelTtlMs: 86400000` (24h maximum reuse) and `accountModelFailureCooldownMs: 300000` (5min failure cooldown). One shared Host flight serves callers; no periodic metadata polling timer (the timestamp's display-only timer is separate). Last same-account metadata may display during TTL refresh/loading/error but never authorize requests. Credential/account/permission invalidation or proof expiry revokes request evidence immediately; the TTL does not extend tokens. Definitive `UNKNOWN_MODEL` triggers one bounded metadata refresh without message replay/model switching; generic HTTP/network errors are not guessed into that category. - **Last-success display (#97):** `snapshot.discoveredAt` appears beside the model count outside **Manage**, without a duplicate inside. Past times use English relative text; the tooltip and accessible semantic `time` expose the full local date, time and time zone. Missing/invalid timestamps or no account hide it; future times use an absolute label. One mounted 60-second display-only timer updates the relative text without RPC/status/discovery, and is disposed when the timestamp disappears or the view unmounts. Pending/failed refresh keeps the last success time; successful refresh replaces it and sign-out clears it. Cache/discovery and authorization rules are unchanged. - **Honest integration limit:** the published provider-card slot is additive and cannot replace Core **Edit/Delete**. Normal plugin discovery needs no manual model definitions, but the native editor stays available. Embedding account controls does not merge/remove canonical `github-copilot` and managed `github-copilot-preview`, copy credentials, rewrite configuration/history or migrate selection. Any optional real route removal still requires the [separate reviewed migration](./single-route-migration.md). - **Acceptance still required:** test missing/idle/stale/error/loading open-time ensure versus fresh-cache no-request, shared Host flight, TTL/cooldown boundaries, display-only stale data, immediate revocation, definitive unknown-model refresh without replay, Manage-only manual refresh and visible Retry. Retain provider/footer/old-Core fallback, overlapping-owner transfer, final-unmount cleanup and once-per-explicit-login tests. Read the implementation PR's actual results before claiming these checks passed; documentation is not runtime or live OAuth evidence. - **Browser/media evidence:** primary `copilot-model-freshness.png` and `copilot-model-refreshing.png` now show the actual alpha.7 built Client in isolated Edge with synthetic Remote/provider-shell fixtures. The timestamp check advanced a fake clock from 8 to 10 minutes with zero additional RPC, retained the prior time during pending/error, replaced it after successful refresh, cleared it and disposed the timer on sign-out, and verified a safe 375 px layout. Host TTL/cooldown timing is unit-test evidence, not screenshot evidence; no live Core or production authorization is claimed. Provider-entry/authorization PNGs remain historical alpha.5; older compact-account GIF/PNGs remain alpha.3. ## Session-scoped Copilot follow-up (#99, planned alpha.8) - **V3 ownership:** one global Host account and shared metadata source supply many models; explicitly selected/history-backed Sessions retain independent model context. Search facts use the captured initiating Session's effective request-header/config or explicit `GenerateOptions`, not another Session or future default C. Plans are cached per owner to prevent A/B different-model reuse/cancellation. No global current-model/search-status card is added. - **Cold entrypoints:** Chat picker and `/model` `listModels()` ensure the shared managed source without visiting Settings first. Actual cold managed search performs a non-forcing shared ensure before deriving facts. The same 24h maximum TTL, 5min cooldown, account generation and capability/probe checks remain. - **Native API limits:** a new Copilot draft warns that saving a native profile adds another real group, not another account. Add cannot be vetoed and Save cannot be disabled through the public additive slot. Public `session.selectModel` also writes the future global default; unselected empty Sessions can inherit it. Do not claim enforced one-route registration or immutable empty Sessions. - **Delivery boundary:** code does not auto-migrate configuration, credentials, selections or history. After release, the operator must resolve approved Session/default choices separately; the config-only v1 Ops command performs no automated selection writes, then may compare-and-swap only the reviewed user-native profile path after ruling out base/journal conflicts, and read back registration. Any search allowlist change to `github-copilot-preview` is a separate reviewed Ops edit, never silent broadening. See [migration](./single-route-migration.md). - **Request-context correction:** `Agent.options` remains Core's activation seed, not selected-model evidence. Real `installModelSelection` overrides request/assembly; the Session records effective `requestHeader().config` before tools. Capture the initiating Session's proven request context or use explicit `GenerateOptions`; otherwise traditional search is unavailable. Pending new selection before its next request must not expose the prior header as current prompt guidance. - **Acceptance/evidence:** use real `installModelSelection` to check concurrent A/B models against unchanged activation/default seed C, reject unproven or stale-header prompt guidance after a pending selection, cold Chat and `/model` discovery, cold search/probe, owner invalidation, missing initiator, Add warning with Save retained, and CAS-conflict refusal/readback. The retained alpha.7 screenshots illustrate timestamps, not these new behaviors or a completed migration; consult the implementation/ops results separately. ## Live migration evidence follow-up (#101, planned alpha.9) - **Entry point:** no-argument `githubCopilot.migrationStatus()` reads public live leaves synchronously. Generic `session/list` may be stale and inventory lacks loaded-version evidence. The new result carries loaded build `plugin.name`/`plugin.version`, protocol 1, observation time, five structural capability flags and `complete.sessions/defaultSelection/routes`; unknown/incomplete required selection or route evidence must fail the maintenance gate, not be treated as absence. Idle Agents normally have `activeRequestSelection: null`. - **Session scope:** pending model projection → recorded request-header config → current default only for genuinely empty live Sessions with known projection state. Each row has selection provenance; a running Agent's separate `activeRequestSelection` is its latest recorded header, not proof of an in-flight LLM call. `routes.nativeConfigured`, `nativeRegistered` and `managedRegistered` distinguish effective config from registry state. - **Read-only boundary:** no authorization status/discovery, credential access, network, settings/Session mutations, normal UI or global model/search card. Seven ordinary authorization Remotes retain their existing codec; the eighth Remote uses its own strict `GitHubCopilotMigrationStatus` codec. Source ownership is `src/migration-status.ts` plus the controller/Remote bridge; focused evidence is `tests/migration-status.spec.ts`. - **Limits and Ops handoff:** `historyScope: live-agents-only` excludes cold history. Require operator acknowledgement that older conversations may need explicit selection on resume. Version/capability self-reports are not full Desktop/Core byte attestation or an atomic cross-namespace guarantee; recheck just before CAS. The separate planned Ops `tools/migrate-copilot-managed-route.ps1` in `cloga/dsh-windows-ops` is config-only v1, with no automatic Session/default writes, plugin install, restart or full Desktop-baseline acceptance. This document does not establish its publication, installation or a completed live migration. - **Acceptance:** verify loaded identity, missing/malformed seams and incomplete evidence, fresh live-Agent enumeration, pending-versus-recorded selections, truly empty defaults, running-header caveats, effective-config/registry distinction, bounded strict codec and zero side effects. Read actual test/Ops results rather than treating this checklist as a passing report. ## Core alpha2 compatibility follow-up (#105, planned alpha.11) - **Exact targets:** retain rc.2, rc.1 development dependencies, `0.1.3-alpha.1` (`d347e703908d0406b7a7ef80e3a0e594d86b2215`) and `0.1.5-alpha.1` (`5dda764ed3aa172535a7967b06ff95d9cbfe536a`); add current `0.1.5-alpha.2` (`b2e3b2a0125854567a4a5fcba75782e42fe84901`), `0.1.5-rc.1` (`183f08e9c6dde7e36cd2318eaee70b0da08fb35e`) and `0.1.5-rc.2` (`fb2c4b9e698e30edb738bca4cf0618587db7d203`). Peer admission is not full runtime proof. - **Actual break and narrow fix:** alpha2 `ResolvedPiAiProviderProfile` adds required `modelErrors`, optional `piProvider` and optional `catalogError`. `PiAiAdapter.modelOf` unconditionally calls `profile.modelErrors.get(model)`. The plugin-owned profile now supplies an empty `Map` because only already validated account models enter its provider. It retains an actual `piProvider`, so no fabricated `catalogError` is needed. Unknown/rejected models remain fail-closed. No upstream provider, prototype, model catalog or dependency artifact is patched. - **Runtime regression:** the existing unchanged tagged-source resolver attests exact Git HEAD, clean tracked sources, public package/export paths and real Context/LlmRuntime/PiAiAdapter class identity. `preview-route.spec.ts` resolves, prepares and synthetically streams an account model through the actual selected adapter, not a stub; the same suite runs against all three alpha pins and the rc.1 development adapter. Session/projection and eight strict Remote fixtures run on both 0.1.5 alphas. File projection and opaque replay remain native-owned. - **Other consumed seams audited:** alpha1-to-alpha2 settings mutation/CAS and section installation, credential/authorization APIs, Agent/default-model selection, Session request header and model-selection projection, public gateway, provider-card slot props and Chat assistant-node registration retain the consumed contract. Core adds optional provider-directory `error`, repair UI and Add gating; these do not authorize plugin route migration or alter the account-controller ownership. SessionController file reveal/desktop APIs and API-remotes feedback contributions are additive and unconsumed. Chat file-link closing gains a session argument, but this plugin delegates native rendering and does not call that seam. - **In-band system authority:** the broader 0.1.5 audit found newer system-role messages would be demoted by the legacy Anthropic inline serializer. The preflight now delegates any such Anthropic request unchanged to Core before auth/probe/wire; leading and nonleading positions are covered. This deliberately limits inline hosted search, does not rewrite histories, and leaves the legacy Responses inline mapping of system content to user input text unchanged (not filtering). Managed requests always delegate to native transport. - **Evidence boundary:** CI extends the Windows/Linux pinned matrix and release rechecks the current tagged runtime before packing. Local tests use synthetic credentials/network and unchanged Core source fixtures only; no Core implementation changes/build, live install, OAuth, provider calls, Desktop restart or GitHub publication is implied. Consult the task's exact command results for passes/skips; documentation and static markers alone cannot certify runtime or live behavior. ## DSH 0.1.6 compatibility adaptation (#125, planned alpha.19) - **Exact target:** add official `dsh-v0.1.6-alpha.1` at `0a15e36e7f82b6ed45af6fa9759f29b40dcd965d`, retaining every earlier source pin and the exact `0.1.2-rc.1` development dependency set. The package also declares the same range in `engines.dsh`; neither declaration proves a loaded runtime. - **Agent and Session:** the tagged fixture awaits the 0.1.6 `AgentRegistry.register()` publication boundary and verifies that its asynchronous serial `agent/created` listener has completed before migration evidence reads `agents.list()`, `Session.requestHeader()` and model-selection projections. Production code adds no `eventAt()`, `snapshotEvents()` or `ownEvents()` dependency. - **Unowned service audit:** static source markers pin MCP SDK v2 resource list/template cursors, the `@deepseek-ai/dsh-ptc-runtime` and `@deepseek-ai/dsh-workflow-ptc` names, isolated Node PTC's empty model environment, cancellable asynchronous Sandbox/Shell preparation, consumer-owned optional-plugin startup failures, attachment request-cache separation from normalized objects, and Team task cursor/limit behavior. The plugin imports none of those service packages. - **Tool and attachment behavior:** provider-scoped Copilot filtering removes unavailable `sandbox_permissions` and `justification` fields from the new `run_code` schema as it already does for shell/filesystem tools, while leaving Team pagination untouched. The managed adapter continues to expose only the normalized attachment object's execution-world path; it does not expose DSH request-version cache paths. When the 0.1.6 adapter returns `IMAGE_OFFLOAD_REQUIRED`, the first call remains failed; the exact tagged fixture applies the public durable `image/offload` projection and proves the retry sends mapped placeholder text without image bytes. - **Evidence boundary and blocker:** CI and the release workflow use the unchanged exact tagged source on Windows and Linux. This proves source compatibility and synthetic behavior only. The Draft PR remains blocked on the coordinator's Windows Ops 0.1.5 baseline and must not be merged, installed, or published until explicitly unblocked. ## Remaining limitations and follow-up acceptance criteria - **Initiator compatibility:** planned alpha.8 derives search from the captured initiating Session's effective request-header/config or explicit `GenerateOptions`, never a global default. Without `agents.currentInitiator`, traditional hosted search reports a named unavailable diagnostic. Explicit marked `GenerateOptions` still supports guarded inline routing, possibly uncached when no owner exists. An OAuth credential notification during initial lazy discovery is indistinguishable from external account change via public status: the first search fails closed with `WEB_PROVIDER_UNAVAILABLE` before probe/wire; a later user/driver request may retry, never automatically. Do not claim seamless first-attempt credential refresh. Verify the initiating-context seam on each pinned Core before extending compatibility claims. Capability/probe and route allowlists still gate search; owner isolation is not unrestricted search support. - **Live evidence:** readonly status describes the stored account snapshot, not fresh entitlement or a successful request. A credential update during an in-flight search fails closed; a new explicit request can retry with fresh proof. There is intentionally no automatic retry loop. - **Browser acceptance:** pure React element tests and synthetic screenshots do not establish clipboard permissions, keyboard focus, narrow layouts, pending RPC unmount behavior or a loaded Client update. Add mounted/browser tests with synthetic codes before calling those behaviors fully certified. - **Minimum runtime/full baseline activation:** local and CI Node24 plus source/config checks are not full Node22.19/live-plugin certifications. Run those explicitly before expanding deployment claims. - **Security-sensitive diagnostics:** any suspected credential/session/endpoint exposure requires private triage under SECURITY.md with synthetic-sentinel tests. No real secret exposure was established by this audit; do not publish raw logs or provider bodies as evidence. No attempt is made here to rewrite old commits/tags, auto-enable account models, alter live profile-wide Web provider selection, or introduce a second LLM adapter. The runtime guard can be reverted independently; the tooling and documentation remain useful without installing a plugin release.