From ae7f86c6a9630b69d9e66be6c557fdd5d40cafe5 Mon Sep 17 00:00:00 2001
From: "copilot-action[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Sun, 11 Oct 2026 11:00:00 +0200
Subject: [PATCH 01/23] docs(sdd): specify issue contract in
specs/github-runtime-doctor-command.md
---
specs/CATALOG.md | 4 +-
specs/catalog.json | 3 +-
specs/github-runtime-doctor-command.md | 353 +++++++++++++++++++++++++
3 files changed, 357 insertions(+), 3 deletions(-)
create mode 100644 specs/github-runtime-doctor-command.md
diff --git a/specs/CATALOG.md b/specs/CATALOG.md
index 9abaf78f4..9aaa4fafd 100644
--- a/specs/CATALOG.md
+++ b/specs/CATALOG.md
@@ -16,7 +16,7 @@ debt or convert unknown historic intent into a design decision.
| `bugbot-review-state-reconciliation` | Implemented | Reconcile review snapshots, findings, threads, comments, and check conclusions | [Bugbot review-state reconciliation](./bugbot-review-state-reconciliation.md) | 59 paths · 2026-10-11 |
| `execution-lifecycle` | Implemented | Shared GitHub Action lifecycle from event admission through durable user-facing results | [Execution admission, queueing, routing, and result publication](./execution-admission-queue-and-publication.md) + 3 companion | 84 paths · 2026-09-16 |
| `architecture-quality-hardening` | Implemented | Close verified concurrency, error-contract, context-coupling, fan-out, setup/doctor, and provider-policy risks in dependency order | [Architecture quality and scalability hardening](./architecture-quality-and-scalability-hardening.md) + 1 companion | 72 paths · 2026-09-16 |
-| `setup-and-doctor` | Implemented | Plan, validate, provision, and audit a repository installation without exposing credentials | [Setup, configuration, credentials, and doctor](./setup-configuration-credentials-and-doctor.md) + 2 companion | 165 paths · 2026-10-11 |
+| `setup-and-doctor` | Implemented | Plan, validate, provision, and audit a repository installation without exposing credentials | [Setup, configuration, credentials, and doctor](./setup-configuration-credentials-and-doctor.md) + 3 companion | 165 paths · 2026-10-11 |
| `local-web-setup-assistant` | Proposed | Offer a packaged, loopback-only Svelte setup interface over the existing engine with four-language guidance, English CLI help, basic/custom first-run paths, editable question and plan review, source-labelled GitHub facts, assisted CI and Project evidence, role-separated PATs, and truthful itemized outcomes; inspect existing configuration and apply independent, scope-aware runtime adjustments with permission, drift and read-back verification | [Local web setup assistant](./local-web-setup-assistant.md) + 1 companion | 202 paths · 2026-10-09 |
| `guided-bot-pat-onboarding` | Proposed | Guide creation of the persistent workflow PAT, verify bot identity and grants, recover explicitly from wrong-account input within the session, and install the approved Actions Secret | [Guided bot PAT onboarding](./guided-bot-pat-onboarding.md) | 39 paths · 2026-10-07 |
| `temporary-setup-operator-authorization` | Proposed | Guide creation of the one-run operator PAT through GitHub's official form, verify final setup access, and report user-owned deletion accurately | [Assisted setup PAT creation](./temporary-setup-operator-authorization.md) | 34 paths · 2026-10-07 |
@@ -104,7 +104,7 @@ debt or convert unknown historic intent into a design decision.
- Owner: Copilot maintainers
- Last verified: 2026-10-11
-- Specifications: [`specs/setup-configuration-credentials-and-doctor.md`](./setup-configuration-credentials-and-doctor.md) · [`specs/setup-doctor-architecture-hardening.md`](./setup-doctor-architecture-hardening.md) · [`specs/setup-pat-permission-guidance-and-verification.md`](./setup-pat-permission-guidance-and-verification.md)
+- Specifications: [`specs/setup-configuration-credentials-and-doctor.md`](./setup-configuration-credentials-and-doctor.md) · [`specs/setup-doctor-architecture-hardening.md`](./setup-doctor-architecture-hardening.md) · [`specs/setup-pat-permission-guidance-and-verification.md`](./setup-pat-permission-guidance-and-verification.md) · [`specs/github-runtime-doctor-command.md`](./github-runtime-doctor-command.md)
- Workflows: [`setup/workflows/agent-cli-provisioning.yml`](../setup/workflows/agent-cli-provisioning.yml) · [`setup/workflows/copilot_credential_health.yml`](../setup/workflows/copilot_credential_health.yml)
- Entrypoints: [`src/cli/commands/setup.ts`](../src/cli/commands/setup.ts) · [`src/cli/commands/doctor.ts`](../src/cli/commands/doctor.ts)
- Core code: [`src/domain/setup.ts`](../src/domain/setup.ts) · [`src/domain/setup_questionnaire.ts`](../src/domain/setup_questionnaire.ts) · [`src/domain/setup_token_permissions.ts`](../src/domain/setup_token_permissions.ts) · [`src/application/ports/setup_terminal_ports.ts`](../src/application/ports/setup_terminal_ports.ts) · [`src/application/ports/setup_wizard_ports.ts`](../src/application/ports/setup_wizard_ports.ts) · [`src/application/ports/branch_tag_ports.ts`](../src/application/ports/branch_tag_ports.ts) · [`src/application/policies/setup_issue_workflow_policy.ts`](../src/application/policies/setup_issue_workflow_policy.ts) · [`src/data/repository/release/repository_version_tags_query_repository.ts`](../src/data/repository/release/repository_version_tags_query_repository.ts) · [`src/application/ports/setup_token_permission_ports.ts`](../src/application/ports/setup_token_permission_ports.ts) · [`src/application/policies/setup_token_permission_evidence_policy.ts`](../src/application/policies/setup_token_permission_evidence_policy.ts) · [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/policies/setup_questionnaire_policy.ts`](../src/application/policies/setup_questionnaire_policy.ts) · [`src/application/policies/setup_configuration_plan.ts`](../src/application/policies/setup_configuration_plan.ts) · [`src/application/policies/merge_setup_overrides_policy.ts`](../src/application/policies/merge_setup_overrides_policy.ts) · [`src/application/policies/setup_configuration_storage_policy.ts`](../src/application/policies/setup_configuration_storage_policy.ts) · [`src/application/policies/setup_doctor_message_catalog.ts`](../src/application/policies/setup_doctor_message_catalog.ts) · [`src/application/policies/setup_doctor_report_policy.ts`](../src/application/policies/setup_doctor_report_policy.ts) · [`src/application/policies/setup_journey_policy.ts`](../src/application/policies/setup_journey_policy.ts) · [`src/application/policies/setup_permission_summary_policy.ts`](../src/application/policies/setup_permission_summary_policy.ts) · [`src/application/usecases/setup/setup_wizard_use_case.ts`](../src/application/usecases/setup/setup_wizard_use_case.ts) · [`src/application/usecases/setup/setup_journey_use_case.ts`](../src/application/usecases/setup/setup_journey_use_case.ts) · [`src/application/usecases/setup/setup_session_coordinator.ts`](../src/application/usecases/setup/setup_session_coordinator.ts) · [`src/application/usecases/setup/setup_questionnaire_controller.ts`](../src/application/usecases/setup/setup_questionnaire_controller.ts) · [`src/application/usecases/setup/setup_credentials_use_case.ts`](../src/application/usecases/setup/setup_credentials_use_case.ts) · [`src/application/usecases/setup/setup_token_permissions_use_case.ts`](../src/application/usecases/setup/setup_token_permissions_use_case.ts) · [`src/application/usecases/setup/doctor_use_case.ts`](../src/application/usecases/setup/doctor_use_case.ts) · [`src/application/usecases/setup/merge_queue_readiness_use_case.ts`](../src/application/usecases/setup/merge_queue_readiness_use_case.ts) · [`src/application/usecases/actions/initial_setup_workflow.ts`](../src/application/usecases/actions/initial_setup_workflow.ts) · [`src/application/usecases/actions/setup_resource_provisioning.ts`](../src/application/usecases/actions/setup_resource_provisioning.ts) · [`src/application/ports/message_catalog_ports.ts`](../src/application/ports/message_catalog_ports.ts) · [`src/application/usecases/localization/resolve_message_catalog_use_case.ts`](../src/application/usecases/localization/resolve_message_catalog_use_case.ts) · [`src/application/policies/setup_configuration_validation.ts`](../src/application/policies/setup_configuration_validation.ts) · [`src/infrastructure/setup_workspace_adapter.ts`](../src/infrastructure/setup_workspace_adapter.ts) · [`src/data/repository/repository_variables_repository.ts`](../src/data/repository/repository_variables_repository.ts) · [`src/infrastructure/github/ports/github_repository_variables_protocol.ts`](../src/infrastructure/github/ports/github_repository_variables_protocol.ts) · [`src/infrastructure/setup_remote_credential_health_adapter.ts`](../src/infrastructure/setup_remote_credential_health_adapter.ts) · [`src/infrastructure/setup_credential_validation_adapter.ts`](../src/infrastructure/setup_credential_validation_adapter.ts) · [`src/infrastructure/setup_token_permission_query_adapter.ts`](../src/infrastructure/setup_token_permission_query_adapter.ts) · [`src/infrastructure/setup_permission_actions_probe.ts`](../src/infrastructure/setup_permission_actions_probe.ts) · [`src/infrastructure/setup_permission_issue_probe.ts`](../src/infrastructure/setup_permission_issue_probe.ts) · [`src/infrastructure/setup_permission_issue_cleanup.ts`](../src/infrastructure/setup_permission_issue_cleanup.ts) · [`src/infrastructure/setup_permission_probe_journal.ts`](../src/infrastructure/setup_permission_probe_journal.ts) · [`src/infrastructure/setup_permission_probe_transaction.ts`](../src/infrastructure/setup_permission_probe_transaction.ts) · [`src/infrastructure/setup_permission_resource_probes.ts`](../src/infrastructure/setup_permission_resource_probes.ts) · [`src/infrastructure/setup_projects_probe_page_policy.ts`](../src/infrastructure/setup_projects_probe_page_policy.ts) · [`src/cli/setup_terminal_driver.ts`](../src/cli/setup_terminal_driver.ts) · [`src/cli/setup_question_renderer.ts`](../src/cli/setup_question_renderer.ts) · [`src/cli/setup_journey_presenter.ts`](../src/cli/setup_journey_presenter.ts) · [`src/cli/setup_plan_presenter.ts`](../src/cli/setup_plan_presenter.ts) · [`src/cli/setup_doctor_presenter.ts`](../src/cli/setup_doctor_presenter.ts) · [`src/cli/setup_prompt_rendering.ts`](../src/cli/setup_prompt_rendering.ts) · [`src/cli/setup_credential_prompt_adapter.ts`](../src/cli/setup_credential_prompt_adapter.ts) · [`src/cli/setup_credential_collection.ts`](../src/cli/setup_credential_collection.ts) · [`src/cli/commands/setup_execution.ts`](../src/cli/commands/setup_execution.ts) · [`src/cli/setup_token_permission_presenter.ts`](../src/cli/setup_token_permission_presenter.ts) · [`src/infrastructure/composition/setup_credentials_composition_root.ts`](../src/infrastructure/composition/setup_credentials_composition_root.ts) · [`src/infrastructure/composition/setup_token_permissions_composition_root.ts`](../src/infrastructure/composition/setup_token_permissions_composition_root.ts) · [`src/infrastructure/composition/setup_doctor_composition_root.ts`](../src/infrastructure/composition/setup_doctor_composition_root.ts) · [`scripts/coverage-budgets.json`](../scripts/coverage-budgets.json) · [`scripts/documentation_pat_exception_policy.cjs`](../scripts/documentation_pat_exception_policy.cjs) · [`scripts/validate-documentation-contract.cjs`](../scripts/validate-documentation-contract.cjs) · [`src/infrastructure/http_deadline.ts`](../src/infrastructure/http_deadline.ts) · [`src/infrastructure/setup_permission_denial.ts`](../src/infrastructure/setup_permission_denial.ts) · [`src/infrastructure/setup_permission_read_probe.ts`](../src/infrastructure/setup_permission_read_probe.ts) · [`src/infrastructure/setup_permission_read_evidence.ts`](../src/infrastructure/setup_permission_read_evidence.ts) · [`src/infrastructure/setup_permission_read_shape.ts`](../src/infrastructure/setup_permission_read_shape.ts) · [`src/infrastructure/setup_permission_projects_read.ts`](../src/infrastructure/setup_permission_projects_read.ts) · [`src/infrastructure/setup_permission_projects_access.ts`](../src/infrastructure/setup_permission_projects_access.ts) · [`src/infrastructure/setup_permission_workflow_cleanup.ts`](../src/infrastructure/setup_permission_workflow_cleanup.ts) · [`src/infrastructure/setup_permission_project_cleanup.ts`](../src/infrastructure/setup_permission_project_cleanup.ts) · [`src/infrastructure/setup_permission_probe_http.ts`](../src/infrastructure/setup_permission_probe_http.ts) · [`src/infrastructure/setup_permission_pull_request_probe.ts`](../src/infrastructure/setup_permission_pull_request_probe.ts) · [`src/infrastructure/git_authentication_environment.ts`](../src/infrastructure/git_authentication_environment.ts) · [`src/actions/github_action_input.ts`](../src/actions/github_action_input.ts) · [`src/infrastructure/setup_github_identity_query_adapter.ts`](../src/infrastructure/setup_github_identity_query_adapter.ts) · [`src/infrastructure/github_secret_encryption.ts`](../src/infrastructure/github_secret_encryption.ts) · [`src/infrastructure/setup_permission_organization_owner.ts`](../src/infrastructure/setup_permission_organization_owner.ts) · [`src/data/repository/github/github_actions_resource_inspector.ts`](../src/data/repository/github/github_actions_resource_inspector.ts) · [`src/data/repository/github/github_actions_resource_commands.ts`](../src/data/repository/github/github_actions_resource_commands.ts) · [`src/data/repository/github/github_actions_resource_collection.ts`](../src/data/repository/github/github_actions_resource_collection.ts) · [`src/application/usecases/setup/resolve_setup_workflow_pat_conflict_use_case.ts`](../src/application/usecases/setup/resolve_setup_workflow_pat_conflict_use_case.ts) · [`src/cli/setup_workflow_pat_storage_copy.ts`](../src/cli/setup_workflow_pat_storage_copy.ts) · [`src/cli/commands/setup_policy.ts`](../src/cli/commands/setup_policy.ts) · [`src/application/usecases/execution/execution_issue_number_policy.ts`](../src/application/usecases/execution/execution_issue_number_policy.ts) · [`src/application/usecases/execution/setup_execution_workflow.ts`](../src/application/usecases/execution/setup_execution_workflow.ts)
diff --git a/specs/catalog.json b/specs/catalog.json
index ff7e31a14..a9e7461aa 100644
--- a/specs/catalog.json
+++ b/specs/catalog.json
@@ -653,7 +653,8 @@
"specs": [
"specs/setup-configuration-credentials-and-doctor.md",
"specs/setup-doctor-architecture-hardening.md",
- "specs/setup-pat-permission-guidance-and-verification.md"
+ "specs/setup-pat-permission-guidance-and-verification.md",
+ "specs/github-runtime-doctor-command.md"
],
"workflows": [
"setup/workflows/agent-cli-provisioning.yml",
diff --git a/specs/github-runtime-doctor-command.md b/specs/github-runtime-doctor-command.md
new file mode 100644
index 000000000..e3407cb3a
--- /dev/null
+++ b/specs/github-runtime-doctor-command.md
@@ -0,0 +1,353 @@
+# GitHub Runtime Doctor Command
+
+- Status: Draft
+- Date: 2026-10-11
+- Catalog capability ID: `setup-and-doctor`
+- Last verified: not applicable (prospective specification)
+- Owners: setup-and-doctor capability owner; implementation owners to be confirmed in review
+- Scope: Add deterministic `/copilot doctor` replies to issue and pull request conversations using bounded read-only evidence about runtime prerequisites.
+- Related issues/PRs: Issue #461
+- Required review gates: product UX, architecture, testing, documentation, security/operations
+- Open decisions blocking readiness: None identified in the supplied issue and answers.
+
+## 1. Executive summary
+
+A repository participant who can comment may invoke `/copilot doctor` in an issue or pull request. The command checks a fixed set of runtime and workflow prerequisites, reports the status and source of each check, and gives one primary next action. It performs no model call and makes no repair or other repository mutation. Each successful read is evidence only for that read; unavailable evidence is never presented as proof of a missing permission or unhealthy credential.
+
+```text
+/copilot doctor -> bounded independent reads -> one correlated, localized reply with check states, evidence links, and one primary action
+```
+
+## 2. Problem, current behavior, and evidence
+
+### 2.1 Problem
+
+Maintainers investigating a policy-unavailable recommendation result may need to inspect workflow logs and run a local doctor. `/copilot diagnose` is an AI investigation and does not provide a deterministic runtime configuration audit. Participants need safe, inspectable evidence in the conversation without sharing tokens or installing a local CLI.
+
+### 2.2 Current behavior
+
+Facts supported by the issue: the prior live round produced a policy-unavailable assessment even though the policy was valid; diagnosis required workflow logs and local doctor; `/copilot diagnose` is AI-assisted. The issue does not establish the exact current command admission implementation or the existing adapter method names. Those must be verified against repository code during implementation.
+
+### 2.3 Evidence
+
+- Code/workflow/documentation references: issue #461 identifies the existing setup/doctor policies, comment-automation, semantic GitHub communication, issue-start-and-sdd-readiness, `specs/catalog.json`, and the feature workflow as the relevant owners and sources. Exact file paths and contract details require repository verification.
+- Incidents or concrete examples: a live recommendation assessment reported policy unavailable although the policy was valid; investigation required workflow logs and local doctor.
+- External primary sources: not required to define this repository behavior.
+- Unknowns: current concrete port APIs, workflow contract shape, existing test commands and thresholds, and documentation route names must be confirmed before implementation.
+
+### 2.4 Retrospective classification (as-built baselines only)
+
+Not applicable: this is a prospective specification and does not reconstruct an implemented baseline.
+
+## 3. Actors, surfaces, and terminology
+
+| Actor | Goal | Entry point | Visible surfaces |
+|---|---|---|---|
+| Repository participant | Understand actionable runtime prerequisites | Explicit `/copilot doctor` comment | Issue or PR reply and linked evidence |
+| Maintainer/operator | Fix a proven prerequisite or investigate unavailable evidence | Reply links and workflow run | Repository settings/policy source and run logs |
+| Workflow | Gather bounded evidence and publish one response | Existing comment event workflow | One reconciled issue/PR comment |
+
+“Evidence group” means one bounded prerequisite assessment in the fixed order in §6. “Unavailable” means the command could not establish a fact because a read or sample was absent or failed. “Blocked” means a successful, authoritative read or pure policy established an invalid required prerequisite. “Current PR identity” means the exact PR identified by the event and verified through a canonical current PR read, including a final head freshness check.
+
+## 4. Goals, non-goals, and fixed invariants
+
+### 4.1 Goals
+
+1. Provide a deterministic, read-only runtime audit in issue and PR conversations.
+2. Preserve independent successful results when another evidence group is unavailable.
+3. Give each result a distinct state, inspectable source/link, and safe next action.
+4. Reuse existing policies and semantic read ports when their trust and credential roles match.
+
+### 4.2 Non-goals
+
+1. Model invocation, diagnosis, repair, setup provisioning, or mutation of files, labels, variables, secrets, projects, checks, or reviews.
+2. Testing credential write access, secret values or health, organization grants unavailable to the runtime, or local setup-operator PAT behavior in an Action run.
+3. Certifying CI, coverage, producer health, write grants, or the entire runtime credential from names or successful reads.
+
+### 4.3 Fixed product/safety invariants
+
+1. Explicit-command admission and bot-loop guards remain in force; any participant permitted by GitHub to comment may invoke the command. No organization-membership gate is added.
+2. No new configuration flag, credential, or permission requirement is introduced.
+3. Never infer a missing grant from a denied, absent, malformed, or incomplete read. Never expose tokens, secret values, raw provider diagnostics, or guessed zero values.
+4. PR identity comes from the event PR marker and exact PR number, then canonical current identity/head reads. Never search other PRs or infer a linked issue from an equal number.
+5. A successful policy read proves only that read. A declared `producerAttested` value is input, not independently verified producer health.
+
+## 5. Current versus proposed product journey
+
+| Stage | Current | Proposed | User/operator effect |
+|---|---|---|---|
+| Request | Runtime audit is not described as a deterministic conversation command | Explicit `/copilot doctor` follows existing admission and bot-loop rules | Participant requests a safe audit in context |
+| Evidence | Incident investigation may require logs and local doctor | Fixed seven-group bounded reads and existing pure policies | Successful facts remain useful even when other reads fail |
+| Reply | No command-specific report contract is established by issue evidence | One localized, correlated reply with state, source, link, and primary action | Participant can inspect and act without token sharing |
+
+```mermaid
+flowchart LR
+ A[Explicit issue or PR command] --> B[Validate target and gather independent bounded evidence]
+ B --> C[Apply fixed state priority and choose one action]
+ C --> D[Reconcile one localized correlated reply]
+```
+
+Text equivalent: explicit issue/PR command -> independent bounded reads and policy evaluation -> one prioritized action -> one reconciled reply.
+
+## 6. Functional behavior and state model
+
+### 6.1 Happy path
+
+1. The existing comment entrypoint admits an explicit `/copilot doctor` request and rejects bot loops using current guards.
+2. The application identifies an issue target or exact PR target. For a PR it verifies canonical identity and current head; a final freshness check detects a changed head.
+3. The application evaluates the seven groups in §6.2. It continues independent reads after failures and retains safe results.
+4. Pure aggregation assigns each group `pass`, `warning`, `blocked`, `unavailable`, or `not-applicable`. Overall severity is `blocked`, then `unavailable`, then `warning`, then `pass`; not-applicable checks are excluded. If all checks are not applicable, overall state is not applicable.
+5. The presenter chooses the first actionable check in the fixed group order within the highest severity and produces one localized reply through the current reply reconciler.
+
+### 6.2 Evidence groups and alternatives
+
+The fixed order is:
+
+1. Effective issue-workflow and dependency configuration, validated by existing pure policies.
+2. Pre-branch SDD eligibility and repository catalog/template prerequisites. Help/exempt issues mark these checks not applicable.
+3. Linked-branch readiness when the current issue requires branch work. Missing optional linked issue context on a PR does not invalidate the PR; issue checks depending on it are not applicable.
+4. Installed issue, comment, and PR workflow contracts read from one verified default-branch revision. Inspect documented structural contracts only; do not infer token write grants from workflow contents.
+5. Approval policy accessibility and strict validity, using the existing effective repository/organization Actions Variables policy reader and strict `PR_APPROVAL_POLICY` v1 parser, including established precedence.
+6. Declared approval prerequisites when policy mode is active. Valid `off` makes active-assessment checks not applicable; valid `recommend` or `guarded` reports declared settings. A verified missing value retains existing off semantics. Never fall back to an injected variable after trusted read failure.
+7. Exact current PR identity/head when the target is a PR. For issue targets this group is not applicable.
+
+Proven invalid required configuration, invalid policy, or failed required contract is blocked. Denied/missing ports, malformed provider response, network error, incomplete sample, or failed canonical identity/head read is unavailable, never proof of missing permission. A failure to read policy is unavailable; a successfully read malformed policy is blocked.
+
+### 6.3 State machine and delivery behavior
+
+| State | Entered when | User-visible meaning | Allowed next states | Recovery/owner |
+|---|---|---|---|---|
+| Gathering | An admitted request starts | Checks are being evaluated | Reported, unavailable report | Application use case |
+| Reported | Checks are summarized | Current evidence and next action are shown | Updated on same delivery replay | Reply reconciler |
+| Partial report | One or more checks are unavailable but other results exist | Some evidence is usable; remaining checks need investigation | Updated on replay or new request | Participant/operator |
+| Stale target | PR head changes before final freshness check | Current PR evidence cannot be confirmed | Unavailable report | Participant retries after inspecting PR |
+
+The existing correlated reply identity is exact repository, conversation target, and source comment ID. Redelivery/replay for the same delivery updates or reuses one reply. A new user request creates a new correlated reply. No extra cards or notifications are published. Existing lifecycle and waiting labels are preserved. No other durable state schema is introduced by this feature.
+
+## 7. User-facing configuration
+
+There is no new user-facing configuration. The command uses existing effective configuration and policy semantics. Recommended configuration is the repository’s existing valid setup and approval policy; a meaningful alternative is a valid approval policy mode of `off`, which makes active-assessment prerequisites not applicable. No new override is permitted.
+
+- Existing precedence and operation-time reread behavior must be preserved by the owning readers and policies; implementation must verify their actual contract.
+- Missing policy value follows existing off semantics only when the trusted reader successfully establishes absence. Read failure is unavailable and must not use an injected fallback.
+- Unknown, malformed, or retired policy values are handled by the existing strict parser; malformed successfully read policy is blocked.
+- Safety invariants in §4.3 are not configurable.
+
+## 8. Clean Architecture design
+
+### 8.1 Responsibilities and dependency direction
+
+| Layer/boundary | Owns | Must not own/import |
+|---|---|---|
+| Domain/pure policy | Check state, severity priority, applicability, action selection, and validation decisions | GitHub SDK, provider DTOs, network or rendering |
+| Application | Doctor use case, fixed evidence plan, independent reads, aggregation inputs, immutable outputs | Concrete SDK/process calls, provider DTOs, mutations |
+| Semantic ports | Narrow runtime configuration, classification, repository content, linked-branch readiness, canonical PR, approval-policy reads, and reply reconciliation capabilities | Provider response models or policy decisions |
+| Data/provider adapters | Mapping GitHub/runtime responses and provider errors into semantic results | Product state priority or permission inference |
+| Infrastructure/composition | Wiring existing runtime credential and installed capabilities | Business policy or local operator PAT fabrication |
+| Entrypoints | Parse/adapt comment event and enforce existing admission/loop guards | Duplicate orchestration or target-number inference |
+| Presentation | Localized bounded report and sanitized links | Reads, mutations, or state transitions |
+
+```mermaid
+flowchart LR
+ E[Comment event entrypoint] --> A[Doctor application use case]
+ A --> D[Pure doctor and existing setup policies]
+ A --> P[Typed semantic read ports]
+ I[Existing runtime adapters] --> P
+ A --> R[Existing correlated reply reconciler]
+ A --> V[Localized report model and renderer]
+```
+
+Text equivalent: event entrypoint calls the application; application combines pure policy with typed read ports, then supplies a view model to the renderer and the existing reply reconciler.
+
+### 8.2 Contracts, state, and trust boundaries
+
+- Pure decisions: group applicability, check classification from explicit semantic outcomes, severity priority, and primary action selection.
+- Application contracts: immutable request target, bounded evidence results, check view models, and report outcome. No provider DTOs cross the application boundary.
+- Semantic ports: reuse existing narrow runtime configuration, issue classification, repository content, linked-branch readiness, canonical PR, approval-policy read, and correlated reply capabilities only where trust and runtime credential roles match.
+- Durable state/schema ownership: existing comment correlation/reconciler owns reply identity. No feature-specific durable state is required.
+- Concurrency/idempotency: use existing delivery correlation and reconciler; recheck PR head freshness before publication. A stale head produces unavailable target evidence.
+- Trusted and untrusted inputs: GitHub event identity and authoritative read results are validated at adapters; comments, repository content, provider strings, and workflow files are untrusted data. Workflow files are read, parsed for documented structure, and never executed.
+- Provider error mapping: permission denied, missing port, malformed response, network failure, and incomplete sample map to unavailable with safe category only. Do not expose raw diagnostics or synthesize absent values.
+
+### 8.3 Executable architecture constraints
+
+Automated checks MUST enforce no provider SDK/DTO imports in domain or application contracts, no dependency from domain to adapters or presentation, no write port in the doctor use case, and no workflow-content execution path. Contract tests MUST parse workflow structure at one authoritative default-branch revision. Existing architecture and dependency checks remain required.
+
+## 9. UI/UX and content contract
+
+### 9.1 Information hierarchy
+
+The reply starts with overall state, completed facts, next transition, one primary action, impact, and inspectable links. Technical details follow in a compact details section. Each check shows a textual state, evidence source/link when available, and short safe detail. No raw provider diagnostics or guessed zeros appear.
+
+### 9.2 Representative primary view and variants
+
+```markdown
+
+
+# 🩺 Runtime doctor: Partially available
+
+> **Current status:** Some runtime prerequisites could not be read.
+>
+> **Action required:** Inspect the [workflow run](RUN_URL) and documented read access, then retry `/copilot doctor`.
+
+## Checks
+
+- **Pass — Effective workflow configuration:** Validated by the existing setup policy. [Configuration source](SOURCE_URL)
+- **Unavailable — Approval policy:** The policy could not be read, so its validity is unknown. [Workflow run](RUN_URL)
+- **Not applicable — PR identity:** This command was run in an issue.
+
+Other checks completed; unavailable evidence does not establish a missing permission.
+
+
+Technical details
+
+Safe check categories and authoritative revision or PR links only.
+
+
+```
+
+Concrete state variants:
+
+- Pending/no action: “Runtime doctor is collecting the bounded checks. No repository changes are being made.” A pending comment is only used if existing entrypoint lifecycle already publishes one; no second comment is created.
+- Action required: “Blocked — the required SDD template prerequisite is invalid. Correct the linked template/catalog entry, then retry.” Link to the specific source.
+- Blocked/failed before side effects: “Blocked — the policy was read but is invalid. Correct the policy at [policy source](URL), then retry. No repository changes were made.”
+- Partially complete: use the representative view; retain pass facts and identify unavailable groups without implying failure of successful reads.
+- Complete: “Pass — all applicable checks passed. No action required.” List evidence sources and explain excluded not-applicable checks.
+
+### 9.3 Issue, PR, and comment behavior
+
+- Deterministic title: “Runtime doctor: ``”; purpose is bounded runtime prerequisite evidence.
+- Merge/close/retry does not trigger repair. A new explicit command performs a new audit. Existing PR and issue lifecycle behavior remains authoritative.
+- Durable marker and idempotency use the existing correlated reply identity; replay updates/reuses the same reply.
+- One correlated reply per source comment/delivery; no additional cards or notifications.
+- Labels and checks are not modified and are not used as inferred evidence.
+- Links point to authoritative source files/settings where safe, exact PR, and relevant workflow run. Never fabricate links or infer a linked issue.
+
+### 9.4 Accessibility, localization, and responsive behavior
+
+English and Spanish catalogs are complete and use existing atomic fallback. Status is text, not color or emoji alone. Markdown remains readable on narrow screens, uses logical headings and descriptive links, and avoids wide tables. Escape/sanitize untrusted Markdown, HTML, mentions, commands, markers, and URLs according to existing publication policy. Render only allowlisted repository/GitHub destinations; reject unsafe schemes. Provide textual explanations for all icons and status distinctions.
+
+## 10. Failure, recovery, and cleanup
+
+| Failure/partial state | User impact | Retained facts | Automatic retry | Required action | Cleanup |
+|---|---|---|---|---|---|
+| Trusted policy read denied or unavailable | Policy status unknown | Other successful checks | Existing bounded workflow retry only | Inspect run and documented read access; retry | None |
+| Policy read succeeds but strict parse fails | Required policy blocked | Read source and other checks | No automatic repair | Correct policy and retry | None |
+| Workflow sample incomplete or malformed | Contract evidence unavailable | Verified checks from same operation | Bounded existing retry if supported | Inspect run/source and retry | None |
+| Required contract proven invalid | Blocked prerequisite | Contract revision and other checks | No | Correct contract at linked source | None |
+| PR identity/head read fails or head becomes stale | PR evidence unavailable | Non-PR independent checks | No blind repeat | Inspect exact PR and retry | None |
+| Reply publication fails | User may not see the report | Safe operation result may be logged with correlation | Existing reconciler behavior | Inspect workflow run | No duplicate reply on replay |
+
+User-facing error order is impact, cause, action, retained state. Successful reads remain distinct from failed reads. Cleanup is not applicable because the use case performs no repair or repository mutation.
+
+## 11. Security, permissions, and privacy
+
+1. Use only installed runtime credential capabilities and existing read ports. GitHub’s ability to access a private conversation remains authoritative; do not add organization-membership checks.
+2. Treat comments, repository files, workflow contents, and provider strings as untrusted. Parse documented structure only; never execute file contents.
+3. Never disclose tokens, secret values, credential health, raw provider diagnostics, or local setup-operator PAT details. Do not claim write permission, producer health, or whole-credential validity.
+4. Preserve explicit command admission and bot-loop guards. Correlate publication to exact repository, conversation target, and source comment; revalidate exact PR identity/head.
+5. Make no mutations to files, labels, variables, secrets, projects, checks, reviews, or approval state.
+
+## 12. Observability and operational UX
+
+- User-facing state: overall state and independent check states, source/link, and one primary action.
+- Job Summary/operator evidence: existing workflow summary/log facilities may record correlation and safe categories; exact existing facility is to be verified. Never log secrets or raw provider diagnostics.
+- Logs, metrics, and correlation: use existing workflow run and reply correlation identifiers; do not create a new externally visible event stream.
+- Pending external dependency versus workflow failure: unavailable evidence identifies the safe category and run link; it is not described as a proven missing grant.
+- Rate-limit/retry behavior: follow existing read adapter and workflow retry bounds; no unbounded polling. Exact bounds must match current repository policy.
+- Noise/comment budget: one correlated reply, updated on replay; no extra cards or notifications.
+
+## 13. Compatibility, migration, rollout, and rollback
+
+No persisted feature schema or configuration migration is introduced. Existing in-flight comment lifecycle, waiting labels, locale selection, command admission, and reply reconciliation remain in force. Deploy through the installed feature workflow and its in-progress label before implementation is considered ready, as required by the issue context; do not introduce a feature flag. Rollback removes command routing and presentation while retaining existing comments as historical records. No irreversible repository effects are performed.
+
+## 14. Testing strategy and numeric budget
+
+The minimum is **52 distinct cases**, derived from seven evidence groups, multiple provider failure classes, replay/identity risks, and localized user-visible states. A parameterized row counts only when it exercises a distinct behavior.
+
+| Area | Minimum distinct cases | Behaviors/risks covered |
+|---|---:|---|
+| Domain/configuration/pure planning | 12 | State mapping, applicability, strict policy states, off/recommend/guarded, precedence, severity, action selection |
+| State/application/idempotency/races | 10 | Independent reads, partial reports, replay reuse, new request identity, stale PR head, ordering, cancellation/error retention |
+| Adapters/provider contracts | 8 | Denied, missing port, malformed response, network failure, incomplete sample, trusted absence, canonical PR mapping |
+| Workflows/setup/schema | 6 | One verified default revision, structural workflow contract, existing admission/loop guards, no permission inference, installed workflow integration |
+| UI/UX/localization/sanitization | 8 | Five states, English/Spanish fallback, safe links, mention/Markdown/HTML sanitization, compact mobile-readable content |
+| Integration/security/compatibility | 8 | No-write/no-model guarantee, exact PR identity, no issue-number inference, secret non-disclosure, authorization, replay and existing lifecycle |
+| **Total** | **52** | No double counting |
+
+Keep repository-wide coverage budgets. New pure policy MUST meet at least 90% branch coverage or the repository’s higher existing threshold. Changed modules MUST meet existing thresholds without reducing repository coverage. Use table-driven tests, deterministic IDs/clocks and fakes; no live GitHub services or real waits. Workflow checks parse structure rather than grep prose. Golden reply fixtures are supplemented by semantic assertions for headings, status, action, links, locale, and sanitization. Manual UX evidence: reviewers inspect narrow-screen rendering in English and Spanish and confirm the primary action and evidence links are understandable without logs.
+
+## 15. Documentation and discoverability
+
+| Audience | Artifact/page | Required content | Validation/navigation |
+|---|---|---|---|
+| User | Existing command/help documentation | `/copilot doctor` use, states, example, no-write boundary | Help catalog and route/link validation |
+| Setup owner | Setup/doctor documentation | Seven evidence groups, existing policy precedence, unavailable vs invalid | Contract fixtures and docs links |
+| Operator | Troubleshooting/runbook | State interpretation, run inspection, retry, exact PR identity, safe recovery | Decision-tree review and route validation |
+| Contributor | Architecture/spec and catalog | Ports, trust boundaries, correlation, test budget, workflow contracts | Specification validator and architecture checks |
+
+Document that local setup operator PAT and Action runtime PAT are distinct and that the former is not available in the Action run. Examples must be checked against reply fixtures and existing policy fixtures. Update catalog evidence and generated catalog view as required by repository specification workflow.
+
+## 16. Acceptance scenarios
+
+1. Given an admitted issue command and all applicable valid reads, when `/copilot doctor` runs, then one localized reply reports pass, links to sources, and says no action is required.
+2. Given a verified invalid required configuration, when the command runs, then that check and overall report are blocked with the linked source and a correction action.
+3. Given a valid approval policy, when the trusted policy reader succeeds, then parser and policy-owned precedence determine the result; an injected value does not override a trusted read.
+4. Given a denied policy read and successful unrelated reads, when the command runs, then policy is unavailable, successful checks remain visible, and no missing permission is claimed.
+5. Given a successfully read malformed policy, when parsing completes, then policy is blocked; this differs from unavailable read evidence.
+6. Given a valid `off` policy, when active approval prerequisites are considered, then those checks are not applicable; a missing value is off only when the trusted read proves absence.
+7. Given an issue exempt from pre-branch SDD checks, when the audit runs, then those checks are not applicable and do not lower overall severity.
+8. Given a PR with no optional linked issue, when the audit runs, then PR checks proceed and linked-issue checks are not applicable.
+9. Given a PR event with exact PR identity, when canonical identity succeeds but the head changes before publication, then PR evidence is unavailable and no other PR is searched.
+10. Given issue and PR numbers that happen to match, when the command runs, then no issue is inferred from the PR number.
+11. Given two workflow contract reads from different revisions, when the evidence is assembled, then the sample is unavailable or rejected; a report never combines revisions as one verified contract set.
+12. Given a provider response containing unsafe Markdown, mentions, HTML markers, or an unsafe URL, when rendered, then content is sanitized and no unintended mention or link is emitted.
+13. Given the same delivery is replayed, when reconciliation runs, then the same correlated reply is updated/reused; a new source comment creates a new reply.
+14. Given a participant allowed by GitHub to comment but not known to be an organization member, when they invoke the command, then existing admission rules apply and no new membership gate is added.
+15. Given any result state, when rendered in English or Spanish, then atomic locale fallback produces readable text status and exactly one primary action.
+16. Given the completed implementation, when documentation and architecture checks run, then catalog/spec validation, workflow structural contracts, test budget, and no-write/no-model checks pass.
+
+## 17. Requirements traceability
+
+| Requirement | Policy/use case/adapter/presentation | Test or evidence | Documentation |
+|---|---|---|---|
+| Seven bounded groups and applicability | Application plan and setup/doctor policies | Pure policy and orchestration cases | Setup/doctor reference |
+| Partial results and unavailable semantics | Application result mapping | Read failure matrix | Operator troubleshooting |
+| Approval precedence and strict parse | Existing policy reader/parser | Trusted absence, failure, malformed, mode cases | Setup/doctor reference |
+| Exact PR identity and freshness | Event adapter and canonical PR port | Identity/head/replay integration cases | Operator troubleshooting |
+| One correlated localized reply | Existing reconciler and renderer | Replay, locale, semantic UI cases | User command/help docs |
+| No mutation, model, or secret disclosure | Use case ports and output sanitizer | Security/no-write/no-model tests | User and contributor docs |
+| Architecture and workflow boundaries | Ports/adapters and workflow contract parser | Architecture and workflow checks | Contributor spec |
+
+## 18. Implementation sequence
+
+1. Verify repository standard, catalog capability, owning setup/doctor SDD, comment-automation and semantic-publication contracts, issue-start-and-sdd-readiness, and installed feature workflow. Keep this companion proportional and preserve the owning contract.
+2. Define immutable application result and semantic port contracts; add architecture constraints forbidding write/model dependencies.
+3. Implement pure status, applicability, severity, and action policies with risk-derived tests.
+4. Orchestrate independent reads, policy validation, exact PR freshness, and partial-result retention.
+5. Reuse existing adapters and correlate publication through the existing reply reconciler; add structural workflow contract checks.
+6. Implement localized presentation, safe links, sanitization, and golden/semantic fixtures.
+7. Complete user, setup, operator, and contributor documentation; update catalog evidence and generated artifacts as required.
+8. Run repository validation, coverage, workflow/security checks, and manual English/Spanish narrow-screen UX review before readiness.
+
+## 19. Definition of Done
+
+- [ ] Every normative requirement has acceptance and traceability.
+- [ ] Architecture boundaries are implemented and automatically enforced; doctor has no mutation or model capability.
+- [ ] Numeric 52-case minimum, distribution, coverage requirements, and repository budgets pass.
+- [ ] Existing policy defaults, precedence, validation, and trusted-read semantics are preserved.
+- [ ] Primary report states and content examples are implemented and reviewed.
+- [ ] English/Spanish, fallback, accessibility, responsive behavior, sanitization, and one-reply noise budget pass.
+- [ ] User/setup/operator/contributor documentation is discoverable and examples match fixtures.
+- [ ] Partial evidence, failures, retry, idempotency, PR freshness, security, and cleanup pass.
+- [ ] Required workflow structural contracts and repository validation commands pass.
+- [ ] `specs/catalog.json` evidence and `specs/CATALOG.md` are current and `pnpm run validate:specifications` passes.
+- [ ] Feature workflow and in-progress label requirements are satisfied before implementation readiness.
+- [ ] No readiness-blocking decision remains unresolved.
+
+## 20. References and decisions
+
+- Primary sources: Issue #461 and supplied answers Q1–Q7; repository standard and template as provided in the application task.
+- Related specs/ADRs/issues/PRs: setup-and-doctor owning specification; comment-automation; semantic GitHub communication; issue-start-and-sdd-readiness; exact paths and current contract details to be verified in repository.
+- Decisions: preserve existing owning contract and use a focused companion; no new configuration or permission; independent partial reads; blocked/unavailable distinction; exact PR identity; one correlated localized reply.
+- Follow-up work outside this spec: any repair workflow, credential write testing, local setup provisioning, or broader credential-health certification.
\ No newline at end of file
From 0eae21ea3d6dc56a99ffddada1f71c1df6940460 Mon Sep 17 00:00:00 2001
From: vypbot
Date: Sun, 11 Oct 2026 15:37:10 +0200
Subject: [PATCH 02/23] chore(#461): apply user request
---
.../__tests__/github_runtime_doctor.test.ts | 46 ++++++++++++
src/domain/github_runtime_doctor.ts | 74 +++++++++++++++++++
2 files changed, 120 insertions(+)
create mode 100644 src/domain/__tests__/github_runtime_doctor.test.ts
create mode 100644 src/domain/github_runtime_doctor.ts
diff --git a/src/domain/__tests__/github_runtime_doctor.test.ts b/src/domain/__tests__/github_runtime_doctor.test.ts
new file mode 100644
index 000000000..538d6a27b
--- /dev/null
+++ b/src/domain/__tests__/github_runtime_doctor.test.ts
@@ -0,0 +1,46 @@
+import {
+ parseTrustedDoctorApprovalPolicy,
+ type TrustedApprovalPolicyRead,
+} from '../github_runtime_doctor';
+import { DEFAULT_PULL_REQUEST_APPROVAL_POLICY } from '../pull_request_approval_policy';
+
+const validPolicy = {
+ ...DEFAULT_PULL_REQUEST_APPROVAL_POLICY,
+ testChecks: [{ name: 'CI', sourceAppId: 15368, workflowName: 'CI' }],
+ coverage: { mode: 'check' as const, checkName: 'CI' },
+};
+
+describe('parseTrustedDoctorApprovalPolicy', () => {
+ it('parses a valid repository policy from the trusted effective read', () => {
+ const read: TrustedApprovalPolicyRead = {
+ status: 'available', source: 'repository', value: JSON.stringify(validPolicy),
+ };
+ expect(parseTrustedDoctorApprovalPolicy(read)).toEqual({
+ status: 'valid', source: 'repository', policy: validPolicy,
+ });
+ });
+
+ it('retains the trusted organization source when that is the effective value', () => {
+ const read: TrustedApprovalPolicyRead = {
+ status: 'available', source: 'organization', value: JSON.stringify(validPolicy),
+ };
+ expect(parseTrustedDoctorApprovalPolicy(read)).toMatchObject({ status: 'valid', source: 'organization' });
+ });
+
+ it('treats verified absence as the existing off default', () => {
+ expect(parseTrustedDoctorApprovalPolicy({ status: 'absent' })).toMatchObject({
+ status: 'valid', source: 'default-off', policy: { mode: 'off' },
+ });
+ });
+
+ it('keeps a failed trusted read unavailable', () => {
+ expect(parseTrustedDoctorApprovalPolicy({ status: 'unavailable' })).toEqual({ status: 'unavailable' });
+ });
+
+ it.each(['{', 'null', JSON.stringify({ ...DEFAULT_PULL_REQUEST_APPROVAL_POLICY, version: 2 })])(
+ 'marks successfully read malformed policy as invalid without exposing parser details', (value) => {
+ expect(parseTrustedDoctorApprovalPolicy({ status: 'available', source: 'repository', value }))
+ .toEqual({ status: 'invalid' });
+ },
+ );
+});
diff --git a/src/domain/github_runtime_doctor.ts b/src/domain/github_runtime_doctor.ts
new file mode 100644
index 000000000..5bcb13aeb
--- /dev/null
+++ b/src/domain/github_runtime_doctor.ts
@@ -0,0 +1,74 @@
+import {
+ DISABLED_PULL_REQUEST_APPROVAL_POLICY,
+ parsePullRequestApprovalPolicy,
+ type PullRequestApprovalPolicy,
+} from './pull_request_approval_policy';
+
+// Domain contracts and trusted policy parsing are implemented here. Application
+// orchestration, adapters, command routing, rendering, and reply publication remain pending.
+
+/** The fixed, ordered evidence groups in the GitHub runtime doctor contract. */
+export const GITHUB_RUNTIME_DOCTOR_GROUPS = [
+ 'effective-configuration',
+ 'issue-readiness',
+ 'linked-branch-readiness',
+ 'workflow-contracts',
+ 'approval-policy',
+ 'approval-prerequisites',
+ 'pull-request-identity',
+] as const;
+
+export type GithubRuntimeDoctorGroup = typeof GITHUB_RUNTIME_DOCTOR_GROUPS[number];
+export type GithubRuntimeDoctorCheckState = 'pass' | 'warning' | 'blocked' | 'unavailable' | 'not-applicable';
+export type GithubRuntimeDoctorSeverity = 'blocked' | 'unavailable' | 'warning' | 'pass' | 'not-applicable';
+export type GithubRuntimeDoctorTarget =
+ | { readonly kind: 'issue'; readonly repositoryId: number; readonly issueNumber: number }
+ | { readonly kind: 'pull-request'; readonly repositoryId: number; readonly pullNumber: number };
+
+/** A domain result never includes provider diagnostics, credentials, or raw response DTOs. */
+export interface GithubRuntimeDoctorCheck {
+ readonly group: GithubRuntimeDoctorGroup;
+ readonly state: GithubRuntimeDoctorCheckState;
+ readonly evidence?: { readonly source: string; readonly url?: string };
+ readonly action?: string;
+}
+
+export interface GithubRuntimeDoctorReport {
+ readonly target: GithubRuntimeDoctorTarget;
+ readonly checks: readonly GithubRuntimeDoctorCheck[];
+ readonly severity: GithubRuntimeDoctorSeverity;
+ readonly primaryAction?: string;
+}
+
+/** Semantic input to the strict policy boundary: callers must say whether a read succeeded. */
+export type TrustedApprovalPolicyRead =
+ | {
+ readonly status: 'available';
+ /** Effective trusted source after repository-over-organization precedence. */
+ readonly source: 'repository' | 'organization';
+ readonly value: string;
+ }
+ | { readonly status: 'absent' }
+ | { readonly status: 'unavailable' };
+
+export type DoctorApprovalPolicyResult =
+ | { readonly status: 'valid'; readonly source: 'repository' | 'organization' | 'default-off'; readonly policy: PullRequestApprovalPolicy }
+ | { readonly status: 'invalid' }
+ | { readonly status: 'unavailable' };
+
+/**
+ * Parses only a successful trusted effective read. Verified absence preserves the
+ * established off default; read failure is unavailable and never consults an injected value.
+ */
+export function parseTrustedDoctorApprovalPolicy(read: TrustedApprovalPolicyRead): DoctorApprovalPolicyResult {
+ if (read.status === 'unavailable') return { status: 'unavailable' };
+ if (read.status === 'absent') {
+ return { status: 'valid', source: 'default-off', policy: DISABLED_PULL_REQUEST_APPROVAL_POLICY };
+ }
+ try {
+ return { status: 'valid', source: read.source, policy: parsePullRequestApprovalPolicy(read.value) };
+ } catch {
+ // Do not leak parser/provider text into a report or user-facing surface.
+ return { status: 'invalid' };
+ }
+}
From 650a9d6f45636e3c670e1c23d21e1a1c2f1db7f2 Mon Sep 17 00:00:00 2001
From: vypbot
Date: Sun, 11 Oct 2026 16:23:24 +0200
Subject: [PATCH 03/23] chore(#461): apply user request
---
docs/issues/comment-commands.mdx | 7 ++++
specs/CATALOG.md | 12 +++----
specs/catalog.json | 18 +++++++---
specs/github-runtime-doctor-command.md | 11 +++++-
.../policies/copilot_interaction_policy.ts | 1 +
.../policies/publication_message_catalog.ts | 3 ++
.../comment_automation_use_case.test.ts | 26 ++++++++++++++
.../comment_automation_command_workflow.ts | 9 +++++
src/domain/__tests__/copilot_command.test.ts | 1 +
.../__tests__/github_runtime_doctor.test.ts | 36 +++++++++++++++++++
src/domain/copilot_command.ts | 1 +
src/domain/github_runtime_doctor.ts | 28 +++++++++++++--
12 files changed, 140 insertions(+), 13 deletions(-)
diff --git a/docs/issues/comment-commands.mdx b/docs/issues/comment-commands.mdx
index 08065bdaa..bff2d579f 100644
--- a/docs/issues/comment-commands.mdx
+++ b/docs/issues/comment-commands.mdx
@@ -45,6 +45,7 @@ Commands are recognized at the beginning of a comment, case-insensitively. They
| `/copilot findings` | Reconcile and publish current findings. | No |
| `/copilot recheck` | Re-run the review against the current branch. | No |
| `/copilot status` | Show the current Copilot status. | No |
+| `/copilot doctor` | Check runtime prerequisites using bounded read-only evidence. | No |
| `/copilot description` | Refresh the PR description when that action is configured. | No |
| `/copilot fix [finding-id ...]` | Fix one or more existing Bugbot findings. | Yes, if authorized |
| `/copilot dismiss [finding-id ...]` | Dismiss one or more findings. | No file changes |
@@ -119,6 +120,12 @@ separate from an issue with the same number. Status and description queries
preserve existing lifecycle and waiting labels when they make no lifecycle
decision.
+`/copilot doctor` is registered as a deterministic read-only command. Its full
+seven-group runtime evidence collection and correlated report are still being
+implemented; until those adapters are installed, the command returns a safe
+unavailable response and does not invoke an agent. It does not inspect secret
+values, test write access, or infer grants from workflow contents.
+
Read-only commands answer the current comment, using the issue or PR as
background. Add the file, symbol, failure or requested work after the command to
make the subject explicit. For example, `/copilot explain src/domain/copilot_command.ts`
diff --git a/specs/CATALOG.md b/specs/CATALOG.md
index cbad6ef85..2f2f3ab12 100644
--- a/specs/CATALOG.md
+++ b/specs/CATALOG.md
@@ -10,13 +10,13 @@ debt or convert unknown historic intent into a design decision.
| Capability ID | Status | Scope | Primary SDD | Evidence |
|---|---|---|---|---|
-| `github-communication-experience` | Implemented | English-default, localized, semantic, bounded, and idempotent product messages across GitHub and repository-aware operator surfaces | [Semantic GitHub communication and repository localization](./semantic-github-publication-and-notification.md) + 1 companion | 218 paths · 2026-10-11 |
+| `github-communication-experience` | Implemented | English-default, localized, semantic, bounded, and idempotent product messages across GitHub and repository-aware operator surfaces | [Semantic GitHub communication and repository localization](./semantic-github-publication-and-notification.md) + 1 companion | 219 paths · 2026-10-11 |
| `release-orchestration` | Implemented | Release and hotfix promotion, publication, reconciliation, and durable recovery | [Configurable production-first release orchestration](./configurable-release-orchestration.md) + 2 companion | 52 paths · 2026-09-16 |
| `merge-queue-readiness` | Implemented | Fail-closed validation of required checks and merge-group workflow support | [Merge queue readiness and effective target rules](./merge-queue-readiness.md) | 24 paths · 2026-09-15 |
| `bugbot-review-state-reconciliation` | Implemented | Reconcile review snapshots, findings, threads, comments, and check conclusions | [Bugbot review-state reconciliation](./bugbot-review-state-reconciliation.md) | 59 paths · 2026-10-11 |
| `execution-lifecycle` | Implemented | Shared GitHub Action lifecycle from event admission through durable user-facing results | [Execution admission, queueing, routing, and result publication](./execution-admission-queue-and-publication.md) + 3 companion | 84 paths · 2026-09-16 |
| `architecture-quality-hardening` | Implemented | Close verified concurrency, error-contract, context-coupling, fan-out, setup/doctor, and provider-policy risks in dependency order | [Architecture quality and scalability hardening](./architecture-quality-and-scalability-hardening.md) + 1 companion | 72 paths · 2026-09-16 |
-| `setup-and-doctor` | Implemented | Plan, validate, provision, and audit a repository installation without exposing credentials | [Setup, configuration, credentials, and doctor](./setup-configuration-credentials-and-doctor.md) + 3 companion | 165 paths · 2026-10-11 |
+| `setup-and-doctor` | Implemented | Plan, validate, provision, and audit a repository installation without exposing credentials | [Setup, configuration, credentials, and doctor](./setup-configuration-credentials-and-doctor.md) + 3 companion | 174 paths · 2026-10-11 |
| `local-web-setup-assistant` | Proposed | Offer a packaged, loopback-only Svelte setup interface over the existing engine with four-language guidance, English CLI help, basic/custom first-run paths, editable question and plan review, source-labelled GitHub facts, assisted CI and Project evidence, role-separated PATs, and truthful itemized outcomes; inspect existing configuration and apply independent, scope-aware runtime adjustments with permission, drift and read-back verification | [Local web setup assistant](./local-web-setup-assistant.md) + 1 companion | 202 paths · 2026-10-09 |
| `guided-bot-pat-onboarding` | Proposed | Guide creation of the persistent workflow PAT, verify bot identity and grants, recover explicitly from wrong-account input within the session, and install the approved Actions Secret | [Guided bot PAT onboarding](./guided-bot-pat-onboarding.md) | 39 paths · 2026-10-07 |
| `temporary-setup-operator-authorization` | Proposed | Guide creation of the one-run operator PAT through GitHub's official form, verify final setup access, and report user-owned deletion accurately | [Assisted setup PAT creation](./temporary-setup-operator-authorization.md) | 34 paths · 2026-10-07 |
@@ -41,7 +41,7 @@ debt or convert unknown historic intent into a design decision.
- Specifications: [`specs/semantic-github-publication-and-notification.md`](./semantic-github-publication-and-notification.md) · [`specs/repository-locale-and-localization.md`](./repository-locale-and-localization.md)
- Workflows: [`.github/workflows/copilot_issue.yml`](../.github/workflows/copilot_issue.yml) · [`.github/workflows/copilot_issue_comment.yml`](../.github/workflows/copilot_issue_comment.yml) · [`.github/workflows/copilot_pull_request.yml`](../.github/workflows/copilot_pull_request.yml) · [`.github/workflows/copilot_pull_request_review_state.yml`](../.github/workflows/copilot_pull_request_review_state.yml) · [`.github/workflows/copilot_pull_request_comment.yml`](../.github/workflows/copilot_pull_request_comment.yml) · [`.github/workflows/copilot_commit.yml`](../.github/workflows/copilot_commit.yml) · [`.github/workflows/copilot_close_inactive_issues.yml`](../.github/workflows/copilot_close_inactive_issues.yml) · [`.github/workflows/copilot_deployment_orchestration.yml`](../.github/workflows/copilot_deployment_orchestration.yml)
- Entrypoints: [`src/actions/github_action.ts`](../src/actions/github_action.ts) · [`src/actions/github_action_completion.ts`](../src/actions/github_action_completion.ts) · [`src/actions/github_event_inputs.ts`](../src/actions/github_event_inputs.ts) · [`src/cli_context.ts`](../src/cli_context.ts) · [`src/cli/commands/check_progress.ts`](../src/cli/commands/check_progress.ts) · [`src/cli/commands/issue_command_policy.ts`](../src/cli/commands/issue_command_policy.ts) · [`src/api.ts`](../src/api.ts) · [`src/cli.ts`](../src/cli.ts) · [`package.json`](../package.json)
-- Core code: [`scripts/coverage-budgets.json`](../scripts/coverage-budgets.json) · [`scripts/validate-github-communication-test-budget.cjs`](../scripts/validate-github-communication-test-budget.cjs) · [`src/architecture/github_communication_test_budget.json`](../src/architecture/github_communication_test_budget.json) · [`src/domain/implementation_plan.ts`](../src/domain/implementation_plan.ts) · [`src/domain/locale.ts`](../src/domain/locale.ts) · [`src/domain/message_catalog.ts`](../src/domain/message_catalog.ts) · [`src/data/model/locale.ts`](../src/data/model/locale.ts) · [`src/actions/github_action_locale_inputs.ts`](../src/actions/github_action_locale_inputs.ts) · [`src/application/ports/message_catalog_ports.ts`](../src/application/ports/message_catalog_ports.ts) · [`src/application/policies/resolved_message_catalog_policy.ts`](../src/application/policies/resolved_message_catalog_policy.ts) · [`src/application/policies/action_summary_message_catalog.ts`](../src/application/policies/action_summary_message_catalog.ts) · [`src/application/policies/branch_sync_message_catalog.ts`](../src/application/policies/branch_sync_message_catalog.ts) · [`src/application/policies/inactivity_message_catalog.ts`](../src/application/policies/inactivity_message_catalog.ts) · [`src/application/policies/inactivity_notification_policy.ts`](../src/application/policies/inactivity_notification_policy.ts) · [`src/application/policies/merge_queue_message_catalog.ts`](../src/application/policies/merge_queue_message_catalog.ts) · [`src/application/policies/setup_doctor_message_catalog.ts`](../src/application/policies/setup_doctor_message_catalog.ts) · [`src/application/policies/setup_doctor_report_policy.ts`](../src/application/policies/setup_doctor_report_policy.ts) · [`src/application/usecases/localization/resolve_message_catalog_use_case.ts`](../src/application/usecases/localization/resolve_message_catalog_use_case.ts) · [`src/application/usecases/setup/doctor_use_case.ts`](../src/application/usecases/setup/doctor_use_case.ts) · [`src/application/usecases/setup/merge_queue_readiness_use_case.ts`](../src/application/usecases/setup/merge_queue_readiness_use_case.ts) · [`src/application/usecases/steps/common/comment_language_translation_workflow.ts`](../src/application/usecases/steps/common/comment_language_translation_workflow.ts) · [`src/application/policies/comment_translation_policy.ts`](../src/application/policies/comment_translation_policy.ts) · [`src/application/usecases/steps/common/think_request_policy.ts`](../src/application/usecases/steps/common/think_request_policy.ts) · [`src/application/usecases/steps/common/think_workflow.ts`](../src/application/usecases/steps/common/think_workflow.ts) · [`src/application/usecases/steps/common/think_answer_workflow.ts`](../src/application/usecases/steps/common/think_answer_workflow.ts) · [`src/application/usecases/steps/common/think_use_case.ts`](../src/application/usecases/steps/common/think_use_case.ts) · [`src/application/usecases/comment_automation_use_case.ts`](../src/application/usecases/comment_automation_use_case.ts) · [`src/application/usecases/steps/common/publish_resume_workflow.ts`](../src/application/usecases/steps/common/publish_resume_workflow.ts) · [`src/domain/github_publication.ts`](../src/domain/github_publication.ts) · [`src/domain/git_object_id.ts`](../src/domain/git_object_id.ts) · [`src/application/ports/publication_freshness_ports.ts`](../src/application/ports/publication_freshness_ports.ts) · [`src/application/policies/publication_identity_policy.ts`](../src/application/policies/publication_identity_policy.ts) · [`src/application/policies/copilot_interaction_policy.ts`](../src/application/policies/copilot_interaction_policy.ts) · [`src/application/policies/recommendation_policy.ts`](../src/application/policies/recommendation_policy.ts) · [`src/application/ports/actor_authorization_ports.ts`](../src/application/ports/actor_authorization_ports.ts) · [`src/data/repository/actor_modification_policy.ts`](../src/data/repository/actor_modification_policy.ts) · [`src/data/repository/organization/actor_authorization_repository.ts`](../src/data/repository/organization/actor_authorization_repository.ts) · [`src/application/policies/publication_outcome_policy.ts`](../src/application/policies/publication_outcome_policy.ts) · [`src/application/policies/publication_message_catalog.ts`](../src/application/policies/publication_message_catalog.ts) · [`src/application/policies/semantic_result_publication_policy.ts`](../src/application/policies/semantic_result_publication_policy.ts) · [`src/application/policies/agent_response_schemas.ts`](../src/application/policies/agent_response_schemas.ts) · [`src/application/usecases/actions/recommend_steps_result_policy.ts`](../src/application/usecases/actions/recommend_steps_result_policy.ts) · [`src/application/usecases/actions/publish_issue_comment_workflow.ts`](../src/application/usecases/actions/publish_issue_comment_workflow.ts) · [`src/data/model/recommendation_state.ts`](../src/data/model/recommendation_state.ts) · [`src/data/model/config.ts`](../src/data/model/config.ts) · [`src/prompts/recommend_steps.ts`](../src/prompts/recommend_steps.ts) · [`src/application/usecases/issue_use_case.ts`](../src/application/usecases/issue_use_case.ts) · [`src/application/usecases/issue_workflow.ts`](../src/application/usecases/issue_workflow.ts) · [`src/application/usecases/issue_workflow_context.ts`](../src/application/usecases/issue_workflow_context.ts) · [`src/application/usecases/steps/issue/answer_issue_help_use_case.ts`](../src/application/usecases/steps/issue/answer_issue_help_use_case.ts) · [`src/application/usecases/steps/issue/answer_issue_help_workflow.ts`](../src/application/usecases/steps/issue/answer_issue_help_workflow.ts) · [`src/application/ports/issue_lifecycle_ports.ts`](../src/application/ports/issue_lifecycle_ports.ts) · [`src/application/usecases/steps/common/status_card_publication_workflow.ts`](../src/application/usecases/steps/common/status_card_publication_workflow.ts) · [`src/application/usecases/steps/common/reply_publication_workflow.ts`](../src/application/usecases/steps/common/reply_publication_workflow.ts) · [`src/application/usecases/steps/common/transition_notification_workflow.ts`](../src/application/usecases/steps/common/transition_notification_workflow.ts) · [`src/application/usecases/steps/common/duplicate_comment_cleanup_workflow.ts`](../src/application/usecases/steps/common/duplicate_comment_cleanup_workflow.ts) · [`src/actions/local_action.ts`](../src/actions/local_action.ts) · [`src/actions/local_action_output.ts`](../src/actions/local_action_output.ts) · [`src/cli/commands/think.ts`](../src/cli/commands/think.ts) · [`src/cli/commands/think_command_handler.ts`](../src/cli/commands/think_command_handler.ts) · [`src/infrastructure/composition/local_action_composition_root.ts`](../src/infrastructure/composition/local_action_composition_root.ts) · [`src/infrastructure/composition/main_run_route_composition_root.ts`](../src/infrastructure/composition/main_run_route_composition_root.ts) · [`src/infrastructure/composition/issue_use_case_composition_root.ts`](../src/infrastructure/composition/issue_use_case_composition_root.ts) · [`src/infrastructure/composition/shared_capability_port_binding.ts`](../src/infrastructure/composition/shared_capability_port_binding.ts) · [`src/architecture/github_publication_mutation_baseline.json`](../src/architecture/github_publication_mutation_baseline.json) · [`src/application/policies/action_summary_policy.ts`](../src/application/policies/action_summary_policy.ts) · [`src/application/policies/application_error_message_catalog.ts`](../src/application/policies/application_error_message_catalog.ts) · [`src/application/policies/application_error_presentation_policy.ts`](../src/application/policies/application_error_presentation_policy.ts) · [`src/application/policies/branch_sync_notification_policy.ts`](../src/application/policies/branch_sync_notification_policy.ts) · [`src/application/policies/bugbot_message_catalog.ts`](../src/application/policies/bugbot_message_catalog.ts) · [`src/application/policies/deployment_message_catalog.ts`](../src/application/policies/deployment_message_catalog.ts) · [`src/application/usecases/actions/observe_branch_sync_use_case.ts`](../src/application/usecases/actions/observe_branch_sync_use_case.ts) · [`src/application/usecases/actions/close_inactive_issues_use_case.ts`](../src/application/usecases/actions/close_inactive_issues_use_case.ts) · [`src/application/usecases/actions/close_inactive_issues_workflow.ts`](../src/application/usecases/actions/close_inactive_issues_workflow.ts) · [`src/application/usecases/push_single_action_contexts.ts`](../src/application/usecases/push_single_action_contexts.ts) · [`src/infrastructure/composition/issue_inactivity_composition_root.ts`](../src/infrastructure/composition/issue_inactivity_composition_root.ts) · [`src/application/policies/bugbot_review_presentation_policy.ts`](../src/application/policies/bugbot_review_presentation_policy.ts) · [`src/application/usecases/steps/commit/detect_potential_problems_workflow.ts`](../src/application/usecases/steps/commit/detect_potential_problems_workflow.ts) · [`src/application/usecases/steps/commit/bugbot/publish_pr_review_comments.ts`](../src/application/usecases/steps/commit/bugbot/publish_pr_review_comments.ts) · [`src/application/usecases/steps/commit/bugbot/resolve_issue_finding.ts`](../src/application/usecases/steps/commit/bugbot/resolve_issue_finding.ts) · [`src/application/usecases/steps/commit/bugbot/synchronize_bugbot_review_presentation_use_case.ts`](../src/application/usecases/steps/commit/bugbot/synchronize_bugbot_review_presentation_use_case.ts) · [`src/application/policies/deployment_presentation_policy.ts`](../src/application/policies/deployment_presentation_policy.ts) · [`src/application/usecases/actions/recommend_steps_workflow.ts`](../src/application/usecases/actions/recommend_steps_workflow.ts) · [`src/application/usecases/actions/check_progress_workflow.ts`](../src/application/usecases/actions/check_progress_workflow.ts) · [`src/application/usecases/actions/check_progress_use_case.ts`](../src/application/usecases/actions/check_progress_use_case.ts) · [`src/application/usecases/actions/progress_analysis_workflow.ts`](../src/application/usecases/actions/progress_analysis_workflow.ts) · [`src/data/repository/github_publication_source_repository.ts`](../src/data/repository/github_publication_source_repository.ts) · [`src/infrastructure/composition/check_progress_composition_root.ts`](../src/infrastructure/composition/check_progress_composition_root.ts) · [`src/data/repository/issue/issue_content_repository.ts`](../src/data/repository/issue/issue_content_repository.ts) · [`src/data/repository/github/github_error_policy.ts`](../src/data/repository/github/github_error_policy.ts) · [`src/infrastructure/github/ports/github_issue_provider_ports.ts`](../src/infrastructure/github/ports/github_issue_provider_ports.ts) · [`src/application/usecases/comment_automation_command_workflow.ts`](../src/application/usecases/comment_automation_command_workflow.ts) · [`src/application/usecases/steps/commit/bugbot/dismiss_bugbot_findings_use_case.ts`](../src/application/usecases/steps/commit/bugbot/dismiss_bugbot_findings_use_case.ts) · [`src/application/policies/inactivity_result_counts_projection_policy.ts`](../src/application/policies/inactivity_result_counts_projection_policy.ts)
+- Core code: [`scripts/coverage-budgets.json`](../scripts/coverage-budgets.json) · [`scripts/validate-github-communication-test-budget.cjs`](../scripts/validate-github-communication-test-budget.cjs) · [`src/architecture/github_communication_test_budget.json`](../src/architecture/github_communication_test_budget.json) · [`src/domain/implementation_plan.ts`](../src/domain/implementation_plan.ts) · [`src/domain/locale.ts`](../src/domain/locale.ts) · [`src/domain/message_catalog.ts`](../src/domain/message_catalog.ts) · [`src/data/model/locale.ts`](../src/data/model/locale.ts) · [`src/actions/github_action_locale_inputs.ts`](../src/actions/github_action_locale_inputs.ts) · [`src/application/ports/message_catalog_ports.ts`](../src/application/ports/message_catalog_ports.ts) · [`src/application/policies/resolved_message_catalog_policy.ts`](../src/application/policies/resolved_message_catalog_policy.ts) · [`src/application/policies/action_summary_message_catalog.ts`](../src/application/policies/action_summary_message_catalog.ts) · [`src/application/policies/branch_sync_message_catalog.ts`](../src/application/policies/branch_sync_message_catalog.ts) · [`src/application/policies/inactivity_message_catalog.ts`](../src/application/policies/inactivity_message_catalog.ts) · [`src/application/policies/inactivity_notification_policy.ts`](../src/application/policies/inactivity_notification_policy.ts) · [`src/application/policies/merge_queue_message_catalog.ts`](../src/application/policies/merge_queue_message_catalog.ts) · [`src/application/policies/setup_doctor_message_catalog.ts`](../src/application/policies/setup_doctor_message_catalog.ts) · [`src/application/policies/setup_doctor_report_policy.ts`](../src/application/policies/setup_doctor_report_policy.ts) · [`src/application/usecases/localization/resolve_message_catalog_use_case.ts`](../src/application/usecases/localization/resolve_message_catalog_use_case.ts) · [`src/application/usecases/setup/doctor_use_case.ts`](../src/application/usecases/setup/doctor_use_case.ts) · [`src/application/usecases/setup/merge_queue_readiness_use_case.ts`](../src/application/usecases/setup/merge_queue_readiness_use_case.ts) · [`src/application/usecases/steps/common/comment_language_translation_workflow.ts`](../src/application/usecases/steps/common/comment_language_translation_workflow.ts) · [`src/application/policies/comment_translation_policy.ts`](../src/application/policies/comment_translation_policy.ts) · [`src/application/usecases/steps/common/think_request_policy.ts`](../src/application/usecases/steps/common/think_request_policy.ts) · [`src/application/usecases/steps/common/think_workflow.ts`](../src/application/usecases/steps/common/think_workflow.ts) · [`src/application/usecases/steps/common/think_answer_workflow.ts`](../src/application/usecases/steps/common/think_answer_workflow.ts) · [`src/application/usecases/steps/common/think_use_case.ts`](../src/application/usecases/steps/common/think_use_case.ts) · [`src/application/usecases/comment_automation_use_case.ts`](../src/application/usecases/comment_automation_use_case.ts) · [`src/application/usecases/steps/common/publish_resume_workflow.ts`](../src/application/usecases/steps/common/publish_resume_workflow.ts) · [`src/domain/github_publication.ts`](../src/domain/github_publication.ts) · [`src/domain/git_object_id.ts`](../src/domain/git_object_id.ts) · [`src/application/ports/publication_freshness_ports.ts`](../src/application/ports/publication_freshness_ports.ts) · [`src/application/policies/publication_identity_policy.ts`](../src/application/policies/publication_identity_policy.ts) · [`src/application/policies/copilot_interaction_policy.ts`](../src/application/policies/copilot_interaction_policy.ts) · [`src/application/policies/recommendation_policy.ts`](../src/application/policies/recommendation_policy.ts) · [`src/application/ports/actor_authorization_ports.ts`](../src/application/ports/actor_authorization_ports.ts) · [`src/data/repository/actor_modification_policy.ts`](../src/data/repository/actor_modification_policy.ts) · [`src/data/repository/organization/actor_authorization_repository.ts`](../src/data/repository/organization/actor_authorization_repository.ts) · [`src/application/policies/publication_outcome_policy.ts`](../src/application/policies/publication_outcome_policy.ts) · [`src/application/policies/publication_message_catalog.ts`](../src/application/policies/publication_message_catalog.ts) · [`src/application/policies/semantic_result_publication_policy.ts`](../src/application/policies/semantic_result_publication_policy.ts) · [`src/application/policies/agent_response_schemas.ts`](../src/application/policies/agent_response_schemas.ts) · [`src/application/usecases/actions/recommend_steps_result_policy.ts`](../src/application/usecases/actions/recommend_steps_result_policy.ts) · [`src/application/usecases/actions/publish_issue_comment_workflow.ts`](../src/application/usecases/actions/publish_issue_comment_workflow.ts) · [`src/data/model/recommendation_state.ts`](../src/data/model/recommendation_state.ts) · [`src/data/model/config.ts`](../src/data/model/config.ts) · [`src/prompts/recommend_steps.ts`](../src/prompts/recommend_steps.ts) · [`src/application/usecases/issue_use_case.ts`](../src/application/usecases/issue_use_case.ts) · [`src/application/usecases/issue_workflow.ts`](../src/application/usecases/issue_workflow.ts) · [`src/application/usecases/issue_workflow_context.ts`](../src/application/usecases/issue_workflow_context.ts) · [`src/application/usecases/steps/issue/answer_issue_help_use_case.ts`](../src/application/usecases/steps/issue/answer_issue_help_use_case.ts) · [`src/application/usecases/steps/issue/answer_issue_help_workflow.ts`](../src/application/usecases/steps/issue/answer_issue_help_workflow.ts) · [`src/application/ports/issue_lifecycle_ports.ts`](../src/application/ports/issue_lifecycle_ports.ts) · [`src/application/usecases/steps/common/status_card_publication_workflow.ts`](../src/application/usecases/steps/common/status_card_publication_workflow.ts) · [`src/application/usecases/steps/common/reply_publication_workflow.ts`](../src/application/usecases/steps/common/reply_publication_workflow.ts) · [`src/application/usecases/steps/common/transition_notification_workflow.ts`](../src/application/usecases/steps/common/transition_notification_workflow.ts) · [`src/application/usecases/steps/common/duplicate_comment_cleanup_workflow.ts`](../src/application/usecases/steps/common/duplicate_comment_cleanup_workflow.ts) · [`src/actions/local_action.ts`](../src/actions/local_action.ts) · [`src/actions/local_action_output.ts`](../src/actions/local_action_output.ts) · [`src/cli/commands/think.ts`](../src/cli/commands/think.ts) · [`src/cli/commands/think_command_handler.ts`](../src/cli/commands/think_command_handler.ts) · [`src/infrastructure/composition/local_action_composition_root.ts`](../src/infrastructure/composition/local_action_composition_root.ts) · [`src/infrastructure/composition/main_run_route_composition_root.ts`](../src/infrastructure/composition/main_run_route_composition_root.ts) · [`src/infrastructure/composition/issue_use_case_composition_root.ts`](../src/infrastructure/composition/issue_use_case_composition_root.ts) · [`src/infrastructure/composition/shared_capability_port_binding.ts`](../src/infrastructure/composition/shared_capability_port_binding.ts) · [`src/architecture/github_publication_mutation_baseline.json`](../src/architecture/github_publication_mutation_baseline.json) · [`src/application/policies/action_summary_policy.ts`](../src/application/policies/action_summary_policy.ts) · [`src/application/policies/application_error_message_catalog.ts`](../src/application/policies/application_error_message_catalog.ts) · [`src/application/policies/application_error_presentation_policy.ts`](../src/application/policies/application_error_presentation_policy.ts) · [`src/application/policies/branch_sync_notification_policy.ts`](../src/application/policies/branch_sync_notification_policy.ts) · [`src/application/policies/bugbot_message_catalog.ts`](../src/application/policies/bugbot_message_catalog.ts) · [`src/application/policies/deployment_message_catalog.ts`](../src/application/policies/deployment_message_catalog.ts) · [`src/application/usecases/actions/observe_branch_sync_use_case.ts`](../src/application/usecases/actions/observe_branch_sync_use_case.ts) · [`src/application/usecases/actions/close_inactive_issues_use_case.ts`](../src/application/usecases/actions/close_inactive_issues_use_case.ts) · [`src/application/usecases/actions/close_inactive_issues_workflow.ts`](../src/application/usecases/actions/close_inactive_issues_workflow.ts) · [`src/application/usecases/push_single_action_contexts.ts`](../src/application/usecases/push_single_action_contexts.ts) · [`src/infrastructure/composition/issue_inactivity_composition_root.ts`](../src/infrastructure/composition/issue_inactivity_composition_root.ts) · [`src/application/policies/bugbot_review_presentation_policy.ts`](../src/application/policies/bugbot_review_presentation_policy.ts) · [`src/application/usecases/steps/commit/detect_potential_problems_workflow.ts`](../src/application/usecases/steps/commit/detect_potential_problems_workflow.ts) · [`src/application/usecases/steps/commit/bugbot/publish_pr_review_comments.ts`](../src/application/usecases/steps/commit/bugbot/publish_pr_review_comments.ts) · [`src/application/usecases/steps/commit/bugbot/resolve_issue_finding.ts`](../src/application/usecases/steps/commit/bugbot/resolve_issue_finding.ts) · [`src/application/usecases/steps/commit/bugbot/synchronize_bugbot_review_presentation_use_case.ts`](../src/application/usecases/steps/commit/bugbot/synchronize_bugbot_review_presentation_use_case.ts) · [`src/application/policies/deployment_presentation_policy.ts`](../src/application/policies/deployment_presentation_policy.ts) · [`src/application/usecases/actions/recommend_steps_workflow.ts`](../src/application/usecases/actions/recommend_steps_workflow.ts) · [`src/application/usecases/actions/check_progress_workflow.ts`](../src/application/usecases/actions/check_progress_workflow.ts) · [`src/application/usecases/actions/check_progress_use_case.ts`](../src/application/usecases/actions/check_progress_use_case.ts) · [`src/application/usecases/actions/progress_analysis_workflow.ts`](../src/application/usecases/actions/progress_analysis_workflow.ts) · [`src/data/repository/github_publication_source_repository.ts`](../src/data/repository/github_publication_source_repository.ts) · [`src/infrastructure/composition/check_progress_composition_root.ts`](../src/infrastructure/composition/check_progress_composition_root.ts) · [`src/data/repository/issue/issue_content_repository.ts`](../src/data/repository/issue/issue_content_repository.ts) · [`src/data/repository/github/github_error_policy.ts`](../src/data/repository/github/github_error_policy.ts) · [`src/infrastructure/github/ports/github_issue_provider_ports.ts`](../src/infrastructure/github/ports/github_issue_provider_ports.ts) · [`src/application/usecases/comment_automation_command_workflow.ts`](../src/application/usecases/comment_automation_command_workflow.ts) · [`src/application/usecases/steps/commit/bugbot/dismiss_bugbot_findings_use_case.ts`](../src/application/usecases/steps/commit/bugbot/dismiss_bugbot_findings_use_case.ts) · [`src/application/policies/inactivity_result_counts_projection_policy.ts`](../src/application/policies/inactivity_result_counts_projection_policy.ts) · [`src/domain/copilot_command.ts`](../src/domain/copilot_command.ts)
- Tests: [`src/domain/__tests__/implementation_plan.test.ts`](../src/domain/__tests__/implementation_plan.test.ts) · [`src/domain/__tests__/locale.test.ts`](../src/domain/__tests__/locale.test.ts) · [`src/domain/__tests__/message_catalog.test.ts`](../src/domain/__tests__/message_catalog.test.ts) · [`src/actions/__tests__/configuration_builders.test.ts`](../src/actions/__tests__/configuration_builders.test.ts) · [`src/actions/__tests__/github_event_inputs.test.ts`](../src/actions/__tests__/github_event_inputs.test.ts) · [`src/cli/commands/__tests__/issue_command_policy.test.ts`](../src/cli/commands/__tests__/issue_command_policy.test.ts) · [`src/actions/__tests__/github_action_completion.test.ts`](../src/actions/__tests__/github_action_completion.test.ts) · [`src/data/model/__tests__/config.test.ts`](../src/data/model/__tests__/config.test.ts) · [`src/application/policies/__tests__/agent_response_schemas.test.ts`](../src/application/policies/__tests__/agent_response_schemas.test.ts) · [`src/prompts/__tests__/recommend_steps.test.ts`](../src/prompts/__tests__/recommend_steps.test.ts) · [`src/application/policies/__tests__/comment_translation_policy.test.ts`](../src/application/policies/__tests__/comment_translation_policy.test.ts) · [`src/application/policies/__tests__/action_summary_message_catalog.test.ts`](../src/application/policies/__tests__/action_summary_message_catalog.test.ts) · [`src/application/policies/__tests__/application_error_message_catalog.test.ts`](../src/application/policies/__tests__/application_error_message_catalog.test.ts) · [`src/application/policies/__tests__/application_error_presentation_policy.test.ts`](../src/application/policies/__tests__/application_error_presentation_policy.test.ts) · [`src/application/usecases/localization/__tests__/resolve_message_catalog_use_case.test.ts`](../src/application/usecases/localization/__tests__/resolve_message_catalog_use_case.test.ts) · [`src/prompts/__tests__/localize_message_catalog.test.ts`](../src/prompts/__tests__/localize_message_catalog.test.ts) · [`src/domain/__tests__/github_publication.test.ts`](../src/domain/__tests__/github_publication.test.ts) · [`src/domain/__tests__/git_object_id.test.ts`](../src/domain/__tests__/git_object_id.test.ts) · [`src/data/repository/__tests__/github_publication_source_repository.test.ts`](../src/data/repository/__tests__/github_publication_source_repository.test.ts) · [`src/application/policies/__tests__/publication_identity_policy.test.ts`](../src/application/policies/__tests__/publication_identity_policy.test.ts) · [`src/application/policies/__tests__/publication_outcome_policy.test.ts`](../src/application/policies/__tests__/publication_outcome_policy.test.ts) · [`src/application/policies/__tests__/publication_message_catalog.test.ts`](../src/application/policies/__tests__/publication_message_catalog.test.ts) · [`src/application/policies/__tests__/semantic_result_publication_policy.test.ts`](../src/application/policies/__tests__/semantic_result_publication_policy.test.ts) · [`src/application/policies/__tests__/action_summary_policy.test.ts`](../src/application/policies/__tests__/action_summary_policy.test.ts) · [`src/application/policies/__tests__/branch_sync_notification_policy.test.ts`](../src/application/policies/__tests__/branch_sync_notification_policy.test.ts) · [`src/application/policies/__tests__/inactivity_message_catalog.test.ts`](../src/application/policies/__tests__/inactivity_message_catalog.test.ts) · [`src/application/policies/__tests__/inactivity_notification_policy.test.ts`](../src/application/policies/__tests__/inactivity_notification_policy.test.ts) · [`src/application/policies/__tests__/setup_doctor_message_catalog.test.ts`](../src/application/policies/__tests__/setup_doctor_message_catalog.test.ts) · [`src/application/policies/__tests__/setup_doctor_report_policy.test.ts`](../src/application/policies/__tests__/setup_doctor_report_policy.test.ts) · [`src/application/usecases/actions/__tests__/observe_branch_sync_use_case.test.ts`](../src/application/usecases/actions/__tests__/observe_branch_sync_use_case.test.ts) · [`src/application/usecases/setup/__tests__/doctor_use_case.test.ts`](../src/application/usecases/setup/__tests__/doctor_use_case.test.ts) · [`src/application/usecases/setup/__tests__/merge_queue_readiness_use_case.test.ts`](../src/application/usecases/setup/__tests__/merge_queue_readiness_use_case.test.ts) · [`src/application/policies/__tests__/bugbot_message_catalog.test.ts`](../src/application/policies/__tests__/bugbot_message_catalog.test.ts) · [`src/application/policies/__tests__/deployment_message_catalog.test.ts`](../src/application/policies/__tests__/deployment_message_catalog.test.ts) · [`src/application/policies/__tests__/bugbot_review_presentation_policy.test.ts`](../src/application/policies/__tests__/bugbot_review_presentation_policy.test.ts) · [`src/application/usecases/steps/commit/__tests__/detect_potential_problems_use_case.test.ts`](../src/application/usecases/steps/commit/__tests__/detect_potential_problems_use_case.test.ts) · [`src/application/usecases/steps/commit/bugbot/__tests__/dismiss_bugbot_findings_use_case.test.ts`](../src/application/usecases/steps/commit/bugbot/__tests__/dismiss_bugbot_findings_use_case.test.ts) · [`src/application/usecases/steps/commit/bugbot/__tests__/synchronize_bugbot_review_presentation_use_case.test.ts`](../src/application/usecases/steps/commit/bugbot/__tests__/synchronize_bugbot_review_presentation_use_case.test.ts) · [`src/application/policies/__tests__/deployment_presentation_policy.test.ts`](../src/application/policies/__tests__/deployment_presentation_policy.test.ts) · [`src/application/usecases/actions/__tests__/close_inactive_issues_use_case.test.ts`](../src/application/usecases/actions/__tests__/close_inactive_issues_use_case.test.ts) · [`src/application/usecases/__tests__/push_single_action_contexts.test.ts`](../src/application/usecases/__tests__/push_single_action_contexts.test.ts) · [`src/application/usecases/steps/common/__tests__/comment_language_translation_workflow.test.ts`](../src/application/usecases/steps/common/__tests__/comment_language_translation_workflow.test.ts) · [`src/application/usecases/steps/common/__tests__/think_request_policy.test.ts`](../src/application/usecases/steps/common/__tests__/think_request_policy.test.ts) · [`src/application/usecases/steps/common/__tests__/think_use_case.test.ts`](../src/application/usecases/steps/common/__tests__/think_use_case.test.ts) · [`src/application/usecases/steps/common/__tests__/publish_resume_use_case.test.ts`](../src/application/usecases/steps/common/__tests__/publish_resume_use_case.test.ts) · [`src/application/usecases/steps/common/__tests__/status_card_publication_workflow.test.ts`](../src/application/usecases/steps/common/__tests__/status_card_publication_workflow.test.ts) · [`src/application/usecases/steps/common/__tests__/reply_publication_workflow.test.ts`](../src/application/usecases/steps/common/__tests__/reply_publication_workflow.test.ts) · [`src/application/usecases/steps/common/__tests__/transition_notification_workflow.test.ts`](../src/application/usecases/steps/common/__tests__/transition_notification_workflow.test.ts) · [`src/application/usecases/steps/common/__tests__/duplicate_comment_cleanup_workflow.test.ts`](../src/application/usecases/steps/common/__tests__/duplicate_comment_cleanup_workflow.test.ts) · [`src/data/repository/issue/__tests__/issue_content_repository.test.ts`](../src/data/repository/issue/__tests__/issue_content_repository.test.ts) · [`src/data/repository/deployment/__tests__/deployment_presentation_repository.test.ts`](../src/data/repository/deployment/__tests__/deployment_presentation_repository.test.ts) · [`src/data/repository/__tests__/github_error_policy.test.ts`](../src/data/repository/__tests__/github_error_policy.test.ts) · [`src/application/usecases/steps/issue/__tests__/answer_issue_help_use_case.test.ts`](../src/application/usecases/steps/issue/__tests__/answer_issue_help_use_case.test.ts) · [`src/application/usecases/__tests__/issue_use_case.test.ts`](../src/application/usecases/__tests__/issue_use_case.test.ts) · [`src/application/usecases/__tests__/issue_pull_request_context_projection.test.ts`](../src/application/usecases/__tests__/issue_pull_request_context_projection.test.ts) · [`src/actions/__tests__/local_action.test.ts`](../src/actions/__tests__/local_action.test.ts) · [`src/actions/__tests__/github_action.test.ts`](../src/actions/__tests__/github_action.test.ts) · [`src/data/repository/__tests__/actor_modification_policy.test.ts`](../src/data/repository/__tests__/actor_modification_policy.test.ts) · [`src/data/repository/organization/__tests__/actor_authorization_repository.test.ts`](../src/data/repository/organization/__tests__/actor_authorization_repository.test.ts) · [`src/__tests__/cli.test.ts`](../src/__tests__/cli.test.ts) · [`src/cli/commands/__tests__/think_command_handler.test.ts`](../src/cli/commands/__tests__/think_command_handler.test.ts) · [`src/infrastructure/composition/__tests__/local_action_composition_root.test.ts`](../src/infrastructure/composition/__tests__/local_action_composition_root.test.ts) · [`src/infrastructure/composition/__tests__/main_run_route_composition_root.test.ts`](../src/infrastructure/composition/__tests__/main_run_route_composition_root.test.ts) · [`src/infrastructure/composition/__tests__/issue_use_case_composition_root.test.ts`](../src/infrastructure/composition/__tests__/issue_use_case_composition_root.test.ts) · [`src/infrastructure/composition/__tests__/shared_capability_port_binding.test.ts`](../src/infrastructure/composition/__tests__/shared_capability_port_binding.test.ts) · [`src/infrastructure/composition/__tests__/pull_request_use_case_composition_root.test.ts`](../src/infrastructure/composition/__tests__/pull_request_use_case_composition_root.test.ts) · [`src/architecture/__tests__/github_publication_boundaries.test.ts`](../src/architecture/__tests__/github_publication_boundaries.test.ts) · [`src/application/usecases/steps/common/__tests__/publication_concurrency.integration.test.ts`](../src/application/usecases/steps/common/__tests__/publication_concurrency.integration.test.ts) · [`src/tooling/__tests__/validate_github_communication_test_budget.test.ts`](../src/tooling/__tests__/validate_github_communication_test_budget.test.ts) · [`src/tooling/__tests__/validate_workflow_contract.test.ts`](../src/tooling/__tests__/validate_workflow_contract.test.ts) · [`src/application/usecases/actions/__tests__/recommend_steps_use_case.test.ts`](../src/application/usecases/actions/__tests__/recommend_steps_use_case.test.ts) · [`src/application/usecases/actions/__tests__/check_progress_use_case.test.ts`](../src/application/usecases/actions/__tests__/check_progress_use_case.test.ts) · [`src/application/usecases/__tests__/comment_automation_use_case.test.ts`](../src/application/usecases/__tests__/comment_automation_use_case.test.ts)
- User documentation: [`docs/configuration.mdx`](../docs/configuration.mdx) · [`docs/configuration-checklist.mdx`](../docs/configuration-checklist.mdx) · [`docs/features.mdx`](../docs/features.mdx) · [`docs/issues/configuration.mdx`](../docs/issues/configuration.mdx) · [`docs/issues/comment-commands.mdx`](../docs/issues/comment-commands.mdx) · [`docs/issues/notifications-and-auto-close.mdx`](../docs/issues/notifications-and-auto-close.mdx) · [`docs/issues/branch-synchronization.mdx`](../docs/issues/branch-synchronization.mdx) · [`docs/issues/type/feature.mdx`](../docs/issues/type/feature.mdx) · [`docs/issues/type/bugfix.mdx`](../docs/issues/type/bugfix.mdx) · [`docs/issues/type/docs.mdx`](../docs/issues/type/docs.mdx) · [`docs/issues/type/chore.mdx`](../docs/issues/type/chore.mdx) · [`docs/issues/type/hotfix.mdx`](../docs/issues/type/hotfix.mdx) · [`docs/issues/type/release.mdx`](../docs/issues/type/release.mdx) · [`docs/issues/deployment-orchestration.mdx`](../docs/issues/deployment-orchestration.mdx) · [`docs/pull-requests/configuration.mdx`](../docs/pull-requests/configuration.mdx) · [`docs/pull-requests/capabilities.mdx`](../docs/pull-requests/capabilities.mdx) · [`docs/pull-requests/workflow-setup.mdx`](../docs/pull-requests/workflow-setup.mdx) · [`docs/pull-requests/examples.mdx`](../docs/pull-requests/examples.mdx) · [`docs/bugbot/configuration.mdx`](../docs/bugbot/configuration.mdx) · [`docs/bugbot/finding-publication.mdx`](../docs/bugbot/finding-publication.mdx) · [`docs/bugbot/detection.mdx`](../docs/bugbot/detection.mdx) · [`docs/bugbot/how-it-works.mdx`](../docs/bugbot/how-it-works.mdx) · [`docs/bugbot/programmatic-api.mdx`](../docs/bugbot/programmatic-api.mdx) · [`docs/bugbot/quality-observability.mdx`](../docs/bugbot/quality-observability.mdx) · [`docs/bugbot/failure-scenarios.mdx`](../docs/bugbot/failure-scenarios.mdx) · [`docs/bugbot/examples.mdx`](../docs/bugbot/examples.mdx) · [`docs/single-actions/configuration.mdx`](../docs/single-actions/configuration.mdx) · [`docs/single-actions/available-actions.mdx`](../docs/single-actions/available-actions.mdx) · [`docs/single-actions/workflow-and-cli.mdx`](../docs/single-actions/workflow-and-cli.mdx) · [`docs/single-actions/examples.mdx`](../docs/single-actions/examples.mdx) · [`docs/security-operations/security/prompt-injection.mdx`](../docs/security-operations/security/prompt-injection.mdx) · [`docs/security-operations/operations/verification.mdx`](../docs/security-operations/operations/verification.mdx) · [`docs/development/architecture.mdx`](../docs/development/architecture.mdx) · [`docs/development/testing.mdx`](../docs/development/testing.mdx)
@@ -107,9 +107,9 @@ debt or convert unknown historic intent into a design decision.
- Specifications: [`specs/setup-configuration-credentials-and-doctor.md`](./setup-configuration-credentials-and-doctor.md) · [`specs/setup-doctor-architecture-hardening.md`](./setup-doctor-architecture-hardening.md) · [`specs/setup-pat-permission-guidance-and-verification.md`](./setup-pat-permission-guidance-and-verification.md) · [`specs/github-runtime-doctor-command.md`](./github-runtime-doctor-command.md)
- Workflows: [`setup/workflows/agent-cli-provisioning.yml`](../setup/workflows/agent-cli-provisioning.yml) · [`setup/workflows/copilot_credential_health.yml`](../setup/workflows/copilot_credential_health.yml)
- Entrypoints: [`src/cli/commands/setup.ts`](../src/cli/commands/setup.ts) · [`src/cli/commands/doctor.ts`](../src/cli/commands/doctor.ts)
-- Core code: [`src/domain/setup.ts`](../src/domain/setup.ts) · [`src/domain/setup_questionnaire.ts`](../src/domain/setup_questionnaire.ts) · [`src/domain/setup_token_permissions.ts`](../src/domain/setup_token_permissions.ts) · [`src/application/ports/setup_terminal_ports.ts`](../src/application/ports/setup_terminal_ports.ts) · [`src/application/ports/setup_wizard_ports.ts`](../src/application/ports/setup_wizard_ports.ts) · [`src/application/ports/branch_tag_ports.ts`](../src/application/ports/branch_tag_ports.ts) · [`src/application/policies/setup_issue_workflow_policy.ts`](../src/application/policies/setup_issue_workflow_policy.ts) · [`src/data/repository/release/repository_version_tags_query_repository.ts`](../src/data/repository/release/repository_version_tags_query_repository.ts) · [`src/application/ports/setup_token_permission_ports.ts`](../src/application/ports/setup_token_permission_ports.ts) · [`src/application/policies/setup_token_permission_evidence_policy.ts`](../src/application/policies/setup_token_permission_evidence_policy.ts) · [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/policies/setup_questionnaire_policy.ts`](../src/application/policies/setup_questionnaire_policy.ts) · [`src/application/policies/setup_configuration_plan.ts`](../src/application/policies/setup_configuration_plan.ts) · [`src/application/policies/merge_setup_overrides_policy.ts`](../src/application/policies/merge_setup_overrides_policy.ts) · [`src/application/policies/setup_configuration_storage_policy.ts`](../src/application/policies/setup_configuration_storage_policy.ts) · [`src/application/policies/setup_doctor_message_catalog.ts`](../src/application/policies/setup_doctor_message_catalog.ts) · [`src/application/policies/setup_doctor_report_policy.ts`](../src/application/policies/setup_doctor_report_policy.ts) · [`src/application/policies/setup_journey_policy.ts`](../src/application/policies/setup_journey_policy.ts) · [`src/application/policies/setup_permission_summary_policy.ts`](../src/application/policies/setup_permission_summary_policy.ts) · [`src/application/usecases/setup/setup_wizard_use_case.ts`](../src/application/usecases/setup/setup_wizard_use_case.ts) · [`src/application/usecases/setup/setup_journey_use_case.ts`](../src/application/usecases/setup/setup_journey_use_case.ts) · [`src/application/usecases/setup/setup_session_coordinator.ts`](../src/application/usecases/setup/setup_session_coordinator.ts) · [`src/application/usecases/setup/setup_questionnaire_controller.ts`](../src/application/usecases/setup/setup_questionnaire_controller.ts) · [`src/application/usecases/setup/setup_credentials_use_case.ts`](../src/application/usecases/setup/setup_credentials_use_case.ts) · [`src/application/usecases/setup/setup_token_permissions_use_case.ts`](../src/application/usecases/setup/setup_token_permissions_use_case.ts) · [`src/application/usecases/setup/doctor_use_case.ts`](../src/application/usecases/setup/doctor_use_case.ts) · [`src/application/usecases/setup/merge_queue_readiness_use_case.ts`](../src/application/usecases/setup/merge_queue_readiness_use_case.ts) · [`src/application/usecases/actions/initial_setup_workflow.ts`](../src/application/usecases/actions/initial_setup_workflow.ts) · [`src/application/usecases/actions/setup_resource_provisioning.ts`](../src/application/usecases/actions/setup_resource_provisioning.ts) · [`src/application/ports/message_catalog_ports.ts`](../src/application/ports/message_catalog_ports.ts) · [`src/application/usecases/localization/resolve_message_catalog_use_case.ts`](../src/application/usecases/localization/resolve_message_catalog_use_case.ts) · [`src/application/policies/setup_configuration_validation.ts`](../src/application/policies/setup_configuration_validation.ts) · [`src/infrastructure/setup_workspace_adapter.ts`](../src/infrastructure/setup_workspace_adapter.ts) · [`src/data/repository/repository_variables_repository.ts`](../src/data/repository/repository_variables_repository.ts) · [`src/infrastructure/github/ports/github_repository_variables_protocol.ts`](../src/infrastructure/github/ports/github_repository_variables_protocol.ts) · [`src/infrastructure/setup_remote_credential_health_adapter.ts`](../src/infrastructure/setup_remote_credential_health_adapter.ts) · [`src/infrastructure/setup_credential_validation_adapter.ts`](../src/infrastructure/setup_credential_validation_adapter.ts) · [`src/infrastructure/setup_token_permission_query_adapter.ts`](../src/infrastructure/setup_token_permission_query_adapter.ts) · [`src/infrastructure/setup_permission_actions_probe.ts`](../src/infrastructure/setup_permission_actions_probe.ts) · [`src/infrastructure/setup_permission_issue_probe.ts`](../src/infrastructure/setup_permission_issue_probe.ts) · [`src/infrastructure/setup_permission_issue_cleanup.ts`](../src/infrastructure/setup_permission_issue_cleanup.ts) · [`src/infrastructure/setup_permission_probe_journal.ts`](../src/infrastructure/setup_permission_probe_journal.ts) · [`src/infrastructure/setup_permission_probe_transaction.ts`](../src/infrastructure/setup_permission_probe_transaction.ts) · [`src/infrastructure/setup_permission_resource_probes.ts`](../src/infrastructure/setup_permission_resource_probes.ts) · [`src/infrastructure/setup_projects_probe_page_policy.ts`](../src/infrastructure/setup_projects_probe_page_policy.ts) · [`src/cli/setup_terminal_driver.ts`](../src/cli/setup_terminal_driver.ts) · [`src/cli/setup_question_renderer.ts`](../src/cli/setup_question_renderer.ts) · [`src/cli/setup_journey_presenter.ts`](../src/cli/setup_journey_presenter.ts) · [`src/cli/setup_plan_presenter.ts`](../src/cli/setup_plan_presenter.ts) · [`src/cli/setup_doctor_presenter.ts`](../src/cli/setup_doctor_presenter.ts) · [`src/cli/setup_prompt_rendering.ts`](../src/cli/setup_prompt_rendering.ts) · [`src/cli/setup_credential_prompt_adapter.ts`](../src/cli/setup_credential_prompt_adapter.ts) · [`src/cli/setup_credential_collection.ts`](../src/cli/setup_credential_collection.ts) · [`src/cli/commands/setup_execution.ts`](../src/cli/commands/setup_execution.ts) · [`src/cli/setup_token_permission_presenter.ts`](../src/cli/setup_token_permission_presenter.ts) · [`src/infrastructure/composition/setup_credentials_composition_root.ts`](../src/infrastructure/composition/setup_credentials_composition_root.ts) · [`src/infrastructure/composition/setup_token_permissions_composition_root.ts`](../src/infrastructure/composition/setup_token_permissions_composition_root.ts) · [`src/infrastructure/composition/setup_doctor_composition_root.ts`](../src/infrastructure/composition/setup_doctor_composition_root.ts) · [`scripts/coverage-budgets.json`](../scripts/coverage-budgets.json) · [`scripts/documentation_pat_exception_policy.cjs`](../scripts/documentation_pat_exception_policy.cjs) · [`scripts/validate-documentation-contract.cjs`](../scripts/validate-documentation-contract.cjs) · [`src/infrastructure/http_deadline.ts`](../src/infrastructure/http_deadline.ts) · [`src/infrastructure/setup_permission_denial.ts`](../src/infrastructure/setup_permission_denial.ts) · [`src/infrastructure/setup_permission_read_probe.ts`](../src/infrastructure/setup_permission_read_probe.ts) · [`src/infrastructure/setup_permission_read_evidence.ts`](../src/infrastructure/setup_permission_read_evidence.ts) · [`src/infrastructure/setup_permission_read_shape.ts`](../src/infrastructure/setup_permission_read_shape.ts) · [`src/infrastructure/setup_permission_projects_read.ts`](../src/infrastructure/setup_permission_projects_read.ts) · [`src/infrastructure/setup_permission_projects_access.ts`](../src/infrastructure/setup_permission_projects_access.ts) · [`src/infrastructure/setup_permission_workflow_cleanup.ts`](../src/infrastructure/setup_permission_workflow_cleanup.ts) · [`src/infrastructure/setup_permission_project_cleanup.ts`](../src/infrastructure/setup_permission_project_cleanup.ts) · [`src/infrastructure/setup_permission_probe_http.ts`](../src/infrastructure/setup_permission_probe_http.ts) · [`src/infrastructure/setup_permission_pull_request_probe.ts`](../src/infrastructure/setup_permission_pull_request_probe.ts) · [`src/infrastructure/git_authentication_environment.ts`](../src/infrastructure/git_authentication_environment.ts) · [`src/actions/github_action_input.ts`](../src/actions/github_action_input.ts) · [`src/infrastructure/setup_github_identity_query_adapter.ts`](../src/infrastructure/setup_github_identity_query_adapter.ts) · [`src/infrastructure/github_secret_encryption.ts`](../src/infrastructure/github_secret_encryption.ts) · [`src/infrastructure/setup_permission_organization_owner.ts`](../src/infrastructure/setup_permission_organization_owner.ts) · [`src/data/repository/github/github_actions_resource_inspector.ts`](../src/data/repository/github/github_actions_resource_inspector.ts) · [`src/data/repository/github/github_actions_resource_commands.ts`](../src/data/repository/github/github_actions_resource_commands.ts) · [`src/data/repository/github/github_actions_resource_collection.ts`](../src/data/repository/github/github_actions_resource_collection.ts) · [`src/application/usecases/setup/resolve_setup_workflow_pat_conflict_use_case.ts`](../src/application/usecases/setup/resolve_setup_workflow_pat_conflict_use_case.ts) · [`src/cli/setup_workflow_pat_storage_copy.ts`](../src/cli/setup_workflow_pat_storage_copy.ts) · [`src/cli/commands/setup_policy.ts`](../src/cli/commands/setup_policy.ts) · [`src/application/usecases/execution/execution_issue_number_policy.ts`](../src/application/usecases/execution/execution_issue_number_policy.ts) · [`src/application/usecases/execution/setup_execution_workflow.ts`](../src/application/usecases/execution/setup_execution_workflow.ts)
-- Tests: [`src/data/repository/__tests__/repository_version_tags_query_repository.test.ts`](../src/data/repository/__tests__/repository_version_tags_query_repository.test.ts) · [`src/cli/__tests__/setup_command_options.test.ts`](../src/cli/__tests__/setup_command_options.test.ts) · [`src/data/repository/__tests__/setup_variable_write_failures.test.ts`](../src/data/repository/__tests__/setup_variable_write_failures.test.ts) · [`src/infrastructure/composition/__tests__/initial_setup_composition_root.test.ts`](../src/infrastructure/composition/__tests__/initial_setup_composition_root.test.ts) · [`src/application/policies/__tests__/setup_questionnaire_policy.test.ts`](../src/application/policies/__tests__/setup_questionnaire_policy.test.ts) · [`src/application/policies/__tests__/setup_configuration_policy.test.ts`](../src/application/policies/__tests__/setup_configuration_policy.test.ts) · [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/application/policies/__tests__/setup_remote_facts_policy.test.ts`](../src/application/policies/__tests__/setup_remote_facts_policy.test.ts) · [`src/application/policies/__tests__/setup_doctor_message_catalog.test.ts`](../src/application/policies/__tests__/setup_doctor_message_catalog.test.ts) · [`src/application/policies/__tests__/setup_doctor_report_policy.test.ts`](../src/application/policies/__tests__/setup_doctor_report_policy.test.ts) · [`src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts`](../src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts) · [`src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_session_coordinator.test.ts`](../src/application/usecases/setup/__tests__/setup_session_coordinator.test.ts) · [`src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts) · [`src/application/usecases/setup/__tests__/doctor_use_case.test.ts`](../src/application/usecases/setup/__tests__/doctor_use_case.test.ts) · [`src/application/usecases/setup/__tests__/merge_queue_readiness_use_case.test.ts`](../src/application/usecases/setup/__tests__/merge_queue_readiness_use_case.test.ts) · [`src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts`](../src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts) · [`src/application/usecases/actions/__tests__/setup_resource_provisioning.test.ts`](../src/application/usecases/actions/__tests__/setup_resource_provisioning.test.ts) · [`src/infrastructure/__tests__/setup_workspace_adapter.test.ts`](../src/infrastructure/__tests__/setup_workspace_adapter.test.ts) · [`src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts`](../src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts) · [`src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts) · [`src/infrastructure/__tests__/setup_permission_resource_probes.test.ts`](../src/infrastructure/__tests__/setup_permission_resource_probes.test.ts) · [`src/infrastructure/__tests__/setup_projects_probe_page_policy.test.ts`](../src/infrastructure/__tests__/setup_projects_probe_page_policy.test.ts) · [`src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts`](../src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts) · [`src/data/repository/__tests__/repository_variables_repository.test.ts`](../src/data/repository/__tests__/repository_variables_repository.test.ts) · [`src/cli/__tests__/setup_presenters.test.ts`](../src/cli/__tests__/setup_presenters.test.ts) · [`src/cli/__tests__/setup_journey_presenter.test.ts`](../src/cli/__tests__/setup_journey_presenter.test.ts) · [`src/cli/__tests__/setup_prompt_rendering.test.ts`](../src/cli/__tests__/setup_prompt_rendering.test.ts) · [`src/cli/__tests__/setup_token_permission_presenter.test.ts`](../src/cli/__tests__/setup_token_permission_presenter.test.ts) · [`src/__tests__/cli.test.ts`](../src/__tests__/cli.test.ts) · [`src/cli/__tests__/setup_terminal_driver.test.ts`](../src/cli/__tests__/setup_terminal_driver.test.ts) · [`src/architecture/__tests__/setup_doctor_boundaries.test.ts`](../src/architecture/__tests__/setup_doctor_boundaries.test.ts) · [`src/tooling/__tests__/documentation_pat_exception_policy.test.ts`](../src/tooling/__tests__/documentation_pat_exception_policy.test.ts) · [`src/infrastructure/__tests__/setup_pat_audit.test.ts`](../src/infrastructure/__tests__/setup_pat_audit.test.ts) · [`src/infrastructure/__tests__/setup_permission_projects_access.test.ts`](../src/infrastructure/__tests__/setup_permission_projects_access.test.ts) · [`src/infrastructure/__tests__/setup_credential_validation_adapter.test.ts`](../src/infrastructure/__tests__/setup_credential_validation_adapter.test.ts) · [`src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts) · [`src/infrastructure/__tests__/git_commit_adapter.test.ts`](../src/infrastructure/__tests__/git_commit_adapter.test.ts) · [`src/actions/__tests__/github_action_input.test.ts`](../src/actions/__tests__/github_action_input.test.ts) · [`src/infrastructure/__tests__/setup_permission_cleanup_contract.test.ts`](../src/infrastructure/__tests__/setup_permission_cleanup_contract.test.ts) · [`src/infrastructure/__tests__/setup_permission_actions_cleanup_timing.test.ts`](../src/infrastructure/__tests__/setup_permission_actions_cleanup_timing.test.ts) · [`src/infrastructure/__tests__/setup_permission_operator_actions.test.ts`](../src/infrastructure/__tests__/setup_permission_operator_actions.test.ts) · [`src/infrastructure/__tests__/setup_permission_journal_contract.test.ts`](../src/infrastructure/__tests__/setup_permission_journal_contract.test.ts) · [`src/infrastructure/__tests__/setup_permission_audit_transactions.test.ts`](../src/infrastructure/__tests__/setup_permission_audit_transactions.test.ts) · [`src/infrastructure/__tests__/setup_permission_write_failures.test.ts`](../src/infrastructure/__tests__/setup_permission_write_failures.test.ts) · [`src/infrastructure/__tests__/setup_permission_resource_failures.test.ts`](../src/infrastructure/__tests__/setup_permission_resource_failures.test.ts) · [`src/infrastructure/__tests__/setup_permission_boundary_failures.test.ts`](../src/infrastructure/__tests__/setup_permission_boundary_failures.test.ts) · [`src/infrastructure/__tests__/github_secret_encryption.test.ts`](../src/infrastructure/__tests__/github_secret_encryption.test.ts) · [`src/infrastructure/__tests__/setup_permission_organization_owner.test.ts`](../src/infrastructure/__tests__/setup_permission_organization_owner.test.ts) · [`src/infrastructure/__tests__/setup_pat_bootstrap_transactions.test.ts`](../src/infrastructure/__tests__/setup_pat_bootstrap_transactions.test.ts) · [`src/data/repository/__tests__/repository_actions_sdk_contract.integration.test.ts`](../src/data/repository/__tests__/repository_actions_sdk_contract.integration.test.ts) · [`src/data/repository/__tests__/repository_actions_resource_edge_cases.test.ts`](../src/data/repository/__tests__/repository_actions_resource_edge_cases.test.ts) · [`src/data/repository/__tests__/repository_selected_resource_grants.test.ts`](../src/data/repository/__tests__/repository_selected_resource_grants.test.ts) · [`src/infrastructure/__tests__/setup_permission_read_contract.test.ts`](../src/infrastructure/__tests__/setup_permission_read_contract.test.ts) · [`src/application/usecases/setup/__tests__/setup_workflow_pat_conflict.test.ts`](../src/application/usecases/setup/__tests__/setup_workflow_pat_conflict.test.ts) · [`src/application/usecases/setup/__tests__/setup_workflow_pat_credential_rotation.test.ts`](../src/application/usecases/setup/__tests__/setup_workflow_pat_credential_rotation.test.ts) · [`src/data/repository/__tests__/setup_organization_pat_inventory.test.ts`](../src/data/repository/__tests__/setup_organization_pat_inventory.test.ts) · [`src/cli/__tests__/web_setup_bot_pat_scope_conflict.test.ts`](../src/cli/__tests__/web_setup_bot_pat_scope_conflict.test.ts) · [`src/actions/__tests__/local_setup_apply_contract.test.ts`](../src/actions/__tests__/local_setup_apply_contract.test.ts) · [`src/cli/commands/__tests__/setup_policy.test.ts`](../src/cli/commands/__tests__/setup_policy.test.ts) · [`src/application/usecases/execution/__tests__/execution_issue_number_policy.test.ts`](../src/application/usecases/execution/__tests__/execution_issue_number_policy.test.ts) · [`src/application/usecases/execution/__tests__/setup_execution_workflow.test.ts`](../src/application/usecases/execution/__tests__/setup_execution_workflow.test.ts)
-- User documentation: [`README.md`](../README.md) · [`docs/how-to-use.mdx`](../docs/how-to-use.mdx) · [`docs/configuration.mdx`](../docs/configuration.mdx) · [`docs/configuration-checklist.mdx`](../docs/configuration-checklist.mdx) · [`docs/authentication.mdx`](../docs/authentication.mdx) · [`docs/development/architecture.mdx`](../docs/development/architecture.mdx) · [`docs/security-operations/operations/provisioning.mdx`](../docs/security-operations/operations/provisioning.mdx) · [`docs/security-operations/operations/troubleshooting.mdx`](../docs/security-operations/operations/troubleshooting.mdx) · [`docs/security-operations/security/credentials.mdx`](../docs/security-operations/security/credentials.mdx) · [`docs/single-actions/workflow-and-cli.mdx`](../docs/single-actions/workflow-and-cli.mdx) · [`docs/security-operations/operations/verification.mdx`](../docs/security-operations/operations/verification.mdx) · [`docs/development/setup-pat-provider-audit-2026-10-06.md`](../docs/development/setup-pat-provider-audit-2026-10-06.md)
+- Core code: [`src/domain/setup.ts`](../src/domain/setup.ts) · [`src/domain/setup_questionnaire.ts`](../src/domain/setup_questionnaire.ts) · [`src/domain/setup_token_permissions.ts`](../src/domain/setup_token_permissions.ts) · [`src/application/ports/setup_terminal_ports.ts`](../src/application/ports/setup_terminal_ports.ts) · [`src/application/ports/setup_wizard_ports.ts`](../src/application/ports/setup_wizard_ports.ts) · [`src/application/ports/branch_tag_ports.ts`](../src/application/ports/branch_tag_ports.ts) · [`src/application/policies/setup_issue_workflow_policy.ts`](../src/application/policies/setup_issue_workflow_policy.ts) · [`src/data/repository/release/repository_version_tags_query_repository.ts`](../src/data/repository/release/repository_version_tags_query_repository.ts) · [`src/application/ports/setup_token_permission_ports.ts`](../src/application/ports/setup_token_permission_ports.ts) · [`src/application/policies/setup_token_permission_evidence_policy.ts`](../src/application/policies/setup_token_permission_evidence_policy.ts) · [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/policies/setup_questionnaire_policy.ts`](../src/application/policies/setup_questionnaire_policy.ts) · [`src/application/policies/setup_configuration_plan.ts`](../src/application/policies/setup_configuration_plan.ts) · [`src/application/policies/merge_setup_overrides_policy.ts`](../src/application/policies/merge_setup_overrides_policy.ts) · [`src/application/policies/setup_configuration_storage_policy.ts`](../src/application/policies/setup_configuration_storage_policy.ts) · [`src/application/policies/setup_doctor_message_catalog.ts`](../src/application/policies/setup_doctor_message_catalog.ts) · [`src/application/policies/setup_doctor_report_policy.ts`](../src/application/policies/setup_doctor_report_policy.ts) · [`src/application/policies/setup_journey_policy.ts`](../src/application/policies/setup_journey_policy.ts) · [`src/application/policies/setup_permission_summary_policy.ts`](../src/application/policies/setup_permission_summary_policy.ts) · [`src/application/usecases/setup/setup_wizard_use_case.ts`](../src/application/usecases/setup/setup_wizard_use_case.ts) · [`src/application/usecases/setup/setup_journey_use_case.ts`](../src/application/usecases/setup/setup_journey_use_case.ts) · [`src/application/usecases/setup/setup_session_coordinator.ts`](../src/application/usecases/setup/setup_session_coordinator.ts) · [`src/application/usecases/setup/setup_questionnaire_controller.ts`](../src/application/usecases/setup/setup_questionnaire_controller.ts) · [`src/application/usecases/setup/setup_credentials_use_case.ts`](../src/application/usecases/setup/setup_credentials_use_case.ts) · [`src/application/usecases/setup/setup_token_permissions_use_case.ts`](../src/application/usecases/setup/setup_token_permissions_use_case.ts) · [`src/application/usecases/setup/doctor_use_case.ts`](../src/application/usecases/setup/doctor_use_case.ts) · [`src/application/usecases/setup/merge_queue_readiness_use_case.ts`](../src/application/usecases/setup/merge_queue_readiness_use_case.ts) · [`src/application/usecases/actions/initial_setup_workflow.ts`](../src/application/usecases/actions/initial_setup_workflow.ts) · [`src/application/usecases/actions/setup_resource_provisioning.ts`](../src/application/usecases/actions/setup_resource_provisioning.ts) · [`src/application/ports/message_catalog_ports.ts`](../src/application/ports/message_catalog_ports.ts) · [`src/application/usecases/localization/resolve_message_catalog_use_case.ts`](../src/application/usecases/localization/resolve_message_catalog_use_case.ts) · [`src/application/policies/setup_configuration_validation.ts`](../src/application/policies/setup_configuration_validation.ts) · [`src/infrastructure/setup_workspace_adapter.ts`](../src/infrastructure/setup_workspace_adapter.ts) · [`src/data/repository/repository_variables_repository.ts`](../src/data/repository/repository_variables_repository.ts) · [`src/infrastructure/github/ports/github_repository_variables_protocol.ts`](../src/infrastructure/github/ports/github_repository_variables_protocol.ts) · [`src/infrastructure/setup_remote_credential_health_adapter.ts`](../src/infrastructure/setup_remote_credential_health_adapter.ts) · [`src/infrastructure/setup_credential_validation_adapter.ts`](../src/infrastructure/setup_credential_validation_adapter.ts) · [`src/infrastructure/setup_token_permission_query_adapter.ts`](../src/infrastructure/setup_token_permission_query_adapter.ts) · [`src/infrastructure/setup_permission_actions_probe.ts`](../src/infrastructure/setup_permission_actions_probe.ts) · [`src/infrastructure/setup_permission_issue_probe.ts`](../src/infrastructure/setup_permission_issue_probe.ts) · [`src/infrastructure/setup_permission_issue_cleanup.ts`](../src/infrastructure/setup_permission_issue_cleanup.ts) · [`src/infrastructure/setup_permission_probe_journal.ts`](../src/infrastructure/setup_permission_probe_journal.ts) · [`src/infrastructure/setup_permission_probe_transaction.ts`](../src/infrastructure/setup_permission_probe_transaction.ts) · [`src/infrastructure/setup_permission_resource_probes.ts`](../src/infrastructure/setup_permission_resource_probes.ts) · [`src/infrastructure/setup_projects_probe_page_policy.ts`](../src/infrastructure/setup_projects_probe_page_policy.ts) · [`src/cli/setup_terminal_driver.ts`](../src/cli/setup_terminal_driver.ts) · [`src/cli/setup_question_renderer.ts`](../src/cli/setup_question_renderer.ts) · [`src/cli/setup_journey_presenter.ts`](../src/cli/setup_journey_presenter.ts) · [`src/cli/setup_plan_presenter.ts`](../src/cli/setup_plan_presenter.ts) · [`src/cli/setup_doctor_presenter.ts`](../src/cli/setup_doctor_presenter.ts) · [`src/cli/setup_prompt_rendering.ts`](../src/cli/setup_prompt_rendering.ts) · [`src/cli/setup_credential_prompt_adapter.ts`](../src/cli/setup_credential_prompt_adapter.ts) · [`src/cli/setup_credential_collection.ts`](../src/cli/setup_credential_collection.ts) · [`src/cli/commands/setup_execution.ts`](../src/cli/commands/setup_execution.ts) · [`src/cli/setup_token_permission_presenter.ts`](../src/cli/setup_token_permission_presenter.ts) · [`src/infrastructure/composition/setup_credentials_composition_root.ts`](../src/infrastructure/composition/setup_credentials_composition_root.ts) · [`src/infrastructure/composition/setup_token_permissions_composition_root.ts`](../src/infrastructure/composition/setup_token_permissions_composition_root.ts) · [`src/infrastructure/composition/setup_doctor_composition_root.ts`](../src/infrastructure/composition/setup_doctor_composition_root.ts) · [`scripts/coverage-budgets.json`](../scripts/coverage-budgets.json) · [`scripts/documentation_pat_exception_policy.cjs`](../scripts/documentation_pat_exception_policy.cjs) · [`scripts/validate-documentation-contract.cjs`](../scripts/validate-documentation-contract.cjs) · [`src/infrastructure/http_deadline.ts`](../src/infrastructure/http_deadline.ts) · [`src/infrastructure/setup_permission_denial.ts`](../src/infrastructure/setup_permission_denial.ts) · [`src/infrastructure/setup_permission_read_probe.ts`](../src/infrastructure/setup_permission_read_probe.ts) · [`src/infrastructure/setup_permission_read_evidence.ts`](../src/infrastructure/setup_permission_read_evidence.ts) · [`src/infrastructure/setup_permission_read_shape.ts`](../src/infrastructure/setup_permission_read_shape.ts) · [`src/infrastructure/setup_permission_projects_read.ts`](../src/infrastructure/setup_permission_projects_read.ts) · [`src/infrastructure/setup_permission_projects_access.ts`](../src/infrastructure/setup_permission_projects_access.ts) · [`src/infrastructure/setup_permission_workflow_cleanup.ts`](../src/infrastructure/setup_permission_workflow_cleanup.ts) · [`src/infrastructure/setup_permission_project_cleanup.ts`](../src/infrastructure/setup_permission_project_cleanup.ts) · [`src/infrastructure/setup_permission_probe_http.ts`](../src/infrastructure/setup_permission_probe_http.ts) · [`src/infrastructure/setup_permission_pull_request_probe.ts`](../src/infrastructure/setup_permission_pull_request_probe.ts) · [`src/infrastructure/git_authentication_environment.ts`](../src/infrastructure/git_authentication_environment.ts) · [`src/actions/github_action_input.ts`](../src/actions/github_action_input.ts) · [`src/infrastructure/setup_github_identity_query_adapter.ts`](../src/infrastructure/setup_github_identity_query_adapter.ts) · [`src/infrastructure/github_secret_encryption.ts`](../src/infrastructure/github_secret_encryption.ts) · [`src/infrastructure/setup_permission_organization_owner.ts`](../src/infrastructure/setup_permission_organization_owner.ts) · [`src/data/repository/github/github_actions_resource_inspector.ts`](../src/data/repository/github/github_actions_resource_inspector.ts) · [`src/data/repository/github/github_actions_resource_commands.ts`](../src/data/repository/github/github_actions_resource_commands.ts) · [`src/data/repository/github/github_actions_resource_collection.ts`](../src/data/repository/github/github_actions_resource_collection.ts) · [`src/application/usecases/setup/resolve_setup_workflow_pat_conflict_use_case.ts`](../src/application/usecases/setup/resolve_setup_workflow_pat_conflict_use_case.ts) · [`src/cli/setup_workflow_pat_storage_copy.ts`](../src/cli/setup_workflow_pat_storage_copy.ts) · [`src/cli/commands/setup_policy.ts`](../src/cli/commands/setup_policy.ts) · [`src/application/usecases/execution/execution_issue_number_policy.ts`](../src/application/usecases/execution/execution_issue_number_policy.ts) · [`src/application/usecases/execution/setup_execution_workflow.ts`](../src/application/usecases/execution/setup_execution_workflow.ts) · [`src/domain/github_runtime_doctor.ts`](../src/domain/github_runtime_doctor.ts) · [`src/domain/copilot_command.ts`](../src/domain/copilot_command.ts) · [`src/application/usecases/comment_automation_command_workflow.ts`](../src/application/usecases/comment_automation_command_workflow.ts) · [`src/application/policies/copilot_interaction_policy.ts`](../src/application/policies/copilot_interaction_policy.ts) · [`src/application/policies/publication_message_catalog.ts`](../src/application/policies/publication_message_catalog.ts)
+- Tests: [`src/data/repository/__tests__/repository_version_tags_query_repository.test.ts`](../src/data/repository/__tests__/repository_version_tags_query_repository.test.ts) · [`src/cli/__tests__/setup_command_options.test.ts`](../src/cli/__tests__/setup_command_options.test.ts) · [`src/data/repository/__tests__/setup_variable_write_failures.test.ts`](../src/data/repository/__tests__/setup_variable_write_failures.test.ts) · [`src/infrastructure/composition/__tests__/initial_setup_composition_root.test.ts`](../src/infrastructure/composition/__tests__/initial_setup_composition_root.test.ts) · [`src/application/policies/__tests__/setup_questionnaire_policy.test.ts`](../src/application/policies/__tests__/setup_questionnaire_policy.test.ts) · [`src/application/policies/__tests__/setup_configuration_policy.test.ts`](../src/application/policies/__tests__/setup_configuration_policy.test.ts) · [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/application/policies/__tests__/setup_remote_facts_policy.test.ts`](../src/application/policies/__tests__/setup_remote_facts_policy.test.ts) · [`src/application/policies/__tests__/setup_doctor_message_catalog.test.ts`](../src/application/policies/__tests__/setup_doctor_message_catalog.test.ts) · [`src/application/policies/__tests__/setup_doctor_report_policy.test.ts`](../src/application/policies/__tests__/setup_doctor_report_policy.test.ts) · [`src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts`](../src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts) · [`src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_session_coordinator.test.ts`](../src/application/usecases/setup/__tests__/setup_session_coordinator.test.ts) · [`src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts) · [`src/application/usecases/setup/__tests__/doctor_use_case.test.ts`](../src/application/usecases/setup/__tests__/doctor_use_case.test.ts) · [`src/application/usecases/setup/__tests__/merge_queue_readiness_use_case.test.ts`](../src/application/usecases/setup/__tests__/merge_queue_readiness_use_case.test.ts) · [`src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts`](../src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts) · [`src/application/usecases/actions/__tests__/setup_resource_provisioning.test.ts`](../src/application/usecases/actions/__tests__/setup_resource_provisioning.test.ts) · [`src/infrastructure/__tests__/setup_workspace_adapter.test.ts`](../src/infrastructure/__tests__/setup_workspace_adapter.test.ts) · [`src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts`](../src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts) · [`src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts) · [`src/infrastructure/__tests__/setup_permission_resource_probes.test.ts`](../src/infrastructure/__tests__/setup_permission_resource_probes.test.ts) · [`src/infrastructure/__tests__/setup_projects_probe_page_policy.test.ts`](../src/infrastructure/__tests__/setup_projects_probe_page_policy.test.ts) · [`src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts`](../src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts) · [`src/data/repository/__tests__/repository_variables_repository.test.ts`](../src/data/repository/__tests__/repository_variables_repository.test.ts) · [`src/cli/__tests__/setup_presenters.test.ts`](../src/cli/__tests__/setup_presenters.test.ts) · [`src/cli/__tests__/setup_journey_presenter.test.ts`](../src/cli/__tests__/setup_journey_presenter.test.ts) · [`src/cli/__tests__/setup_prompt_rendering.test.ts`](../src/cli/__tests__/setup_prompt_rendering.test.ts) · [`src/cli/__tests__/setup_token_permission_presenter.test.ts`](../src/cli/__tests__/setup_token_permission_presenter.test.ts) · [`src/__tests__/cli.test.ts`](../src/__tests__/cli.test.ts) · [`src/cli/__tests__/setup_terminal_driver.test.ts`](../src/cli/__tests__/setup_terminal_driver.test.ts) · [`src/architecture/__tests__/setup_doctor_boundaries.test.ts`](../src/architecture/__tests__/setup_doctor_boundaries.test.ts) · [`src/tooling/__tests__/documentation_pat_exception_policy.test.ts`](../src/tooling/__tests__/documentation_pat_exception_policy.test.ts) · [`src/infrastructure/__tests__/setup_pat_audit.test.ts`](../src/infrastructure/__tests__/setup_pat_audit.test.ts) · [`src/infrastructure/__tests__/setup_permission_projects_access.test.ts`](../src/infrastructure/__tests__/setup_permission_projects_access.test.ts) · [`src/infrastructure/__tests__/setup_credential_validation_adapter.test.ts`](../src/infrastructure/__tests__/setup_credential_validation_adapter.test.ts) · [`src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts) · [`src/infrastructure/__tests__/git_commit_adapter.test.ts`](../src/infrastructure/__tests__/git_commit_adapter.test.ts) · [`src/actions/__tests__/github_action_input.test.ts`](../src/actions/__tests__/github_action_input.test.ts) · [`src/infrastructure/__tests__/setup_permission_cleanup_contract.test.ts`](../src/infrastructure/__tests__/setup_permission_cleanup_contract.test.ts) · [`src/infrastructure/__tests__/setup_permission_actions_cleanup_timing.test.ts`](../src/infrastructure/__tests__/setup_permission_actions_cleanup_timing.test.ts) · [`src/infrastructure/__tests__/setup_permission_operator_actions.test.ts`](../src/infrastructure/__tests__/setup_permission_operator_actions.test.ts) · [`src/infrastructure/__tests__/setup_permission_journal_contract.test.ts`](../src/infrastructure/__tests__/setup_permission_journal_contract.test.ts) · [`src/infrastructure/__tests__/setup_permission_audit_transactions.test.ts`](../src/infrastructure/__tests__/setup_permission_audit_transactions.test.ts) · [`src/infrastructure/__tests__/setup_permission_write_failures.test.ts`](../src/infrastructure/__tests__/setup_permission_write_failures.test.ts) · [`src/infrastructure/__tests__/setup_permission_resource_failures.test.ts`](../src/infrastructure/__tests__/setup_permission_resource_failures.test.ts) · [`src/infrastructure/__tests__/setup_permission_boundary_failures.test.ts`](../src/infrastructure/__tests__/setup_permission_boundary_failures.test.ts) · [`src/infrastructure/__tests__/github_secret_encryption.test.ts`](../src/infrastructure/__tests__/github_secret_encryption.test.ts) · [`src/infrastructure/__tests__/setup_permission_organization_owner.test.ts`](../src/infrastructure/__tests__/setup_permission_organization_owner.test.ts) · [`src/infrastructure/__tests__/setup_pat_bootstrap_transactions.test.ts`](../src/infrastructure/__tests__/setup_pat_bootstrap_transactions.test.ts) · [`src/data/repository/__tests__/repository_actions_sdk_contract.integration.test.ts`](../src/data/repository/__tests__/repository_actions_sdk_contract.integration.test.ts) · [`src/data/repository/__tests__/repository_actions_resource_edge_cases.test.ts`](../src/data/repository/__tests__/repository_actions_resource_edge_cases.test.ts) · [`src/data/repository/__tests__/repository_selected_resource_grants.test.ts`](../src/data/repository/__tests__/repository_selected_resource_grants.test.ts) · [`src/infrastructure/__tests__/setup_permission_read_contract.test.ts`](../src/infrastructure/__tests__/setup_permission_read_contract.test.ts) · [`src/application/usecases/setup/__tests__/setup_workflow_pat_conflict.test.ts`](../src/application/usecases/setup/__tests__/setup_workflow_pat_conflict.test.ts) · [`src/application/usecases/setup/__tests__/setup_workflow_pat_credential_rotation.test.ts`](../src/application/usecases/setup/__tests__/setup_workflow_pat_credential_rotation.test.ts) · [`src/data/repository/__tests__/setup_organization_pat_inventory.test.ts`](../src/data/repository/__tests__/setup_organization_pat_inventory.test.ts) · [`src/cli/__tests__/web_setup_bot_pat_scope_conflict.test.ts`](../src/cli/__tests__/web_setup_bot_pat_scope_conflict.test.ts) · [`src/actions/__tests__/local_setup_apply_contract.test.ts`](../src/actions/__tests__/local_setup_apply_contract.test.ts) · [`src/cli/commands/__tests__/setup_policy.test.ts`](../src/cli/commands/__tests__/setup_policy.test.ts) · [`src/application/usecases/execution/__tests__/execution_issue_number_policy.test.ts`](../src/application/usecases/execution/__tests__/execution_issue_number_policy.test.ts) · [`src/application/usecases/execution/__tests__/setup_execution_workflow.test.ts`](../src/application/usecases/execution/__tests__/setup_execution_workflow.test.ts) · [`src/domain/__tests__/github_runtime_doctor.test.ts`](../src/domain/__tests__/github_runtime_doctor.test.ts) · [`src/domain/__tests__/copilot_command.test.ts`](../src/domain/__tests__/copilot_command.test.ts) · [`src/application/usecases/__tests__/comment_automation_use_case.test.ts`](../src/application/usecases/__tests__/comment_automation_use_case.test.ts)
+- User documentation: [`README.md`](../README.md) · [`docs/how-to-use.mdx`](../docs/how-to-use.mdx) · [`docs/configuration.mdx`](../docs/configuration.mdx) · [`docs/configuration-checklist.mdx`](../docs/configuration-checklist.mdx) · [`docs/authentication.mdx`](../docs/authentication.mdx) · [`docs/development/architecture.mdx`](../docs/development/architecture.mdx) · [`docs/security-operations/operations/provisioning.mdx`](../docs/security-operations/operations/provisioning.mdx) · [`docs/security-operations/operations/troubleshooting.mdx`](../docs/security-operations/operations/troubleshooting.mdx) · [`docs/security-operations/security/credentials.mdx`](../docs/security-operations/security/credentials.mdx) · [`docs/single-actions/workflow-and-cli.mdx`](../docs/single-actions/workflow-and-cli.mdx) · [`docs/security-operations/operations/verification.mdx`](../docs/security-operations/operations/verification.mdx) · [`docs/development/setup-pat-provider-audit-2026-10-06.md`](../docs/development/setup-pat-provider-audit-2026-10-06.md) · [`docs/issues/comment-commands.mdx`](../docs/issues/comment-commands.mdx)
### `local-web-setup-assistant` — Local web setup assistant
diff --git a/specs/catalog.json b/specs/catalog.json
index b8da647dd..6bcd945d8 100644
--- a/specs/catalog.json
+++ b/specs/catalog.json
@@ -127,7 +127,8 @@
"src/infrastructure/github/ports/github_issue_provider_ports.ts",
"src/application/usecases/comment_automation_command_workflow.ts",
"src/application/usecases/steps/commit/bugbot/dismiss_bugbot_findings_use_case.ts",
- "src/application/policies/inactivity_result_counts_projection_policy.ts"
+ "src/application/policies/inactivity_result_counts_projection_policy.ts",
+ "src/domain/copilot_command.ts"
],
"tests": [
"src/domain/__tests__/implementation_plan.test.ts",
@@ -749,7 +750,12 @@
"src/cli/setup_workflow_pat_storage_copy.ts",
"src/cli/commands/setup_policy.ts",
"src/application/usecases/execution/execution_issue_number_policy.ts",
- "src/application/usecases/execution/setup_execution_workflow.ts"
+ "src/application/usecases/execution/setup_execution_workflow.ts",
+ "src/domain/github_runtime_doctor.ts",
+ "src/domain/copilot_command.ts",
+ "src/application/usecases/comment_automation_command_workflow.ts",
+ "src/application/policies/copilot_interaction_policy.ts",
+ "src/application/policies/publication_message_catalog.ts"
],
"tests": [
"src/data/repository/__tests__/repository_version_tags_query_repository.test.ts",
@@ -815,7 +821,10 @@
"src/actions/__tests__/local_setup_apply_contract.test.ts",
"src/cli/commands/__tests__/setup_policy.test.ts",
"src/application/usecases/execution/__tests__/execution_issue_number_policy.test.ts",
- "src/application/usecases/execution/__tests__/setup_execution_workflow.test.ts"
+ "src/application/usecases/execution/__tests__/setup_execution_workflow.test.ts",
+ "src/domain/__tests__/github_runtime_doctor.test.ts",
+ "src/domain/__tests__/copilot_command.test.ts",
+ "src/application/usecases/__tests__/comment_automation_use_case.test.ts"
],
"documentation": [
"README.md",
@@ -829,7 +838,8 @@
"docs/security-operations/security/credentials.mdx",
"docs/single-actions/workflow-and-cli.mdx",
"docs/security-operations/operations/verification.mdx",
- "docs/development/setup-pat-provider-audit-2026-10-06.md"
+ "docs/development/setup-pat-provider-audit-2026-10-06.md",
+ "docs/issues/comment-commands.mdx"
]
},
{
diff --git a/specs/github-runtime-doctor-command.md b/specs/github-runtime-doctor-command.md
index e3407cb3a..6c47178ab 100644
--- a/specs/github-runtime-doctor-command.md
+++ b/specs/github-runtime-doctor-command.md
@@ -31,6 +31,7 @@ Facts supported by the issue: the prior live round produced a policy-unavailable
### 2.3 Evidence
- Code/workflow/documentation references: issue #461 identifies the existing setup/doctor policies, comment-automation, semantic GitHub communication, issue-start-and-sdd-readiness, `specs/catalog.json`, and the feature workflow as the relevant owners and sources. Exact file paths and contract details require repository verification.
+- Verified implementation baseline on 2026-10-11: `src/domain/github_runtime_doctor.ts` contains the fixed seven-group contract, trusted approval-policy parsing, and deterministic aggregate priority. `/copilot doctor` is recognized and listed in English/Spanish help, but the comment route intentionally returns a sanitized unavailable error until evidence adapters and correlated rendering are composed. No runtime evidence group is currently complete.
- Incidents or concrete examples: a live recommendation assessment reported policy unavailable although the policy was valid; investigation required workflow logs and local doctor.
- External primary sources: not required to define this repository behavior.
- Unknowns: current concrete port APIs, workflow contract shape, existing test commands and thresholds, and documentation route names must be confirmed before implementation.
@@ -332,6 +333,14 @@ Document that local setup operator PAT and Action runtime PAT are distinct and t
## 19. Definition of Done
+Implementation status: **incomplete**. The current code covers the domain contract,
+trusted strict policy parsing, aggregate ordering/priority, command recognition,
+and bilingual help discoverability. The route fails closed with an unavailable
+response; it does not claim that any runtime prerequisite passed. This partial
+state does not satisfy the numeric 52-case budget or the capability acceptance
+scenarios below. Do not mark this specification implemented or its catalog
+capability complete until every unchecked item is verified.
+
- [ ] Every normative requirement has acceptance and traceability.
- [ ] Architecture boundaries are implemented and automatically enforced; doctor has no mutation or model capability.
- [ ] Numeric 52-case minimum, distribution, coverage requirements, and repository budgets pass.
@@ -350,4 +359,4 @@ Document that local setup operator PAT and Action runtime PAT are distinct and t
- Primary sources: Issue #461 and supplied answers Q1–Q7; repository standard and template as provided in the application task.
- Related specs/ADRs/issues/PRs: setup-and-doctor owning specification; comment-automation; semantic GitHub communication; issue-start-and-sdd-readiness; exact paths and current contract details to be verified in repository.
- Decisions: preserve existing owning contract and use a focused companion; no new configuration or permission; independent partial reads; blocked/unavailable distinction; exact PR identity; one correlated localized reply.
-- Follow-up work outside this spec: any repair workflow, credential write testing, local setup provisioning, or broader credential-health certification.
\ No newline at end of file
+- Follow-up work outside this spec: any repair workflow, credential write testing, local setup provisioning, or broader credential-health certification.
diff --git a/src/application/policies/copilot_interaction_policy.ts b/src/application/policies/copilot_interaction_policy.ts
index f6515834a..ab28de8ee 100644
--- a/src/application/policies/copilot_interaction_policy.ts
+++ b/src/application/policies/copilot_interaction_policy.ts
@@ -23,6 +23,7 @@ const READ_ONLY_COMMANDS: readonly [string, PublicationMessageId][] = Object.fre
['/copilot recheck', 'interaction.help.command.recheck'],
['/copilot description', 'interaction.help.command.description'],
['/copilot status', 'interaction.help.command.status'],
+ ['/copilot doctor', 'interaction.help.command.doctor'],
]);
const CHANGE_COMMANDS: readonly [string, PublicationMessageId][] = Object.freeze([
diff --git a/src/application/policies/publication_message_catalog.ts b/src/application/policies/publication_message_catalog.ts
index 37f8e04e6..b46889533 100644
--- a/src/application/policies/publication_message_catalog.ts
+++ b/src/application/policies/publication_message_catalog.ts
@@ -53,6 +53,7 @@ const PUBLICATION_SURFACE_MESSAGE_IDS = Object.freeze([
'interaction.help.command.recheck',
'interaction.help.command.description',
'interaction.help.command.status',
+ 'interaction.help.command.doctor',
'interaction.help.command.fixOne',
'interaction.help.command.fixAll',
'interaction.help.command.dismiss',
@@ -194,6 +195,7 @@ const ENGLISH_MESSAGES: Readonly> =
'interaction.help.command.recheck': 're-run the review and reconcile findings.',
'interaction.help.command.description': 'refresh the pull-request description.',
'interaction.help.command.status': 'show the current automation status.',
+ 'interaction.help.command.doctor': 'check available runtime prerequisites without making repository changes.',
'interaction.help.command.fixOne': 'fix one reported finding.',
'interaction.help.command.fixAll': 'fix all unresolved findings.',
'interaction.help.command.dismiss': 'dismiss a finding.',
@@ -302,6 +304,7 @@ const SPANISH_MESSAGES: Readonly> =
'interaction.help.command.recheck': 'repite la revisión y reconcilia los hallazgos.',
'interaction.help.command.description': 'actualiza la descripción del pull request.',
'interaction.help.command.status': 'muestra el estado actual de la automatización.',
+ 'interaction.help.command.doctor': 'comprueba los requisitos disponibles del runtime sin modificar el repositorio.',
'interaction.help.command.fixOne': 'corrige un hallazgo.',
'interaction.help.command.fixAll': 'corrige todos los hallazgos sin resolver.',
'interaction.help.command.dismiss': 'descarta un hallazgo.',
diff --git a/src/application/usecases/__tests__/comment_automation_use_case.test.ts b/src/application/usecases/__tests__/comment_automation_use_case.test.ts
index 1a8f70311..407e38ddc 100644
--- a/src/application/usecases/__tests__/comment_automation_use_case.test.ts
+++ b/src/application/usecases/__tests__/comment_automation_use_case.test.ts
@@ -853,6 +853,32 @@ describe("runCommentAutomation", () => {
});
});
+ it('keeps doctor deterministic and fails closed without invoking language or agent capabilities', async () => {
+ const language = { invoke: jest.fn() };
+ const think = { invoke: jest.fn() };
+ const results = await runCommentAutomation(
+ { owner: 'o', repo: 'r', actor: 'actor', tokens: { token: 't' } } as Execution,
+ {
+ taskId: 'CommentAutomation',
+ languageUseCase: language as never,
+ intentUseCase: {} as never,
+ thinkUseCase: think as never,
+ autofixUseCase: {} as never,
+ doUserRequestUseCase: {} as never,
+ userComment: '/copilot doctor',
+ bugbotGitMutationPort: {} as never,
+ },
+ {} as never,
+ {} as never,
+ );
+ expect(results[0]).toMatchObject({
+ id: 'CommentAutomation.Doctor', success: false, executed: true,
+ });
+ expect(results[0].errors[0].message).not.toContain('token');
+ expect(language.invoke).not.toHaveBeenCalled();
+ expect(think.invoke).not.toHaveBeenCalled();
+ });
+
it('rejects an invalid explicit command without invoking an agent', async () => {
const think = { invoke: jest.fn() };
const results = await runCommentAutomation(
diff --git a/src/application/usecases/comment_automation_command_workflow.ts b/src/application/usecases/comment_automation_command_workflow.ts
index 3780883fe..2b93b0499 100644
--- a/src/application/usecases/comment_automation_command_workflow.ts
+++ b/src/application/usecases/comment_automation_command_workflow.ts
@@ -26,6 +26,15 @@ export async function runExplicitCommentCommand(
actorAuthorizationPort: BoundActorAuthorizationPort,
): Promise {
if (command.name === 'help') return runHelpCommand(param, options);
+ if (command.name === 'doctor') return [new Result({
+ id: `${options.taskId}.Doctor`,
+ success: false,
+ executed: true,
+ errors: [new ApplicationError(
+ 'configuration.unsupported',
+ 'Runtime doctor evidence is not available in this workflow composition yet.',
+ )],
+ })];
if (command.name === 'status') return [await readCopilotStatus(param, options.taskId, options.statusReadPorts)];
if (command.name === 'dismiss') return runDismissCommand(param, options, command, actorAuthorizationPort);
if (command.name === 'remember') return runRememberCommand(param, options, command, actorAuthorizationPort);
diff --git a/src/domain/__tests__/copilot_command.test.ts b/src/domain/__tests__/copilot_command.test.ts
index 14fb865af..a4c212489 100644
--- a/src/domain/__tests__/copilot_command.test.ts
+++ b/src/domain/__tests__/copilot_command.test.ts
@@ -16,6 +16,7 @@ describe('Copilot command policy', () => {
['/copilot implement add a regression test', 'implement'],
['/copilot review security regression', 'review'],
['/copilot status', 'status'],
+ ['/copilot doctor', 'doctor'],
['/copilot description', 'description'],
['/copilot fix FINDING-1 FINDING-2', 'fix'],
['/copilot sync-branch --dry-run', 'sync-branch'],
diff --git a/src/domain/__tests__/github_runtime_doctor.test.ts b/src/domain/__tests__/github_runtime_doctor.test.ts
index 538d6a27b..8fd67cc5f 100644
--- a/src/domain/__tests__/github_runtime_doctor.test.ts
+++ b/src/domain/__tests__/github_runtime_doctor.test.ts
@@ -1,4 +1,6 @@
import {
+ aggregateGithubRuntimeDoctorReport,
+ GITHUB_RUNTIME_DOCTOR_GROUPS,
parseTrustedDoctorApprovalPolicy,
type TrustedApprovalPolicyRead,
} from '../github_runtime_doctor';
@@ -11,6 +13,40 @@ const validPolicy = {
};
describe('parseTrustedDoctorApprovalPolicy', () => {
+ it('orders all groups and chooses the first action at the highest severity', () => {
+ const report = aggregateGithubRuntimeDoctorReport({
+ target: { kind: 'issue', repositoryId: 42, issueNumber: 7 },
+ checks: [
+ { group: 'effective-configuration', state: 'pass' },
+ { group: 'approval-policy', state: 'blocked', action: 'Correct policy' },
+ { group: 'workflow-contracts', state: 'unavailable', action: 'Retry' },
+ ],
+ });
+ expect(report.checks.map(check => check.group)).toEqual(GITHUB_RUNTIME_DOCTOR_GROUPS);
+ expect(report.checks.filter(check => check.state === 'unavailable')).toHaveLength(5);
+ expect(report.severity).toBe('blocked');
+ expect(report.primaryAction).toBe('Correct policy');
+ });
+
+ it('excludes not-applicable checks and handles an all-not-applicable report', () => {
+ const report = aggregateGithubRuntimeDoctorReport({
+ target: { kind: 'issue', repositoryId: 42, issueNumber: 7 },
+ checks: GITHUB_RUNTIME_DOCTOR_GROUPS.map(group => ({ group, state: 'not-applicable' as const })),
+ });
+ expect(report.severity).toBe('not-applicable');
+ expect(report.primaryAction).toBeUndefined();
+ });
+
+ it('rejects duplicate groups instead of discarding evidence', () => {
+ expect(() => aggregateGithubRuntimeDoctorReport({
+ target: { kind: 'issue', repositoryId: 42, issueNumber: 7 },
+ checks: [
+ { group: 'approval-policy', state: 'pass' },
+ { group: 'approval-policy', state: 'blocked' },
+ ],
+ })).toThrow('Duplicate GitHub runtime doctor group');
+ });
+
it('parses a valid repository policy from the trusted effective read', () => {
const read: TrustedApprovalPolicyRead = {
status: 'available', source: 'repository', value: JSON.stringify(validPolicy),
diff --git a/src/domain/copilot_command.ts b/src/domain/copilot_command.ts
index 1aa4f9daa..736acd099 100644
--- a/src/domain/copilot_command.ts
+++ b/src/domain/copilot_command.ts
@@ -7,6 +7,7 @@ export const COPILOT_COMMAND_NAMES = [
'estimate',
'test-plan',
'status',
+ 'doctor',
'description',
'explain',
'diagnose',
diff --git a/src/domain/github_runtime_doctor.ts b/src/domain/github_runtime_doctor.ts
index 5bcb13aeb..225dbd0da 100644
--- a/src/domain/github_runtime_doctor.ts
+++ b/src/domain/github_runtime_doctor.ts
@@ -4,8 +4,7 @@ import {
type PullRequestApprovalPolicy,
} from './pull_request_approval_policy';
-// Domain contracts and trusted policy parsing are implemented here. Application
-// orchestration, adapters, command routing, rendering, and reply publication remain pending.
+// Domain contracts and trusted policy parsing for the issue/PR runtime doctor.
/** The fixed, ordered evidence groups in the GitHub runtime doctor contract. */
export const GITHUB_RUNTIME_DOCTOR_GROUPS = [
@@ -72,3 +71,28 @@ export function parseTrustedDoctorApprovalPolicy(read: TrustedApprovalPolicyRead
return { status: 'invalid' };
}
}
+
+/** Aggregate explicit independent evidence. Missing groups are unavailable, never assumed healthy. */
+export function aggregateGithubRuntimeDoctorReport(input: {
+ readonly target: GithubRuntimeDoctorTarget;
+ readonly checks: readonly GithubRuntimeDoctorCheck[];
+}): GithubRuntimeDoctorReport {
+ const byGroup = new Map();
+ for (const check of input.checks) {
+ if (byGroup.has(check.group)) throw new Error(`Duplicate GitHub runtime doctor group: ${check.group}.`);
+ byGroup.set(check.group, Object.freeze({ ...check }));
+ }
+ const checks: readonly GithubRuntimeDoctorCheck[] = GITHUB_RUNTIME_DOCTOR_GROUPS.map(group => byGroup.get(group)
+ ?? Object.freeze({ group, state: 'unavailable' as const }));
+ const priority: readonly GithubRuntimeDoctorSeverity[] = ['blocked', 'unavailable', 'warning', 'pass'];
+ const applicable = checks.filter(check => check.state !== 'not-applicable');
+ const severity = applicable.length === 0 ? 'not-applicable'
+ : priority.find(state => applicable.some(check => check.state === state)) ?? 'unavailable';
+ const primaryAction = applicable.find(check => check.state === severity && check.action)?.action;
+ return Object.freeze({
+ target: Object.freeze({ ...input.target }),
+ checks: Object.freeze(checks),
+ severity,
+ ...(primaryAction ? { primaryAction } : {}),
+ });
+}
From e3f183f1f1e72ce7a2d2e81ba698e6ca04b21af4 Mon Sep 17 00:00:00 2001
From: Efra Espada
Date: Sun, 11 Oct 2026 18:42:55 +0200
Subject: [PATCH 04/23] feat(issue): publish deterministic read-only runtime
doctor (#461)
---
build/cli/index.js | 1298 ++++++++++++++++-
build/github_action/index.js | 766 +++++++++-
docs/issues/comment-commands.mdx | 32 +-
specs/CATALOG.md | 10 +-
specs/catalog.json | 24 +-
specs/github-runtime-doctor-command.md | 36 +-
...up-configuration-credentials-and-doctor.md | 18 +
.../github_runtime_doctor_policy.test.ts | 46 +
...runtime_doctor_presentation_policy.test.ts | 60 +
.../policies/github_runtime_doctor_policy.ts | 68 +
...thub_runtime_doctor_presentation_policy.ts | 64 +
.../policies/publication_message_catalog.ts | 105 ++
.../semantic_result_publication_policy.ts | 10 +-
.../ports/github_runtime_doctor_ports.ts | 33 +
.../comment_automation_use_case.test.ts | 8 +-
.../fixtures/runtime_doctor_fixture.ts | 31 +
.../__tests__/issue_comment_use_case.test.ts | 35 +
.../read_copilot_doctor_workflow.test.ts | 113 ++
...untime_doctor_security_integration.test.ts | 52 +
.../comment_automation_command_workflow.ts | 11 +-
.../usecases/comment_automation_context.ts | 26 +
.../usecases/comment_automation_contracts.ts | 2 +
.../usecases/comment_automation_use_case.ts | 2 +-
.../usecases/issue_comment_use_case.ts | 10 +-
.../pull_request_review_comment_use_case.ts | 3 +
.../usecases/read_copilot_doctor_workflow.ts | 93 ++
.../runtime_doctor_repository.test.ts | 114 ++
.../github/runtime_doctor_repository.ts | 91 ++
.../pull_request_approval_repository.test.ts | 38 +
.../pull_request_approval_repository.ts | 49 +-
src/domain/copilot_command.ts | 3 +
src/domain/github_runtime_doctor.ts | 2 +-
...ub_runtime_doctor_composition_root.test.ts | 27 +
.../main_run_route_composition_root.test.ts | 4 +
.../github_runtime_doctor_composition_root.ts | 16 +
.../main_run_route_composition_root.ts | 3 +
36 files changed, 3213 insertions(+), 90 deletions(-)
create mode 100644 src/application/policies/__tests__/github_runtime_doctor_policy.test.ts
create mode 100644 src/application/policies/__tests__/github_runtime_doctor_presentation_policy.test.ts
create mode 100644 src/application/policies/github_runtime_doctor_policy.ts
create mode 100644 src/application/policies/github_runtime_doctor_presentation_policy.ts
create mode 100644 src/application/ports/github_runtime_doctor_ports.ts
create mode 100644 src/application/usecases/__tests__/fixtures/runtime_doctor_fixture.ts
create mode 100644 src/application/usecases/__tests__/read_copilot_doctor_workflow.test.ts
create mode 100644 src/application/usecases/__tests__/runtime_doctor_security_integration.test.ts
create mode 100644 src/application/usecases/read_copilot_doctor_workflow.ts
create mode 100644 src/data/repository/github/__tests__/runtime_doctor_repository.test.ts
create mode 100644 src/data/repository/github/runtime_doctor_repository.ts
create mode 100644 src/infrastructure/composition/__tests__/github_runtime_doctor_composition_root.test.ts
create mode 100644 src/infrastructure/composition/github_runtime_doctor_composition_root.ts
diff --git a/build/cli/index.js b/build/cli/index.js
index ed55ff015..6bdb38f9c 100755
--- a/build/cli/index.js
+++ b/build/cli/index.js
@@ -40552,6 +40552,7 @@ const READ_ONLY_COMMANDS = Object.freeze([
['/copilot recheck', 'interaction.help.command.recheck'],
['/copilot description', 'interaction.help.command.description'],
['/copilot status', 'interaction.help.command.status'],
+ ['/copilot doctor', 'interaction.help.command.doctor'],
]);
const CHANGE_COMMANDS = Object.freeze([
['/copilot fix ', 'interaction.help.command.fixOne'],
@@ -41625,6 +41626,185 @@ function neutralizeGithubControls(value) {
}
+/***/ }),
+
+/***/ 99566:
+/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.validDoctorConfiguration = validDoctorConfiguration;
+exports.doctorRepositoryPrerequisites = doctorRepositoryPrerequisites;
+exports.doctorWorkflowContracts = doctorWorkflowContracts;
+const js_yaml_1 = __nccwpck_require__(783);
+const issue_workflow_profile_1 = __nccwpck_require__(26744);
+const issue_start_policy_1 = __nccwpck_require__(90332);
+function validDoctorConfiguration(value) {
+ if (!value || typeof value.preBranchSdd !== 'boolean' || typeof value.issueManagedBranches !== 'boolean'
+ || !/^(?!-|.*(?:\.\.|[\s~^:?*[\]\\]))[A-Za-z0-9._/-]{1,200}$/u.test(value.dependencyBranch))
+ return false;
+ try {
+ if ('error' in (0, issue_workflow_profile_1.parseIssueWorkflowProfile)(JSON.stringify(value.profile)))
+ return false;
+ (0, issue_start_policy_1.decideIssueStart)({ labels: [], issueManagedBranches: value.issueManagedBranches, preBranchSdd: value.preBranchSdd });
+ return true;
+ }
+ catch {
+ return false;
+ }
+}
+function doctorRepositoryPrerequisites(read) {
+ if (read.state !== 'available')
+ return 'unavailable';
+ const paths = ['specs/catalog.json', 'specs/_template.md', 'specs/README.md'];
+ if (paths.some(path => read.files[path]?.state !== 'present'))
+ return 'blocked';
+ const catalogFile = read.files['specs/catalog.json'];
+ if (catalogFile.state !== 'present')
+ return 'blocked';
+ try {
+ const catalog = object(JSON.parse(catalogFile.text));
+ if (catalog.version !== 1 || !Array.isArray(catalog.capabilities))
+ return 'blocked';
+ const ids = new Set();
+ for (const raw of catalog.capabilities) {
+ const capability = object(raw);
+ if (typeof capability.id !== 'string' || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(capability.id)
+ || ids.has(capability.id) || !Array.isArray(capability.specs) || capability.specs.length === 0
+ || capability.specs.some(path => typeof path !== 'string' || !/^specs\/[a-z0-9_-]+\.md$/u.test(path)))
+ return 'blocked';
+ ids.add(capability.id);
+ }
+ return paths.slice(1).every(path => {
+ const file = read.files[path];
+ return file.state === 'present' && /^#\s+\S/mu.test(file.text);
+ }) ? 'pass' : 'blocked';
+ }
+ catch {
+ return 'blocked';
+ }
+}
+/** Structural evidence only. YAML is parsed with the JSON schema and never executed. */
+function doctorWorkflowContracts(read) {
+ if (read.state !== 'available')
+ return 'unavailable';
+ const expected = [
+ ['.github/workflows/copilot_issue.yml', 'issues'],
+ ['.github/workflows/copilot_issue_comment.yml', 'issue_comment'],
+ ['.github/workflows/copilot_pull_request.yml', 'pull_request'],
+ ];
+ try {
+ return expected.every(([path, event]) => {
+ const file = read.files[path];
+ if (file?.state !== 'present')
+ return false;
+ const document = object((0, js_yaml_1.load)(file.text, { schema: js_yaml_1.JSON_SCHEMA }));
+ const events = document.on;
+ const admitted = typeof events === 'string' ? events === event : Array.isArray(events)
+ ? events.includes(event) : Object.prototype.hasOwnProperty.call(object(events), event);
+ return admitted && Object.values(object(document.jobs)).some(job => {
+ const steps = object(job).steps;
+ return Array.isArray(steps) && steps.some(raw => {
+ const step = object(raw);
+ return step.uses === './' || typeof step.uses === 'string'
+ && /^vypdev\/copilot@[^\s${}<>]+$/iu.test(step.uses);
+ });
+ });
+ }) ? 'pass' : 'blocked';
+ }
+ catch {
+ return 'blocked';
+ }
+}
+function object(value) {
+ return value !== null && typeof value === 'object' && !Array.isArray(value) ? value : {};
+}
+
+
+/***/ }),
+
+/***/ 2808:
+/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.isGithubRuntimeDoctorProjection = isGithubRuntimeDoctorProjection;
+exports.formatGithubRuntimeDoctor = formatGithubRuntimeDoctor;
+const github_runtime_doctor_1 = __nccwpck_require__(20413);
+const publication_message_catalog_1 = __nccwpck_require__(34223);
+const ACTIONS = ['configuration', 'workflow', 'retry', 'issue', 'specifications', 'branch', 'policy', 'declared'];
+const STATES = ['pass', 'warning', 'blocked', 'unavailable', 'not-applicable'];
+function isGithubRuntimeDoctorProjection(value) {
+ if (!value || typeof value !== 'object' || Array.isArray(value))
+ return false;
+ const projection = value;
+ const report = projection.report;
+ if (['serverOrigin', 'revision', 'owner', 'repository'].some(key => {
+ const field = projection[key];
+ return field !== undefined && (typeof field !== 'string' || field.length > 300);
+ }))
+ return false;
+ if (report === undefined)
+ return true;
+ if (!report || typeof report !== 'object' || !report.target || !Number.isSafeInteger(report.target.repositoryId)
+ || report.target.repositoryId < 1 || (report.target.kind !== 'issue' && report.target.kind !== 'pull-request')
+ || !Number.isSafeInteger(report.target.kind === 'issue' ? report.target.issueNumber : report.target.pullNumber)
+ || (report.target.kind === 'issue' ? report.target.issueNumber : report.target.pullNumber) < 1)
+ return false;
+ return STATES.includes(report.severity) && Array.isArray(report.checks) && report.checks.length === 7
+ && report.checks.every((check, index) => check && check.group === github_runtime_doctor_1.GITHUB_RUNTIME_DOCTOR_GROUPS[index] && STATES.includes(check.state))
+ && (report.primaryAction === undefined || ACTIONS.includes(report.primaryAction));
+}
+function formatGithubRuntimeDoctor(projection, locale = 'en-US', catalog = (0, publication_message_catalog_1.resolveStaticPublicationCatalog)(locale).catalog) {
+ if (!isGithubRuntimeDoctorProjection(projection))
+ projection = {};
+ const message = (suffix) => catalog.render(`interaction.doctor.${suffix}`);
+ const severity = projection.report?.severity ?? 'unavailable';
+ const action = projection.report?.primaryAction;
+ const lines = [`## 🩺 ${message('heading')}: ${message(`state.${severity}`)}`, '',
+ message(`summary.${severity}`), '',
+ `**${message('action')}:** ${action && ACTIONS.includes(action) ? message(`action.${action}`)
+ : severity === 'pass' || severity === 'not-applicable' ? message('action.none') : message('action.retry')}`,
+ '', message('readonly'), '', `### ${message('checks')}`, ''];
+ const source = repositorySource(projection);
+ const origin = source?.split('/').slice(0, 5).join('/');
+ for (const group of github_runtime_doctor_1.GITHUB_RUNTIME_DOCTOR_GROUPS) {
+ const state = projection.report?.checks.find(check => check.group === group)?.state ?? 'unavailable';
+ const evidence = projection.report?.checks.find(check => check.group === group)?.evidence?.source;
+ const target = projection.report?.target;
+ const link = origin && target && group === 'pull-request-identity' && target.kind === 'pull-request'
+ ? `${origin}/pull/${target.pullNumber}` : origin && target && target.kind === 'issue'
+ && (group === 'issue-readiness' || group === 'linked-branch-readiness') ? `${origin}/issues/${target.issueNumber}` : undefined;
+ const policySource = group === 'approval-policy' && (evidence === 'repository' || evidence === 'organization' || evidence === 'default-off')
+ ? message(`source.${evidence}`) : undefined;
+ const mode = group === 'approval-prerequisites' && (evidence === 'off' || evidence === 'recommend' || evidence === 'guarded')
+ ? `${message('mode')}: ${evidence}` : undefined;
+ lines.push(`- **${message(`group.${group}`)}:** ${message(`state.${state}`)}${policySource ? ` · ${policySource}` : ''}${mode ? ` · ${mode}` : ''}${link ? ` · [${message('source')}](${link})` : ''}`);
+ }
+ if (source)
+ lines.push('', `[${message('source')}](${source})`);
+ return lines.join('\n');
+}
+function repositorySource(value) {
+ if (!value.serverOrigin || !value.revision || !/^[a-f0-9]{40}$/u.test(value.revision)
+ || !value.owner || !value.repository || !/^[A-Za-z0-9][A-Za-z0-9-]{0,38}$/u.test(value.owner)
+ || !/^[A-Za-z0-9][A-Za-z0-9_.-]{0,99}$/u.test(value.repository))
+ return undefined;
+ try {
+ const url = new URL(value.serverOrigin);
+ if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash
+ || url.pathname !== '/' || url.origin !== value.serverOrigin.replace(/\/$/u, ''))
+ return undefined;
+ return `${url.origin}/${value.owner}/${value.repository}/tree/${value.revision}`;
+ }
+ catch {
+ return undefined;
+ }
+}
+
+
/***/ }),
/***/ 30203:
@@ -42432,6 +42612,7 @@ const PUBLICATION_SURFACE_MESSAGE_IDS = Object.freeze([
'interaction.help.command.recheck',
'interaction.help.command.description',
'interaction.help.command.status',
+ 'interaction.help.command.doctor',
'interaction.help.command.fixOne',
'interaction.help.command.fixAll',
'interaction.help.command.dismiss',
@@ -42459,6 +42640,41 @@ const PUBLICATION_SURFACE_MESSAGE_IDS = Object.freeze([
'interaction.branchSync.merged',
'interaction.branchSync.agentResolution',
'interaction.branchSync.verification',
+ 'interaction.doctor.source.repository',
+ 'interaction.doctor.source.organization',
+ 'interaction.doctor.source.default-off',
+ 'interaction.doctor.mode',
+ 'interaction.doctor.heading',
+ 'interaction.doctor.action',
+ 'interaction.doctor.checks',
+ 'interaction.doctor.readonly',
+ 'interaction.doctor.source',
+ 'interaction.doctor.group.effective-configuration',
+ 'interaction.doctor.group.issue-readiness',
+ 'interaction.doctor.group.linked-branch-readiness',
+ 'interaction.doctor.group.workflow-contracts',
+ 'interaction.doctor.group.approval-policy',
+ 'interaction.doctor.group.approval-prerequisites',
+ 'interaction.doctor.group.pull-request-identity',
+ 'interaction.doctor.action.configuration',
+ 'interaction.doctor.action.workflow',
+ 'interaction.doctor.action.retry',
+ 'interaction.doctor.action.issue',
+ 'interaction.doctor.action.specifications',
+ 'interaction.doctor.action.branch',
+ 'interaction.doctor.action.policy',
+ 'interaction.doctor.action.declared',
+ 'interaction.doctor.action.none',
+ 'interaction.doctor.state.pass',
+ 'interaction.doctor.summary.pass',
+ 'interaction.doctor.state.blocked',
+ 'interaction.doctor.summary.blocked',
+ 'interaction.doctor.state.unavailable',
+ 'interaction.doctor.summary.unavailable',
+ 'interaction.doctor.state.warning',
+ 'interaction.doctor.summary.warning',
+ 'interaction.doctor.state.not-applicable',
+ 'interaction.doctor.summary.not-applicable',
'interaction.status.heading',
'interaction.status.repository',
'interaction.status.target',
@@ -42530,6 +42746,7 @@ const ENGLISH_MESSAGES = Object.freeze({
'interaction.help.command.recheck': 're-run the review and reconcile findings.',
'interaction.help.command.description': 'refresh the pull-request description.',
'interaction.help.command.status': 'show the current automation status.',
+ 'interaction.help.command.doctor': 'check available runtime prerequisites without making repository changes.',
'interaction.help.command.fixOne': 'fix one reported finding.',
'interaction.help.command.fixAll': 'fix all unresolved findings.',
'interaction.help.command.dismiss': 'dismiss a finding.',
@@ -42569,6 +42786,41 @@ const ENGLISH_MESSAGES = Object.freeze({
one: '{count} verification check passed.',
other: '{count} verification checks passed.',
}),
+ 'interaction.doctor.source.repository': "Repository variable",
+ 'interaction.doctor.source.organization': "Organization variable",
+ 'interaction.doctor.source.default-off': "Verified absence; default off",
+ 'interaction.doctor.mode': "Mode",
+ 'interaction.doctor.heading': "Runtime doctor",
+ 'interaction.doctor.action': "Next action",
+ 'interaction.doctor.checks': "Checks",
+ 'interaction.doctor.readonly': "This command reads evidence only. Declared approval settings do not prove producer health or write access.",
+ 'interaction.doctor.source': "Inspected repository revision",
+ 'interaction.doctor.group.effective-configuration': "Effective configuration",
+ 'interaction.doctor.group.issue-readiness': "Issue and SDD prerequisites",
+ 'interaction.doctor.group.linked-branch-readiness': "Native linked branch",
+ 'interaction.doctor.group.workflow-contracts': "Installed workflow structure",
+ 'interaction.doctor.group.approval-policy': "Trusted approval policy read",
+ 'interaction.doctor.group.approval-prerequisites': "Declared approval prerequisites",
+ 'interaction.doctor.group.pull-request-identity': "Exact current PR identity",
+ 'interaction.doctor.action.configuration': "Inspect the effective workflow configuration and correct the invalid setting.",
+ 'interaction.doctor.action.workflow': "Inspect the linked workflow revision and correct its required structure.",
+ 'interaction.doctor.action.retry': "Inspect this workflow run and retry /copilot doctor.",
+ 'interaction.doctor.action.issue': "Inspect the issue form and its routing labels.",
+ 'interaction.doctor.action.specifications': "Inspect the catalog, template and specification standard at the linked revision.",
+ 'interaction.doctor.action.branch': "Inspect the issue’s native linked branch and retry after branch readiness.",
+ 'interaction.doctor.action.policy': "Inspect the trusted Actions Variables approval policy source.",
+ 'interaction.doctor.action.declared': "Verify the declared producer prerequisites using the approval setup documentation.",
+ 'interaction.doctor.action.none': "No action required for the inspected prerequisites.",
+ 'interaction.doctor.state.pass': "Pass",
+ 'interaction.doctor.summary.pass': "The inspected applicable prerequisites passed.",
+ 'interaction.doctor.state.blocked': "Blocked",
+ 'interaction.doctor.summary.blocked': "An authoritative read established an invalid required prerequisite.",
+ 'interaction.doctor.state.unavailable': "Unavailable",
+ 'interaction.doctor.summary.unavailable': "Some prerequisites could not be verified; completed checks remain visible.",
+ 'interaction.doctor.state.warning': "Warning",
+ 'interaction.doctor.summary.warning': "A prerequisite needs attention or independent verification.",
+ 'interaction.doctor.state.not-applicable': "Not applicable",
+ 'interaction.doctor.summary.not-applicable': "The inspected prerequisites do not apply to this target.",
'interaction.status.heading': 'Copilot status',
'interaction.status.repository': 'Repository',
'interaction.status.target': 'Target',
@@ -42637,6 +42889,7 @@ const SPANISH_MESSAGES = Object.freeze({
'interaction.help.command.recheck': 'repite la revisión y reconcilia los hallazgos.',
'interaction.help.command.description': 'actualiza la descripción del pull request.',
'interaction.help.command.status': 'muestra el estado actual de la automatización.',
+ 'interaction.help.command.doctor': 'comprueba los requisitos disponibles del runtime sin modificar el repositorio.',
'interaction.help.command.fixOne': 'corrige un hallazgo.',
'interaction.help.command.fixAll': 'corrige todos los hallazgos sin resolver.',
'interaction.help.command.dismiss': 'descarta un hallazgo.',
@@ -42680,6 +42933,41 @@ const SPANISH_MESSAGES = Object.freeze({
many: 'Se superaron {count} comprobaciones de verificación.',
other: 'Se superaron {count} comprobaciones de verificación.',
}),
+ 'interaction.doctor.source.repository': "Variable del repositorio",
+ 'interaction.doctor.source.organization': "Variable de la organización",
+ 'interaction.doctor.source.default-off': "Ausencia verificada; off por defecto",
+ 'interaction.doctor.mode': "Modo",
+ 'interaction.doctor.heading': "Diagnóstico del runtime",
+ 'interaction.doctor.action': "Siguiente acción",
+ 'interaction.doctor.checks': "Comprobaciones",
+ 'interaction.doctor.readonly': "Este comando solo lee evidencias. La configuración de aprobación declarada no acredita la salud de los productores ni el acceso de escritura.",
+ 'interaction.doctor.source': "Revisión inspeccionada del repositorio",
+ 'interaction.doctor.group.effective-configuration': "Configuración efectiva",
+ 'interaction.doctor.group.issue-readiness': "Requisitos de la issue y el SDD",
+ 'interaction.doctor.group.linked-branch-readiness': "Rama enlazada nativa",
+ 'interaction.doctor.group.workflow-contracts': "Estructura de los workflows instalados",
+ 'interaction.doctor.group.approval-policy': "Lectura confiable de la política de aprobación",
+ 'interaction.doctor.group.approval-prerequisites': "Requisitos de aprobación declarados",
+ 'interaction.doctor.group.pull-request-identity': "Identidad actual exacta del PR",
+ 'interaction.doctor.action.configuration': "Revisa la configuración efectiva del workflow y corrige el ajuste inválido.",
+ 'interaction.doctor.action.workflow': "Revisa la revisión enlazada de los workflows y corrige la estructura requerida.",
+ 'interaction.doctor.action.retry': "Revisa esta ejecución del workflow y vuelve a ejecutar /copilot doctor.",
+ 'interaction.doctor.action.issue': "Revisa el formulario de la issue y sus etiquetas de clasificación.",
+ 'interaction.doctor.action.specifications': "Revisa el catálogo, la plantilla y el estándar de especificaciones en la revisión enlazada.",
+ 'interaction.doctor.action.branch': "Revisa la rama enlazada nativa de la issue y reintenta cuando esté preparada.",
+ 'interaction.doctor.action.policy': "Revisa la fuente confiable de la política de aprobación en Actions Variables.",
+ 'interaction.doctor.action.declared': "Verifica los requisitos de los productores declarados siguiendo la documentación de aprobación.",
+ 'interaction.doctor.action.none': "No se requiere ninguna acción para los requisitos inspeccionados.",
+ 'interaction.doctor.state.pass': "Correcto",
+ 'interaction.doctor.summary.pass': "Los requisitos aplicables inspeccionados son correctos.",
+ 'interaction.doctor.state.blocked': "Bloqueado",
+ 'interaction.doctor.summary.blocked': "Una lectura autoritativa confirmó un requisito obligatorio inválido.",
+ 'interaction.doctor.state.unavailable': "No disponible",
+ 'interaction.doctor.summary.unavailable': "Algunos requisitos no se pudieron verificar; las comprobaciones completadas siguen visibles.",
+ 'interaction.doctor.state.warning': "Advertencia",
+ 'interaction.doctor.summary.warning': "Un requisito necesita atención o verificación independiente.",
+ 'interaction.doctor.state.not-applicable': "No aplicable",
+ 'interaction.doctor.summary.not-applicable': "Los requisitos inspeccionados no se aplican a este destino.",
'interaction.status.heading': 'Estado de Copilot',
'interaction.status.repository': 'Repositorio',
'interaction.status.target': 'Destino',
@@ -42944,6 +43232,123 @@ function areOrderedUniquePositiveIntegers(values) {
}
+/***/ }),
+
+/***/ 79017:
+/***/ ((__unused_webpack_module, exports) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.renderApprovalAssessment = renderApprovalAssessment;
+exports.renderApprovalRunSummary = renderApprovalRunSummary;
+/** One bounded, text-first card. No PR prose or provider response is rendered. */
+function renderApprovalAssessment(input) {
+ const spanish = input.locale.toLowerCase().startsWith('es');
+ const { evidence, decision, target } = input;
+ const url = `https://github.com/${encodeURIComponent(target.owner)}/${encodeURIComponent(target.repository)}/pull/${target.pullNumber}`;
+ const approved = decision.code === 'approved' || decision.status === 'already-approved';
+ const partial = decision.code === 'publication-unknown';
+ const status = approved ? (spanish ? 'Aprobada por el bot' : 'Approved by the bot')
+ : partial ? (spanish ? 'Resultado de publicación incierto' : 'Publication outcome uncertain')
+ : decision.status === 'pending' ? (spanish ? 'Esperando evidencias' : 'Waiting for evidence')
+ : decision.status === 'recommend' ? (spanish ? 'Revisión humana necesaria' : 'Human review needed')
+ : (spanish ? 'Aprobación bloqueada' : 'Approval blocked');
+ const action = approved ? (spanish ? 'Ninguna; GitHub seguirá aplicando sus reglas.' : 'None; GitHub still applies its review rules.')
+ : partial ? (spanish ? 'Reejecuta el observador; comprobará la revisión antes de intentar publicarla otra vez.'
+ : 'Rerun the observer; it reads the review before any retry.')
+ : decision.status === 'pending' ? (spanish ? 'Espera al productor pendiente o reejecuta el observador.'
+ : 'Wait for the pending producer or rerun the observer.')
+ : decision.status === 'recommend' ? (spanish ? 'Pide una decisión a una persona revisora.'
+ : 'Ask a human reviewer to decide.')
+ : (spanish ? 'Corrige el requisito indicado y reejecuta el observador.'
+ : 'Fix the named prerequisite and rerun the observer.');
+ const completed = evidence.checks.filter(check => check.status === 'completed' && check.conclusion === 'success').length;
+ const selected = evidence.checks.length;
+ const bugbot = evidence.bugbot?.headSha === evidence.headSha && evidence.bugbot.outcome === 'complete'
+ && evidence.bugbot.coverage === 'complete';
+ const detail = spanish ? spanishDecisionReason(decision.code) : escapeMarkdown(decision.detail);
+ const lines = [
+ '',
+ `### ${spanish ? 'Aprobación de Copilot' : 'Copilot approval'} · ${status}`,
+ `**${spanish ? 'Estado' : 'Status'}:** ${detail}`,
+ `**${spanish ? 'Completado' : 'Completed'}:** ${completed}/${selected} ${spanish ? 'checks observados correctos' : 'observed checks succeeded'}; Bugbot ${bugbot ? (spanish ? 'completo' : 'complete') : (spanish ? 'no verificado' : 'unverified')}.`,
+ `**${spanish ? 'Acción necesaria' : 'Action required'}:** ${action}`,
+ `**${spanish ? 'Impacto' : 'Impact'}:** ${approved
+ ? (spanish ? 'Hay una revisión nativa para esta revisión; no se ha fusionado la PR.' : 'A native review exists for this revision; the PR was not merged.')
+ : (spanish ? 'No se ha confirmado ninguna aprobación nueva para esta revisión.' : 'No new approval is confirmed for this revision.')}`,
+ input.reviewId ? `[${spanish ? 'Abrir aprobación' : 'Open approval'}](${url}#pullrequestreview-${input.reviewId}) · [${spanish ? 'Ver checks' : 'See checks'}](${url}/checks)`
+ : `[${spanish ? 'Ver checks' : 'See checks'}](${url}/checks)`,
+ '',
+ `${spanish ? 'Evidencia técnica' : 'Technical evidence'}
`,
+ `${spanish ? 'Revisión' : 'Revision'}: \`${evidence.headSha.slice(0, 12)}\`; base: \`${evidence.baseSha.slice(0, 12)}\`; ${spanish ? 'motivo' : 'reason'}: \`${escapeMarkdown(decision.code)}\`.`,
+ ' ',
+ ];
+ return lines.join('\n').slice(0, 1800);
+}
+const SPANISH_DECISION_REASONS = Object.freeze({
+ disabled: 'La aprobación automática está desactivada.',
+ 'unsupported-pr': 'Solo se evalúan PRs abiertas, no borrador, completas y del mismo repositorio.',
+ scope: 'La PR queda fuera del ámbito de aprobación configurado.',
+ 'bot-author': 'Una persona debe revisar las PRs abiertas por bots.',
+ 'protected-path': 'Ha cambiado un archivo protegido de workflow, política o seguridad.',
+ 'bugbot-ignored-path': 'Bugbot omitió un archivo modificado; su resultado no permite aprobar.',
+ 'unsafe-rules': 'Las reglas de rama deben ser legibles y descartar aprobaciones antiguas.',
+ 'producer-unattested': 'Falta la confirmación del productor de CI y del paso obligatorio de cobertura.',
+ 'reporter-unattested': 'No se ha confirmado la instalación del reporter numérico en CI de confianza.',
+ 'merge-ref-unavailable': 'Se espera una fusión de prueba verificada para la base y revisión actuales.',
+ 'approval-check-cycle': 'El check Copilot / Approval no puede exigirse a sí mismo.',
+ 'bugbot-configuration': 'Bugbot debe usar severidad info y tener desactivado dry-run.',
+ 'check-producer-unconfigured': 'Falta configurar un productor de confianza para un check obligatorio.',
+ 'check-missing': 'Se espera un check de la revisión actual.',
+ 'check-source': 'El origen de un check difiere del productor configurado.',
+ 'check-ambiguous': 'Hay intentos recientes ambiguos para un check.',
+ 'check-pending': 'Se espera a que termine un check.',
+ 'check-failed': 'Un check obligatorio no terminó correctamente.',
+ 'bugbot-missing': 'Se espera una revisión completa de Bugbot para esta revisión.',
+ 'bugbot-incomplete': 'La evidencia de Bugbot es parcial, fallida u obsoleta.',
+ 'bugbot-findings': 'Bugbot tiene hallazgos abiertos, desconocidos o descartados que bloquean.',
+ 'coverage-evidence': 'Falta evidencia numérica válida para la revisión actual.',
+ 'coverage-low': 'La cobertura del diff no alcanza el umbral configurado.',
+ 'reviews-unavailable': 'No se pudo leer todo el historial de revisiones.',
+ 'existing-approval': 'El bot ya aprobó esta revisión exacta.',
+ 'approval-dismissed': 'Una persona responsable descartó la aprobación del bot para esta revisión.',
+ 'changes-requested': 'Una persona revisora ha solicitado cambios.',
+ 'human-approved': 'Una persona ya aprobó esta revisión.',
+ 'recommend-mode': 'Las evidencias cumplen; una persona debe decidir.',
+ eligible: 'Las evidencias y reglas permiten una aprobación nativa.',
+ approved: 'El bot aprobó esta revisión; GitHub sigue aplicando las reglas de rama.',
+ 'publication-unknown': 'GitHub podría haber aceptado la revisión; reejecuta para comprobarla.',
+ 'invalid-policy': 'La política instalada no es válida; ejecuta copilot doctor.',
+ 'evidence-unavailable': 'No se pudo leer la evidencia de aprobación; reejecuta el observador.',
+ 'target-mismatch': 'La PR resuelta no coincide con el repositorio y número esperados.',
+ 'pre-submit-unavailable': 'No se pudo volver a leer la evidencia antes de publicar la aprobación.',
+ superseded: 'La política o revisión cambió; vuelve a comprobar la revisión actual.',
+});
+function spanishDecisionReason(code) {
+ return SPANISH_DECISION_REASONS[code] ?? 'No se pudo completar la evaluación de aprobación.';
+}
+function renderApprovalRunSummary(input) {
+ const spanish = input.locale.toLowerCase().startsWith('es');
+ const checks = input.evidence.checks.slice(0, 8).map(check => `- \`${escapeMarkdown(check.name)}\` · App ${check.sourceAppId} · \`${escapeMarkdown(check.workflowName)}\` · ${escapeMarkdown(check.status)}/${escapeMarkdown(check.conclusion ?? 'unknown')} · run ${check.runId}/${check.attempt}`);
+ return [
+ `### ${spanish ? 'Observación de aprobación' : 'Approval observation'} · PR #${input.target.pullNumber}`,
+ `- ${spanish ? 'Inicio' : 'Wakeup'}: ${escapeMarkdown(input.wakeup)}`,
+ `- ${spanish ? 'Modo' : 'Mode'}: \`${escapeMarkdown(input.mode)}\`; ${spanish ? 'resultado' : 'result'}: \`${escapeMarkdown(input.decision.status)}\` (\`${escapeMarkdown(input.decision.code)}\`)`,
+ `- ${spanish ? 'Revisión/base' : 'Head/base'}: \`${input.evidence.headSha}\` / \`${input.evidence.baseSha}\``,
+ `- Bugbot: ${escapeMarkdown(input.evidence.bugbot?.outcome ?? 'missing')}/${escapeMarkdown(input.evidence.bugbot?.coverage ?? 'missing')}`,
+ `- ${spanish ? 'Revisión nativa' : 'Native review'}: ${input.reviewId ?? (spanish ? 'ninguna confirmada' : 'none confirmed')}`,
+ `- ${spanish ? 'Tarjeta/Check' : 'Card/Check'}: ${escapeMarkdown(input.publication)}`,
+ ...(checks.length > 0 ? [spanish ? 'Productores observados:' : 'Observed producers:', ...checks] : []),
+ `- ${spanish ? 'Recuperación' : 'Recovery'}: ${spanish ? 'corrige el bloqueo y reejecuta Copilot - Pull Request Approval con el número de PR' : 'fix the blocker and rerun Copilot - Pull Request Approval with the PR number'}.`,
+ ].join('\n');
+}
+function escapeMarkdown(value) {
+ return [...value].map(character => character === '@' ? '@\u200b'
+ : '\\`*_{}[]()#+.!|>~-<>'.includes(character) ? `\\${character}` : character).join('');
+}
+
+
/***/ }),
/***/ 43268:
@@ -43491,6 +43896,7 @@ exports.hasOwnedPrimaryIssuePublication = hasOwnedPrimaryIssuePublication;
exports.selectSemanticReplyIntents = selectSemanticReplyIntents;
exports.renderSemanticReply = renderSemanticReply;
exports.renderSemanticStatus = renderSemanticStatus;
+const github_runtime_doctor_presentation_policy_1 = __nccwpck_require__(2808);
const github_publication_1 = __nccwpck_require__(35793);
const github_user_policy_1 = __nccwpck_require__(84403);
const result_1 = __nccwpck_require__(73817);
@@ -43588,6 +43994,11 @@ function selectSemanticReplyIntents(context) {
if (kind === 'access-policy') {
return [replyIntent(context, correlationId, 'access-policy', Object.freeze({ kind }))];
}
+ if (result.id.endsWith('.Doctor') && (0, github_runtime_doctor_presentation_policy_1.isGithubRuntimeDoctorProjection)(payload.doctor)) {
+ return [replyIntent(context, correlationId, 'copilot-doctor', Object.freeze({
+ kind: 'doctor-command', snapshot: payload.doctor,
+ }))];
+ }
const snapshot = (0, result_1.getResultPayload)(payload.status);
if (result.id.endsWith('.Status') && isStatusSnapshot(snapshot)) {
const projection = Object.freeze({
@@ -43668,6 +44079,8 @@ function renderReplyBody(intent, catalog) {
if (intent.projection.kind === 'branch-sync-result') {
return renderBranchSyncResult(intent.projection, catalog);
}
+ if (intent.projection.kind === 'doctor-command')
+ return (0, github_runtime_doctor_presentation_policy_1.formatGithubRuntimeDoctor)(intent.projection.snapshot, intent.locale, catalog);
return (0, status_command_policy_1.formatCopilotStatus)(intent.projection.snapshot, intent.locale, catalog);
}
function renderBranchSyncResult(projection, catalog) {
@@ -48597,6 +49010,21 @@ function calculateJitteredWorkflowDelay(baseDelayMilliseconds, randomValue, poli
}
+/***/ }),
+
+/***/ 64921:
+/***/ ((__unused_webpack_module, exports) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.DOCTOR_REPOSITORY_PATHS = void 0;
+exports.DOCTOR_REPOSITORY_PATHS = [
+ '.github/workflows/copilot_issue.yml', '.github/workflows/copilot_issue_comment.yml',
+ '.github/workflows/copilot_pull_request.yml', 'specs/catalog.json', 'specs/_template.md', 'specs/README.md',
+];
+
+
/***/ }),
/***/ 6152:
@@ -52227,6 +52655,7 @@ async function runDoUserRequestAction(param, options, intentPayload) {
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.runExplicitCommentCommand = runExplicitCommentCommand;
exports.invalidCommentCommandResult = invalidCommentCommandResult;
+const read_copilot_doctor_workflow_1 = __nccwpck_require__(12416);
const result_1 = __nccwpck_require__(73817);
const read_copilot_status_workflow_1 = __nccwpck_require__(57206);
const copilot_interaction_policy_1 = __nccwpck_require__(90108);
@@ -52242,6 +52671,8 @@ const LEARNED_BUGBOT_RULE_PATH = '.copilot/BUGBOT.learned.md';
async function runExplicitCommentCommand(param, options, command, actorAuthorizationPort) {
if (command.name === 'help')
return runHelpCommand(param, options);
+ if (command.name === 'doctor')
+ return [await (0, read_copilot_doctor_workflow_1.readCopilotDoctor)(param, options.taskId, options.doctorReadPorts)];
if (command.name === 'status')
return [await (0, read_copilot_status_workflow_1.readCopilotStatus)(param, options.taskId, options.statusReadPorts)];
if (command.name === 'dismiss')
@@ -52468,6 +52899,22 @@ const copilot_command_1 = __nccwpck_require__(11771);
function projectCommentAutomationContext(source, language, userComment) {
const review = (0, bugbot_review_operation_context_1.projectBugbotReviewOperationContext)(source);
return Object.freeze({
+ ...(source.issueWorkflowProfile && typeof source.preBranchSdd === 'boolean'
+ && typeof source.issue.issueManagedBranches === 'boolean' ? { doctorConfiguration: Object.freeze({
+ profile: Object.freeze({ schemaVersion: source.issueWorkflowProfile.schemaVersion,
+ enabled: Object.freeze([...source.issueWorkflowProfile.enabled]) }),
+ labels: Object.freeze({
+ feature: Object.freeze([source.labels?.feature, source.labels?.enhancement].filter((value) => typeof value === 'string' && value.length > 0)),
+ bugfix: Object.freeze([source.labels?.bugfix, source.labels?.bug].filter((value) => typeof value === 'string' && value.length > 0)),
+ documentation: Object.freeze([source.labels?.documentation, source.labels?.docs].filter((value) => typeof value === 'string' && value.length > 0)),
+ chore: Object.freeze([source.labels?.chore, source.labels?.maintenance].filter((value) => typeof value === 'string' && value.length > 0)),
+ help: Object.freeze([source.labels?.help, source.labels?.question].filter((value) => typeof value === 'string' && value.length > 0)),
+ release: Object.freeze([source.labels?.release].filter((value) => typeof value === 'string' && value.length > 0)),
+ hotfix: Object.freeze([source.labels?.hotfix].filter((value) => typeof value === 'string' && value.length > 0)),
+ }),
+ preBranchSdd: source.preBranchSdd, issueManagedBranches: source.issue.issueManagedBranches,
+ dependencyBranch: source.currentConfiguration.parentBranch ?? source.branches.development ?? '',
+ }) } : {}),
actor: source.actor,
trustedBotLogin: source.tokenUser?.trim() ?? '',
userComment,
@@ -52654,7 +53101,7 @@ async function runCommentAutomation(initialParam, options, actorAuthorizationPor
return [(0, comment_automation_command_workflow_1.invalidCommentCommandResult)(options.taskId, command.reason)];
}
const isPublicMetadataCommand = command.kind === 'command'
- && (command.command.name === 'help' || command.command.name === 'status');
+ && (command.command.name === 'help' || command.command.name === 'status' || command.command.name === 'doctor');
if (!isPublicMetadataCommand && param.membersOnly
&& !await actorAuthorizationPort.isActorAllowedToUseMemberOnlyAutomation(param.actor)) {
(0, logging_ports_1.logInfo)('Skipping agent automation because ai-members-only is enabled and the actor is not authorized.');
@@ -53290,8 +53737,9 @@ const check_issue_comment_language_use_case_1 = __nccwpck_require__(93152);
const comment_automation_context_1 = __nccwpck_require__(37055);
const pull_request_workflow_context_1 = __nccwpck_require__(73447);
const pre_branch_sdd_1 = __nccwpck_require__(34730);
+const copilot_command_1 = __nccwpck_require__(11771);
class IssueCommentUseCase {
- constructor(languageUseCase, intentUseCase, thinkUseCase, autofixUseCase, doUserRequestUseCase, actorAuthorizationPort, bugbotGitMutationPort, dismissBugbotFindingsUseCase, reviewPotentialProblemsUseCase, updatePullRequestDescriptionUseCase, rememberBugbotRuleUseCase, syncBranchUseCase, preBranchSddContinuation, rememberBranchPorts, statusReadPorts) {
+ constructor(languageUseCase, intentUseCase, thinkUseCase, autofixUseCase, doUserRequestUseCase, actorAuthorizationPort, bugbotGitMutationPort, dismissBugbotFindingsUseCase, reviewPotentialProblemsUseCase, updatePullRequestDescriptionUseCase, rememberBugbotRuleUseCase, syncBranchUseCase, preBranchSddContinuation, rememberBranchPorts, statusReadPorts, doctorReadPorts) {
this.languageUseCase = languageUseCase;
this.intentUseCase = intentUseCase;
this.thinkUseCase = thinkUseCase;
@@ -53307,16 +53755,19 @@ class IssueCommentUseCase {
this.preBranchSddContinuation = preBranchSddContinuation;
this.rememberBranchPorts = rememberBranchPorts;
this.statusReadPorts = statusReadPorts;
+ this.doctorReadPorts = doctorReadPorts;
this.taskId = "IssueCommentUseCase";
}
async invoke(param) {
- if (param.preBranchSdd && !param.issue.issueManagedBranches) {
+ const command = (0, copilot_command_1.parseCopilotCommand)(param.issue.commentBody);
+ const isDoctor = command.kind === 'command' && command.command.name === 'doctor';
+ if (!isDoctor && param.preBranchSdd && !param.issue.issueManagedBranches) {
return [new result_1.Result({
id: this.taskId, success: false, executed: true,
steps: ['pre-branch-sdd requires issue-managed-branches; correct the Action configuration.'],
})];
}
- if (this.preBranchSddContinuation
+ if (!isDoctor && this.preBranchSddContinuation
&& param.issueStartDecision.sddRequired
&& (0, pre_branch_sdd_1.isSddAnswerComment)(param.issue.commentBody)
&& param.issue.commentAuthor.toLowerCase() !== param.tokenUser?.toLowerCase()) {
@@ -53344,6 +53795,7 @@ class IssueCommentUseCase {
rememberBugbotRuleUseCase: this.rememberBugbotRuleUseCase,
rememberBranchPorts: this.rememberBranchPorts,
statusReadPorts: this.statusReadPorts,
+ doctorReadPorts: this.doctorReadPorts,
syncBranchUseCase: this.syncBranchUseCase
? { invoke: (options) => this.syncBranchUseCase.invoke({ context: context.branchSync, options }) }
: undefined,
@@ -54002,7 +54454,7 @@ const check_pull_request_comment_language_use_case_1 = __nccwpck_require__(21729
const comment_automation_context_1 = __nccwpck_require__(37055);
const pull_request_workflow_context_1 = __nccwpck_require__(73447);
class PullRequestReviewCommentUseCase {
- constructor(languageUseCase, intentUseCase, thinkUseCase, autofixUseCase, doUserRequestUseCase, actorAuthorizationPort, bugbotGitMutationPort, dismissBugbotFindingsUseCase, reviewPotentialProblemsUseCase, updatePullRequestDescriptionUseCase, rememberBugbotRuleUseCase, syncBranchUseCase, rememberBranchPorts, statusReadPorts) {
+ constructor(languageUseCase, intentUseCase, thinkUseCase, autofixUseCase, doUserRequestUseCase, actorAuthorizationPort, bugbotGitMutationPort, dismissBugbotFindingsUseCase, reviewPotentialProblemsUseCase, updatePullRequestDescriptionUseCase, rememberBugbotRuleUseCase, syncBranchUseCase, rememberBranchPorts, statusReadPorts, doctorReadPorts) {
this.languageUseCase = languageUseCase;
this.intentUseCase = intentUseCase;
this.thinkUseCase = thinkUseCase;
@@ -54017,6 +54469,7 @@ class PullRequestReviewCommentUseCase {
this.syncBranchUseCase = syncBranchUseCase;
this.rememberBranchPorts = rememberBranchPorts;
this.statusReadPorts = statusReadPorts;
+ this.doctorReadPorts = doctorReadPorts;
this.taskId = "PullRequestReviewCommentUseCase";
}
async invoke(param) {
@@ -54042,6 +54495,7 @@ class PullRequestReviewCommentUseCase {
rememberBugbotRuleUseCase: this.rememberBugbotRuleUseCase,
rememberBranchPorts: this.rememberBranchPorts,
statusReadPorts: this.statusReadPorts,
+ doctorReadPorts: this.doctorReadPorts,
syncBranchUseCase: this.syncBranchUseCase
? { invoke: (options) => this.syncBranchUseCase.invoke({ context: context.branchSync, options }) }
: undefined,
@@ -54519,6 +54973,120 @@ function deepFreezeCopy(value) {
}
+/***/ }),
+
+/***/ 12416:
+/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.readCopilotDoctor = readCopilotDoctor;
+const result_1 = __nccwpck_require__(73817);
+const github_runtime_doctor_1 = __nccwpck_require__(20413);
+const issue_workflow_profile_1 = __nccwpck_require__(26744);
+const issue_start_policy_1 = __nccwpck_require__(90332);
+const positive_integer_policy_1 = __nccwpck_require__(19879);
+const context_1 = __nccwpck_require__(14712);
+const github_runtime_doctor_policy_1 = __nccwpck_require__(99566);
+const github_runtime_doctor_presentation_policy_1 = __nccwpck_require__(2808);
+const bugbot_context_request_1 = __nccwpck_require__(98299);
+async function readCopilotDoctor(param, taskId, ports) {
+ const repositoryId = (0, positive_integer_policy_1.parsePositiveSafeInteger)(param.bugbot.review.repository.id);
+ const isPull = param.status.target === 'pull-request';
+ const number = (0, positive_integer_policy_1.parsePositiveSafeInteger)(isPull ? param.status.pullRequestNumber : param.status.issueNumber);
+ if (!repositoryId || !number)
+ return doctorResult(taskId, undefined);
+ const target = isPull ? { kind: 'pull-request', repositoryId, pullNumber: number }
+ : { kind: 'issue', repositoryId, issueNumber: number };
+ const checks = [];
+ const put = (group, state, action) => checks.push(Object.freeze({ group, state, evidence: { source: group }, ...(action && state !== 'pass' && state !== 'not-applicable' ? { action: state === 'unavailable' ? 'retry' : action } : {}) }));
+ const configuration = param.doctorConfiguration;
+ const valid = (0, github_runtime_doctor_policy_1.validDoctorConfiguration)(configuration);
+ put('effective-configuration', configuration === undefined ? 'unavailable' : valid ? 'pass' : 'blocked', !valid ? 'configuration' : undefined);
+ const repositoryRead = ports ? await ports.readRepository(repositoryId).catch(() => ({ state: 'unavailable' }))
+ : { state: 'unavailable' };
+ const verifiedRead = repositoryRead.state === 'available'
+ && (repositoryRead.repositoryId !== repositoryId || !/^[a-f0-9]{40}$/u.test(repositoryRead.revision))
+ ? { state: 'unavailable' } : repositoryRead;
+ put('workflow-contracts', (0, github_runtime_doctor_policy_1.doctorWorkflowContracts)(verifiedRead), 'workflow');
+ if (isPull) {
+ // Optional issue context is not guessed from a PR number or event prose.
+ put('issue-readiness', 'not-applicable');
+ put('linked-branch-readiness', 'not-applicable');
+ }
+ else if (!valid || !ports) {
+ put('issue-readiness', 'unavailable', 'retry');
+ put('linked-branch-readiness', 'unavailable', 'retry');
+ }
+ else {
+ try {
+ const issue = await ports.readIssue(number);
+ if (issue.number !== number)
+ throw new Error('Wrong issue identity.');
+ const admission = (0, issue_workflow_profile_1.classifyIssueWorkflow)(issue.labels, configuration.profile, configuration.labels, issue.body);
+ if (admission.status !== 'eligible') {
+ put('issue-readiness', admission.status === 'unmanaged' ? 'unavailable' : 'blocked', 'issue');
+ put('linked-branch-readiness', 'not-applicable');
+ }
+ else {
+ const decision = (0, issue_start_policy_1.decideIssueStart)({ kind: admission.kind, labels: issue.labels,
+ issueManagedBranches: configuration.issueManagedBranches, preBranchSdd: configuration.preBranchSdd });
+ put('issue-readiness', decision.sddRequired ? (0, github_runtime_doctor_policy_1.doctorRepositoryPrerequisites)(verifiedRead) : 'not-applicable', 'specifications');
+ if (!decision.branchRequired)
+ put('linked-branch-readiness', 'not-applicable');
+ else {
+ const branch = await ports.readLinkedBranch(number).catch(() => ({ state: 'unavailable' }));
+ const state = branch.state === 'available' && branch.repositoryId === repositoryId && /^[a-f0-9]{40}$/u.test(branch.headSha)
+ ? 'pass' : branch.state === 'absent' ? 'warning' : 'unavailable';
+ put('linked-branch-readiness', state, 'branch');
+ }
+ }
+ }
+ catch {
+ put('issue-readiness', 'unavailable', 'retry');
+ put('linked-branch-readiness', 'unavailable', 'retry');
+ }
+ }
+ const policyRead = ports ? await ports.readApprovalPolicy().catch(() => ({ status: 'unavailable' }))
+ : { status: 'unavailable' };
+ const policy = (0, github_runtime_doctor_1.parseTrustedDoctorApprovalPolicy)(policyRead);
+ put('approval-policy', policy.status === 'valid' ? 'pass' : policy.status === 'invalid' ? 'blocked' : 'unavailable', 'policy');
+ if (policy.status === 'valid' || policyRead.status === 'available')
+ checks[checks.length - 1] = Object.freeze({ ...checks[checks.length - 1],
+ evidence: { source: policyRead.status === 'available' ? policyRead.source : policy.status === 'valid' ? policy.source : 'unavailable' } });
+ put('approval-prerequisites', policy.status !== 'valid' ? 'unavailable' : policy.policy.mode === 'off' ? 'not-applicable' : 'warning', 'declared');
+ if (policy.status === 'valid')
+ checks[checks.length - 1] = Object.freeze({ ...checks[checks.length - 1], evidence: { source: policy.policy.mode } });
+ if (!isPull)
+ put('pull-request-identity', 'not-applicable');
+ else {
+ let state = 'unavailable';
+ try {
+ if (ports) {
+ const request = (0, bugbot_context_request_1.projectBugbotContextRequest)(param.bugbot.review);
+ const selection = request.target.pullRequestSelection;
+ if (selection.kind === 'event' && selection.number === number) {
+ const canonical = (0, context_1.selectCanonicalBugbotPullRequest)(request.target, [await ports.pullRequests.getPullRequest(number)], 'event');
+ if (canonical.kind === 'canonical' && /^[a-f0-9]{40}$/u.test(canonical.pullRequest.headSha)
+ && await ports.pullRequests.getPullRequestHeadSha(number) === canonical.pullRequest.headSha)
+ state = 'pass';
+ }
+ }
+ }
+ catch { /* Independent facts remain available. */ }
+ put('pull-request-identity', state, 'retry');
+ }
+ const report = (0, github_runtime_doctor_1.aggregateGithubRuntimeDoctorReport)({ target, checks });
+ return doctorResult(taskId, report, ports?.serverOrigin, verifiedRead.state === 'available' ? verifiedRead.revision : undefined, param.status.owner, param.status.repository);
+}
+function doctorResult(taskId, report, serverOrigin, revision, owner, repository) {
+ const projection = Object.freeze({ report, serverOrigin, revision, owner, repository });
+ return new result_1.Result({ id: `${taskId}.Doctor`, success: true, executed: true, stepFormat: 'markdown',
+ steps: [(0, github_runtime_doctor_presentation_policy_1.formatGithubRuntimeDoctor)(projection)], payload: { doctor: projection } });
+}
+
+
/***/ }),
/***/ 57206:
@@ -76076,6 +76644,156 @@ function requireObject(data, operation) {
}
+/***/ }),
+
+/***/ 25282:
+/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
+
+"use strict";
+
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+ o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || (function () {
+ var ownKeys = function(o) {
+ ownKeys = Object.getOwnPropertyNames || function (o) {
+ var ar = [];
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
+ return ar;
+ };
+ return ownKeys(o);
+ };
+ return function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+})();
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.RuntimeDoctorRepository = void 0;
+const github = __importStar(__nccwpck_require__(78227));
+const github_runtime_doctor_ports_1 = __nccwpck_require__(64921);
+const pull_request_approval_repository_1 = __nccwpck_require__(23001);
+const SHA = /^[a-f0-9]{40}$/u;
+const SAFE_REF = /^(?!-|.*(?:\.\.|[\s~^:?*[\]\\]))[A-Za-z0-9._/-]{1,200}$/u;
+/** SDK DTOs and diagnostics terminate here; no mutation, provisioning, or filesystem API. */
+class RuntimeDoctorRepository {
+ constructor(owner, repository, token) {
+ this.owner = owner;
+ this.repository = repository;
+ this.client = github.getOctokit(token);
+ }
+ async readRepository(repositoryId) {
+ try {
+ const repo = await this.client.rest.repos.get({ owner: this.owner, repo: this.repository });
+ if (repo.data.id !== repositoryId || !SAFE_REF.test(repo.data.default_branch))
+ return { state: 'unavailable' };
+ const ref = await this.client.rest.git.getRef({ owner: this.owner, repo: this.repository, ref: `heads/${repo.data.default_branch}` });
+ const revision = ref.data.object.sha;
+ if (!SHA.test(revision) || ref.data.object.type !== 'commit')
+ return { state: 'unavailable' };
+ const values = await Promise.all(github_runtime_doctor_ports_1.DOCTOR_REPOSITORY_PATHS.map(async (path) => [path, await this.readFile(path, revision)]));
+ const final = await this.client.rest.git.getRef({ owner: this.owner, repo: this.repository, ref: `heads/${repo.data.default_branch}` });
+ if (final.data.object.type !== 'commit' || final.data.object.sha !== revision)
+ return { state: 'unavailable' };
+ return Object.freeze({ state: 'available', repositoryId, revision,
+ files: Object.freeze(Object.fromEntries(values)) });
+ }
+ catch {
+ return { state: 'unavailable' };
+ }
+ }
+ async readFile(path, revision) {
+ try {
+ const response = await this.client.rest.repos.getContent({ owner: this.owner, repo: this.repository, path, ref: revision });
+ const data = response.data;
+ if (Array.isArray(data) || data.type !== 'file' || data.path !== path || !('content' in data)
+ || data.encoding !== 'base64' || typeof data.content !== 'string' || data.content.length > 300000
+ || !/^[A-Za-z0-9+/=\r\n]*$/u.test(data.content))
+ throw new Error('Malformed bounded file response.');
+ const bytes = Buffer.from(data.content, 'base64');
+ const text = bytes.toString('utf8');
+ if (bytes.toString('base64') !== data.content.replace(/[\r\n]/gu, '')
+ || bytes.length > 200000 || !Buffer.from(text, 'utf8').equals(bytes))
+ throw new Error('Malformed bounded file content.');
+ return Object.freeze({ state: 'present', text });
+ }
+ catch (error) {
+ if (status(error) === 404)
+ return Object.freeze({ state: 'missing' });
+ throw error;
+ }
+ }
+ async readIssue(issueNumber) {
+ const response = await this.client.rest.issues.get({ owner: this.owner, repo: this.repository, issue_number: issueNumber });
+ const issue = response.data;
+ if (issue.number !== issueNumber || issue.pull_request || typeof issue.body !== 'string' || issue.body.length > 70000
+ || typeof issue.title !== 'string' || issue.title.length > 256 || !Array.isArray(issue.labels) || issue.labels.length > 100)
+ throw new Error('Malformed bounded issue response.');
+ const labels = issue.labels.map((label) => typeof label === 'string' ? label : label.name);
+ if (labels.some((label) => typeof label !== 'string' || label.length > 120))
+ throw new Error('Malformed issue labels.');
+ return Object.freeze({ number: issue.number, title: issue.title, body: issue.body, labels: Object.freeze(labels) });
+ }
+ async readLinkedBranch(issueNumber) {
+ try {
+ const result = await this.client.graphql(`query RuntimeDoctorLinkedBranch($owner:String!,$repo:String!,$number:Int!){repository(owner:$owner,name:$repo){
+ databaseId nameWithOwner issue(number:$number){number linkedBranches(first:100){
+ pageInfo{hasNextPage} nodes{ref{name target{oid} repository{databaseId nameWithOwner}}}}}}}`, { owner: this.owner, repo: this.repository, number: issueNumber });
+ const repo = result.repository;
+ const links = repo?.issue?.linkedBranches;
+ const expected = `${this.owner}/${this.repository}`.toLowerCase();
+ if (!repo || !Number.isSafeInteger(repo.databaseId) || (repo.databaseId ?? 0) <= 0 || repo.nameWithOwner?.toLowerCase() !== expected
+ || repo.issue?.number !== issueNumber || links?.pageInfo?.hasNextPage !== false || !Array.isArray(links.nodes))
+ return { state: 'unavailable' };
+ const refs = links.nodes.flatMap((node) => node?.ref ? [node.ref] : []);
+ if (refs.length === 0)
+ return { state: 'absent' };
+ if (refs.length !== 1)
+ return { state: 'unavailable' };
+ const branch = refs[0];
+ if (!branch.name || !SAFE_REF.test(branch.name) || !branch.target?.oid || !SHA.test(branch.target.oid)
+ || branch.repository?.databaseId !== repo.databaseId || branch.repository?.nameWithOwner?.toLowerCase() !== expected)
+ return { state: 'unavailable' };
+ const live = await this.client.rest.git.getRef({ owner: this.owner, repo: this.repository, ref: `heads/${branch.name}` });
+ return live.data.object.type === 'commit' && live.data.object.sha === branch.target.oid
+ ? Object.freeze({ state: 'available', repositoryId: repo.databaseId, name: branch.name, headSha: branch.target.oid }) : { state: 'unavailable' };
+ }
+ catch {
+ return { state: 'unavailable' };
+ }
+ }
+ async readApprovalPolicy() {
+ try {
+ const value = await (0, pull_request_approval_repository_1.readEffectiveApprovalPolicy)(this.client, { owner: this.owner, repository: this.repository });
+ return value ? Object.freeze({ status: 'available', ...value }) : Object.freeze({ status: 'absent' });
+ }
+ catch {
+ return Object.freeze({ status: 'unavailable' });
+ }
+ }
+}
+exports.RuntimeDoctorRepository = RuntimeDoctorRepository;
+function status(error) {
+ return error && typeof error === 'object' && 'status' in error && typeof error.status === 'number' ? error.status : undefined;
+}
+
+
/***/ }),
/***/ 52644:
@@ -77954,6 +78672,81 @@ function selectAvailableMembers(members, currentMembers, requested) {
}
+/***/ }),
+
+/***/ 96710:
+/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.parseApprovalCoverageZip = parseApprovalCoverageZip;
+const node_zlib_1 = __nccwpck_require__(65628);
+/** Parse one bounded GitHub artifact as data; never extract paths or execute content. */
+function parseApprovalCoverageZip(value) {
+ const zip = Buffer.from(value);
+ if (zip.length > 64 * 1024 || zip.length < 30)
+ return undefined;
+ let eocd = -1;
+ for (let index = zip.length - 22; index >= Math.max(0, zip.length - 65557); index--) {
+ if (zip.readUInt32LE(index) === 0x06054b50) {
+ eocd = index;
+ break;
+ }
+ }
+ if (eocd < 0 || zip.readUInt16LE(eocd + 10) !== 1)
+ return undefined;
+ const central = zip.readUInt32LE(eocd + 16);
+ if (central + 46 > zip.length || zip.readUInt32LE(central) !== 0x02014b50)
+ return undefined;
+ const method = zip.readUInt16LE(central + 10);
+ const compressedSize = zip.readUInt32LE(central + 20);
+ const plainSize = zip.readUInt32LE(central + 24);
+ const nameLength = zip.readUInt16LE(central + 28);
+ const name = zip.subarray(central + 46, central + 46 + nameLength).toString('utf8');
+ const local = zip.readUInt32LE(central + 42);
+ if (name !== 'copilot-diff-coverage.json' || plainSize > 16384 || compressedSize > 64 * 1024
+ || ![0, 8].includes(method) || local + 30 > zip.length || zip.readUInt32LE(local) !== 0x04034b50)
+ return undefined;
+ const start = local + 30 + zip.readUInt16LE(local + 26) + zip.readUInt16LE(local + 28);
+ if (start + compressedSize > zip.length)
+ return undefined;
+ let raw;
+ try {
+ const contents = zip.subarray(start, start + compressedSize);
+ raw = method === 0 ? contents : (0, node_zlib_1.inflateRawSync)(contents, { maxOutputLength: 16384 });
+ }
+ catch {
+ return undefined;
+ }
+ if (raw.length !== plainSize)
+ return undefined;
+ let parsed;
+ try {
+ parsed = JSON.parse(raw.toString('utf8'));
+ }
+ catch {
+ return undefined;
+ }
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
+ return undefined;
+ const item = parsed;
+ const keys = [
+ 'version', 'repositoryId', 'pullNumber', 'headSha', 'baseSha', 'workflowRunId',
+ 'workflowRunAttempt', 'coveredChangedLines', 'totalChangedLines',
+ ];
+ if (Object.keys(item).sort().join(',') !== keys.sort().join(',') || item.version !== 1
+ || ![item.repositoryId, item.pullNumber, item.workflowRunId, item.workflowRunAttempt,
+ item.coveredChangedLines, item.totalChangedLines].every(Number.isSafeInteger)
+ || !/^[a-f0-9]{40}$/iu.test(String(item.headSha)) || !/^[a-f0-9]{40}$/iu.test(String(item.baseSha))
+ || Number(item.repositoryId) <= 0 || Number(item.pullNumber) <= 0 || Number(item.workflowRunId) <= 0
+ || Number(item.workflowRunAttempt) <= 0 || Number(item.totalChangedLines) < 0
+ || Number(item.coveredChangedLines) < 0 || Number(item.coveredChangedLines) > Number(item.totalChangedLines))
+ return undefined;
+ return item;
+}
+
+
/***/ }),
/***/ 55165:
@@ -77985,6 +78778,404 @@ class BugbotPullRequestRepository {
exports.BugbotPullRequestRepository = BugbotPullRequestRepository;
+/***/ }),
+
+/***/ 23001:
+/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
+
+"use strict";
+
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+ o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || (function () {
+ var ownKeys = function(o) {
+ ownKeys = Object.getOwnPropertyNames || function (o) {
+ var ar = [];
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
+ return ar;
+ };
+ return ownKeys(o);
+ };
+ return function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+})();
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.PullRequestApprovalRepository = void 0;
+exports.readEffectiveApprovalPolicy = readEffectiveApprovalPolicy;
+const github = __importStar(__nccwpck_require__(78227));
+const pull_request_approval_policy_1 = __nccwpck_require__(98820);
+const approval_coverage_artifact_1 = __nccwpck_require__(96710);
+const file_ignore_policy_1 = __nccwpck_require__(20542);
+const pull_request_approval_presentation_policy_1 = __nccwpck_require__(79017);
+/** GitHub DTOs terminate here. Every partial/forbidden read becomes non-authorizing evidence. */
+class PullRequestApprovalRepository {
+ constructor(token, settings) {
+ this.token = token;
+ this.settings = settings;
+ this.octokit = github.getOctokit(token);
+ this.checkOctokit = settings.githubToken ? github.getOctokit(settings.githubToken) : undefined;
+ }
+ /** Read-only source evidence; failure never consults an injected policy value. */
+ async readPolicyEvidence(target) {
+ try {
+ const read = await this.readEffectivePolicy(target);
+ return Object.freeze(read ? { status: 'available', ...read } : { status: 'absent' });
+ }
+ catch {
+ return Object.freeze({ status: 'unavailable' });
+ }
+ }
+ async readPolicy(target) {
+ return (await this.readEffectivePolicy(target))?.value;
+ }
+ async readEffectivePolicy(target) {
+ return readEffectiveApprovalPolicy(this.octokit, target);
+ }
+ async loadEvidence(target) {
+ const { data: pull } = await this.octokit.rest.pulls.get({
+ owner: target.owner, repo: target.repository, pull_number: target.pullNumber,
+ });
+ const headSha = pull.head.sha;
+ const policy = (0, pull_request_approval_policy_1.parsePullRequestApprovalPolicy)(await this.readPolicy(target));
+ let testedMergeSha;
+ if (pull.mergeable === true && /^[a-f0-9]{40}$/iu.test(pull.merge_commit_sha ?? '')) {
+ try {
+ const merge = await this.octokit.rest.git.getCommit({
+ owner: target.owner, repo: target.repository, commit_sha: pull.merge_commit_sha,
+ });
+ if (merge.data.parents[0]?.sha === pull.base.sha && merge.data.parents[1]?.sha === headSha
+ && merge.data.parents.length === 2)
+ testedMergeSha = pull.merge_commit_sha;
+ }
+ catch { /* Missing or unreadable merge parentage is non-authorizing. */ }
+ }
+ const [files, reviews, comments, headChecks, mergeChecks, headRuns, mergeRuns, rules, user] = await Promise.all([
+ this.allPages(this.octokit.rest.pulls.listFiles, { owner: target.owner, repo: target.repository, pull_number: target.pullNumber }),
+ this.allPages(this.octokit.rest.pulls.listReviews, { owner: target.owner, repo: target.repository, pull_number: target.pullNumber }),
+ this.allPages(this.octokit.rest.issues.listComments, { owner: target.owner, repo: target.repository, issue_number: target.pullNumber }),
+ this.allPages(this.octokit.rest.checks.listForRef, { owner: target.owner, repo: target.repository, ref: headSha, filter: 'all' }),
+ testedMergeSha ? this.allPages(this.octokit.rest.checks.listForRef, { owner: target.owner, repo: target.repository, ref: testedMergeSha, filter: 'all' }) : Promise.resolve([]),
+ this.allPages(this.octokit.rest.actions.listWorkflowRunsForRepo, { owner: target.owner, repo: target.repository, head_sha: headSha }),
+ testedMergeSha ? this.allPages(this.octokit.rest.actions.listWorkflowRunsForRepo, { owner: target.owner, repo: target.repository, head_sha: testedMergeSha }) : Promise.resolve([]),
+ this.effectiveRules(target.owner, target.repository, pull.base.ref),
+ this.octokit.rest.users.getAuthenticated(),
+ ]);
+ const branch = Object.entries(this.settings.branchPrefixes)
+ .find(([, prefix]) => pull.head.ref.startsWith(`${prefix}/`));
+ const branchKind = branch?.[0];
+ const branchIssue = branch
+ ? Number(pull.head.ref.slice(branch[1].length + 1).match(/^(\d+)-/u)?.[1])
+ : NaN;
+ const linkedIssue = Number.isSafeInteger(branchIssue) && branchIssue > 0
+ && new RegExp(`\\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\\s+#${branchIssue}\\b`, 'iu').test(pull.body ?? '');
+ const checkRuns = [...headChecks, ...mergeChecks];
+ const workflowRuns = [...headRuns, ...mergeRuns];
+ const latestRuns = [...new Set(policy.testChecks.map(producer => producer.workflowName))].flatMap(name => {
+ const named = workflowRuns.filter(run => run.name === name);
+ const onMerge = named.filter(run => run.head_sha === testedMergeSha);
+ const authoritative = onMerge.length > 0 ? onMerge : named.filter(run => run.head_sha === headSha);
+ const latest = [...authoritative].sort((left, right) => right.id - left.id || right.run_attempt - left.run_attempt)[0];
+ return latest ? [latest] : [];
+ });
+ const currentJobs = await Promise.all(latestRuns.map(async (run) => ({
+ run,
+ jobs: await this.allPages(this.octokit.rest.actions.listJobsForWorkflowRunAttempt, {
+ owner: target.owner, repo: target.repository, run_id: run.id, attempt_number: run.run_attempt,
+ }),
+ })));
+ const checkFacts = currentJobs.flatMap(({ run, jobs }) => jobs.flatMap(job => {
+ const id = Number(job.check_run_url?.match(/\/check-runs\/(\d+)$/u)?.[1]);
+ const check = checkRuns.find(item => item.id === id && item.head_sha === run.head_sha);
+ if (!check)
+ return [];
+ return [{
+ name: check.name,
+ sourceAppId: check.app?.id ?? 0,
+ workflowName: run.name,
+ headSha: check.head_sha,
+ conclusion: run.conclusion === 'success' ? check.conclusion : run.conclusion,
+ status: run.status === 'completed' ? check.status : run.status,
+ runId: run.id,
+ attempt: run.run_attempt,
+ }];
+ }));
+ const botId = user.data.id;
+ const botCards = comments.filter((comment) => comment.user?.id === botId && /'));
+ const body = (0, pull_request_approval_presentation_policy_1.renderApprovalAssessment)({ target, evidence, decision, locale: this.settings.locale ?? 'en-US',
+ ...(reviewId ? { reviewId } : {}) });
+ if (card) {
+ if (card.body !== body)
+ await this.octokit.rest.issues.updateComment({ owner: target.owner, repo: target.repository, comment_id: card.id, body });
+ }
+ else {
+ await this.octokit.rest.issues.createComment({ owner: target.owner, repo: target.repository, issue_number: target.pullNumber, body });
+ }
+ if (this.checkOctokit) {
+ const prior = await this.checkOctokit.rest.checks.listForRef({
+ owner: target.owner, repo: target.repository, ref: headSha, check_name: 'Copilot / Approval', filter: 'all', per_page: 100,
+ });
+ if (prior.data.total_count > 100)
+ throw new Error('Approval Check Run history exceeds the bounded lookup.');
+ const externalId = `copilot:approval:${target.repositoryId}:${target.pullNumber}`;
+ const matches = prior.data.check_runs.filter((check) => check.name === 'Copilot / Approval'
+ && check.app?.slug === 'github-actions' && check.external_id === externalId);
+ if (matches.length > 1)
+ throw new Error('Approval Check Run identity is ambiguous.');
+ const existing = matches[0];
+ const fields = {
+ owner: target.owner, repo: target.repository, name: 'Copilot / Approval', head_sha: headSha,
+ external_id: externalId,
+ status: 'completed',
+ conclusion: decision.code === 'approved' || decision.status === 'already-approved' ? 'success'
+ : decision.status === 'blocked' ? 'failure' : 'neutral',
+ output: { title: `PR approval: ${decision.status}`, summary: body.slice(0, 20000) },
+ };
+ if (existing) {
+ await this.checkOctokit.rest.checks.update({
+ owner: target.owner, repo: target.repository, check_run_id: existing.id,
+ status: fields.status, conclusion: fields.conclusion, output: fields.output,
+ });
+ }
+ else {
+ await this.checkOctokit.rest.checks.create(fields);
+ }
+ }
+ }
+ async effectiveRules(owner, repo, branch) {
+ try {
+ const rulesResponse = await this.octokit.request('GET /repos/{owner}/{repo}/rules/branches/{branch}', { owner, repo, branch });
+ const rules = Array.isArray(rulesResponse.data) ? rulesResponse.data : [];
+ let classic;
+ try {
+ const response = await this.octokit.rest.repos.getBranchProtection({ owner, repo, branch });
+ classic = response.data;
+ }
+ catch (error) {
+ if (statusOf(error) !== 404)
+ throw error;
+ }
+ const reviewRules = rules.filter(rule => rule.type === 'pull_request');
+ const classicReview = record(classic?.required_pull_request_reviews);
+ const allStale = reviewRules.every(rule => record(rule.parameters)?.dismiss_stale_reviews === true)
+ && (!classicReview || classicReview.dismiss_stale_reviews === true);
+ const hasStaleRule = reviewRules.length > 0 || classicReview !== undefined;
+ const requiredChecks = new Set();
+ const classicChecks = record(classic?.required_status_checks);
+ for (const item of array(classicChecks?.contexts))
+ if (typeof item === 'string')
+ requiredChecks.add(item);
+ for (const item of array(classicChecks?.checks)) {
+ const context = record(item)?.context;
+ if (typeof context === 'string')
+ requiredChecks.add(context);
+ }
+ for (const rule of rules.filter(item => item.type === 'required_status_checks')) {
+ for (const item of array(record(rule.parameters)?.required_status_checks)) {
+ const context = record(item)?.context;
+ if (typeof context === 'string')
+ requiredChecks.add(context);
+ }
+ }
+ return { readable: true, dismissesStaleReviews: hasStaleRule && allStale, requiredChecks: [...requiredChecks] };
+ }
+ catch {
+ return { readable: false, dismissesStaleReviews: false, requiredChecks: [] };
+ }
+ }
+ async allPages(method, params) {
+ const result = [];
+ for await (const response of this.octokit.paginate.iterator(method, { ...params, per_page: 100 })) {
+ const page = Array.isArray(response.data) ? response.data
+ : record(response.data)?.check_runs ?? record(response.data)?.workflow_runs
+ ?? record(response.data)?.artifacts ?? record(response.data)?.jobs;
+ if (!Array.isArray(page))
+ throw new Error('GitHub pagination returned an invalid page.');
+ result.push(...page);
+ if (result.length > 3000)
+ throw new Error('Approval evidence pagination exceeded the safe bound.');
+ }
+ return result;
+ }
+}
+exports.PullRequestApprovalRepository = PullRequestApprovalRepository;
+function parseBugbotCard(body, headSha, pullNumber) {
+ const status = body.match(//u);
+ const evidence = body.match(//u);
+ if (!status || !evidence || Number(status[1]) !== pullNumber || status[2] !== headSha
+ || evidence[1] !== headSha || status[3] !== evidence[2])
+ return undefined;
+ const counts = evidence.slice(5).map(Number);
+ if (counts.some(count => !Number.isSafeInteger(count)))
+ return undefined;
+ return {
+ headSha, outcome: evidence[3], coverage: evidence[4],
+ open: counts[0], reopened: counts[1], dismissed: counts[2],
+ verificationRequired: counts[3], unknown: counts[4],
+ };
+}
+function statusOf(error) {
+ return record(error)?.status;
+}
+function record(value) {
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
+}
+function array(value) { return Array.isArray(value) ? value : []; }
+/** Canonical repository-over-organization read shared with the physically read-only doctor binding. */
+async function readEffectiveApprovalPolicy(octokit, target) {
+ const admit = (source, value) => {
+ if (typeof value !== 'string' || value.length > 65536)
+ throw new Error('Approval policy response is malformed or unbounded.');
+ return { source, value };
+ };
+ try {
+ const response = await octokit.rest.actions.getRepoVariable({ owner: target.owner, repo: target.repository, name: 'PR_APPROVAL_POLICY' });
+ return admit('repository', response.data.value);
+ }
+ catch (error) {
+ if (statusOf(error) !== 404)
+ throw error;
+ }
+ try {
+ const response = await octokit.rest.actions.getOrgVariable({ org: target.owner, name: 'PR_APPROVAL_POLICY' });
+ return admit('organization', response.data.value);
+ }
+ catch (error) {
+ if (statusOf(error) === 404)
+ return undefined;
+ throw error;
+ }
+}
+
+
/***/ }),
/***/ 71564:
@@ -80571,6 +81762,7 @@ exports.COPILOT_COMMAND_NAMES = [
'estimate',
'test-plan',
'status',
+ 'doctor',
'description',
'explain',
'diagnose',
@@ -80627,6 +81819,9 @@ function parseCopilotCommand(raw) {
if (argumentsList.length > MAX_ARGUMENTS) {
return { kind: 'invalid', reason: `Copilot commands accept at most ${MAX_ARGUMENTS} arguments.` };
}
+ if (name === 'doctor' && argumentsList.length > 0) {
+ return { kind: 'invalid', reason: '/copilot doctor does not accept arguments.' };
+ }
if ((name === 'fix' || name === 'dismiss' || name === 'implement' || name === 'remember') && argumentsList.length === 0) {
return { kind: 'invalid', reason: `/${name} requires at least one argument.` };
}
@@ -81357,6 +82552,71 @@ function publicationTargetToken(target) {
}
+/***/ }),
+
+/***/ 20413:
+/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.GITHUB_RUNTIME_DOCTOR_GROUPS = void 0;
+exports.parseTrustedDoctorApprovalPolicy = parseTrustedDoctorApprovalPolicy;
+exports.aggregateGithubRuntimeDoctorReport = aggregateGithubRuntimeDoctorReport;
+const pull_request_approval_policy_1 = __nccwpck_require__(98820);
+// Domain contracts and trusted policy parsing for the issue/PR runtime doctor.
+/** The fixed, ordered evidence groups in the GitHub runtime doctor contract. */
+exports.GITHUB_RUNTIME_DOCTOR_GROUPS = [
+ 'effective-configuration',
+ 'issue-readiness',
+ 'linked-branch-readiness',
+ 'workflow-contracts',
+ 'approval-policy',
+ 'approval-prerequisites',
+ 'pull-request-identity',
+];
+/**
+ * Parses only a successful trusted effective read. Verified absence preserves the
+ * established off default; read failure is unavailable and never consults an injected value.
+ */
+function parseTrustedDoctorApprovalPolicy(read) {
+ if (read.status === 'unavailable')
+ return { status: 'unavailable' };
+ if (read.status === 'absent') {
+ return { status: 'valid', source: 'default-off', policy: pull_request_approval_policy_1.DISABLED_PULL_REQUEST_APPROVAL_POLICY };
+ }
+ try {
+ return { status: 'valid', source: read.source, policy: (0, pull_request_approval_policy_1.parsePullRequestApprovalPolicy)(read.value) };
+ }
+ catch {
+ // Do not leak parser/provider text into a report or user-facing surface.
+ return { status: 'invalid' };
+ }
+}
+/** Aggregate explicit independent evidence. Missing groups are unavailable, never assumed healthy. */
+function aggregateGithubRuntimeDoctorReport(input) {
+ const byGroup = new Map();
+ for (const check of input.checks) {
+ if (byGroup.has(check.group))
+ throw new Error(`Duplicate GitHub runtime doctor group: ${check.group}.`);
+ byGroup.set(check.group, Object.freeze({ ...check, ...(check.evidence ? { evidence: Object.freeze({ ...check.evidence }) } : {}) }));
+ }
+ const checks = exports.GITHUB_RUNTIME_DOCTOR_GROUPS.map(group => byGroup.get(group)
+ ?? Object.freeze({ group, state: 'unavailable' }));
+ const priority = ['blocked', 'unavailable', 'warning', 'pass'];
+ const applicable = checks.filter(check => check.state !== 'not-applicable');
+ const severity = applicable.length === 0 ? 'not-applicable'
+ : priority.find(state => applicable.some(check => check.state === state)) ?? 'unavailable';
+ const primaryAction = applicable.find(check => check.state === severity && check.action)?.action;
+ return Object.freeze({
+ target: Object.freeze({ ...input.target }),
+ checks: Object.freeze(checks),
+ severity,
+ ...(primaryAction ? { primaryAction } : {}),
+ });
+}
+
+
/***/ }),
/***/ 84403:
@@ -84788,6 +86048,29 @@ const createRepositoryVersionTagsClient = () => new octokit_release_adapters_1.O
exports.createRepositoryVersionTagsClient = createRepositoryVersionTagsClient;
+/***/ }),
+
+/***/ 36459:
+/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.createGithubRuntimeDoctorPorts = createGithubRuntimeDoctorPorts;
+const runtime_doctor_repository_1 = __nccwpck_require__(25282);
+function createGithubRuntimeDoctorPorts(binding, pullRequests) {
+ const repository = new runtime_doctor_repository_1.RuntimeDoctorRepository(binding.owner, binding.repository, binding.token);
+ return Object.freeze({ serverOrigin: process.env.GITHUB_SERVER_URL ?? 'https://github.com',
+ readRepository: (id) => repository.readRepository(id),
+ readIssue: (number) => repository.readIssue(number),
+ readLinkedBranch: (number) => repository.readLinkedBranch(number),
+ readApprovalPolicy: () => repository.readApprovalPolicy(),
+ pullRequests: Object.freeze({ getPullRequest: (number) => pullRequests.getPullRequest(number),
+ getPullRequestHeadSha: (number) => pullRequests.getPullRequestHeadSha(number) }),
+ });
+}
+
+
/***/ }),
/***/ 29839:
@@ -85207,6 +86490,7 @@ exports.createIssueCommentUseCaseCompositionRoot = createIssueCommentUseCaseComp
exports.createPullRequestReviewCommentUseCaseCompositionRoot = createPullRequestReviewCommentUseCaseCompositionRoot;
exports.createCommitUseCaseCompositionRoot = createCommitUseCaseCompositionRoot;
exports.createMainRunRouteCompositionRoot = createMainRunRouteCompositionRoot;
+const github_runtime_doctor_composition_root_1 = __nccwpck_require__(36459);
const linked_branch_readiness_repository_1 = __nccwpck_require__(79421);
const pull_request_changes_repository_1 = __nccwpck_require__(71564);
const commit_use_case_1 = __nccwpck_require__(28001);
@@ -85324,7 +86608,7 @@ function createIssueCommentUseCaseCompositionRoot(binding) {
contextPorts: bugbot.scm.context,
resolutionPorts: bugbot.scm.resolution,
catalogResolver: new resolve_message_catalog_use_case_1.ResolveMessageCatalogUseCase(language),
- }), new detect_potential_problems_use_case_1.DetectPotentialProblemsUseCase(findings, bugbot.scm, bugbot.telemetry, new resolve_message_catalog_use_case_1.ResolveMessageCatalogUseCase(language)), pullRequestDescription, new remember_bugbot_rule_use_case_1.RememberBugbotRuleUseCase(bugbot.rules), branchSync, (0, issue_use_case_composition_root_1.createIssueUseCaseCompositionRoot)(binding), rememberBranchPorts(binding, bugbot.scm.context), bugbot.scm.context);
+ }), new detect_potential_problems_use_case_1.DetectPotentialProblemsUseCase(findings, bugbot.scm, bugbot.telemetry, new resolve_message_catalog_use_case_1.ResolveMessageCatalogUseCase(language)), pullRequestDescription, new remember_bugbot_rule_use_case_1.RememberBugbotRuleUseCase(bugbot.rules), branchSync, (0, issue_use_case_composition_root_1.createIssueUseCaseCompositionRoot)(binding), rememberBranchPorts(binding, bugbot.scm.context), bugbot.scm.context, (0, github_runtime_doctor_composition_root_1.createGithubRuntimeDoctorPorts)(binding, bugbot.scm.context));
}
function createPullRequestReviewCommentUseCaseCompositionRoot(binding) {
const bugbot = (0, bugbot_composition_root_1.createBugbotCompositionRoot)(binding);
@@ -85340,7 +86624,7 @@ function createPullRequestReviewCommentUseCaseCompositionRoot(binding) {
contextPorts: bugbot.scm.context,
resolutionPorts: bugbot.scm.resolution,
catalogResolver: new resolve_message_catalog_use_case_1.ResolveMessageCatalogUseCase(language),
- }), new detect_potential_problems_use_case_1.DetectPotentialProblemsUseCase(findings, bugbot.scm, bugbot.telemetry, new resolve_message_catalog_use_case_1.ResolveMessageCatalogUseCase(language)), pullRequestDescription, new remember_bugbot_rule_use_case_1.RememberBugbotRuleUseCase(bugbot.rules), branchSync, rememberBranchPorts(binding, bugbot.scm.context), bugbot.scm.context);
+ }), new detect_potential_problems_use_case_1.DetectPotentialProblemsUseCase(findings, bugbot.scm, bugbot.telemetry, new resolve_message_catalog_use_case_1.ResolveMessageCatalogUseCase(language)), pullRequestDescription, new remember_bugbot_rule_use_case_1.RememberBugbotRuleUseCase(bugbot.rules), branchSync, rememberBranchPorts(binding, bugbot.scm.context), bugbot.scm.context, (0, github_runtime_doctor_composition_root_1.createGithubRuntimeDoctorPorts)(binding, bugbot.scm.context));
}
function createCommitUseCaseCompositionRoot(projectBoardCommandPort, binding) {
return new commit_use_case_1.CommitUseCase(new notify_new_commit_on_issue_use_case_1.NotifyNewCommitOnIssueUseCase((0, push_single_action_capability_port_binding_1.bindIssueReopen)((0, issue_interaction_composition_root_1.createIssueNotificationRepository)(), binding)), new check_changes_issue_size_use_case_1.CheckChangesIssueSizeUseCase((0, lifecycle_capability_port_binding_2.bindProjectBoardCommands)(projectBoardCommandPort, binding), (0, lifecycle_capability_port_binding_2.bindIssueLabels)((0, issue_labels_composition_root_1.createIssueLabelRepository)(), binding), (0, push_single_action_capability_port_binding_1.bindPullRequestBranchQuery)(new pull_request_lifecycle_repository_1.PullRequestLifecycleRepository((0, github_pull_request_client_factory_1.createPullRequestLifecycleClient)()), binding), (0, push_single_action_capability_port_binding_1.bindBranchChangeSize)(new branch_compare_repository_1.BranchCompareRepository((0, github_branch_client_factory_1.createBranchComparisonClient)()), binding)), createDetectPotentialProblemsUseCase(binding), (0, check_progress_composition_root_1.createCheckProgressCompositionRoot)(binding), (0, actor_authorization_composition_root_1.createActorAuthorizationRepository)());
diff --git a/build/github_action/index.js b/build/github_action/index.js
index 479830a7c..d4edf3442 100644
--- a/build/github_action/index.js
+++ b/build/github_action/index.js
@@ -43227,6 +43227,7 @@ const READ_ONLY_COMMANDS = Object.freeze([
['/copilot recheck', 'interaction.help.command.recheck'],
['/copilot description', 'interaction.help.command.description'],
['/copilot status', 'interaction.help.command.status'],
+ ['/copilot doctor', 'interaction.help.command.doctor'],
]);
const CHANGE_COMMANDS = Object.freeze([
['/copilot fix ', 'interaction.help.command.fixOne'],
@@ -44326,6 +44327,185 @@ function resolveGithubExecutionAdmission(input) {
}
+/***/ }),
+
+/***/ 99566:
+/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.validDoctorConfiguration = validDoctorConfiguration;
+exports.doctorRepositoryPrerequisites = doctorRepositoryPrerequisites;
+exports.doctorWorkflowContracts = doctorWorkflowContracts;
+const js_yaml_1 = __nccwpck_require__(783);
+const issue_workflow_profile_1 = __nccwpck_require__(26744);
+const issue_start_policy_1 = __nccwpck_require__(90332);
+function validDoctorConfiguration(value) {
+ if (!value || typeof value.preBranchSdd !== 'boolean' || typeof value.issueManagedBranches !== 'boolean'
+ || !/^(?!-|.*(?:\.\.|[\s~^:?*[\]\\]))[A-Za-z0-9._/-]{1,200}$/u.test(value.dependencyBranch))
+ return false;
+ try {
+ if ('error' in (0, issue_workflow_profile_1.parseIssueWorkflowProfile)(JSON.stringify(value.profile)))
+ return false;
+ (0, issue_start_policy_1.decideIssueStart)({ labels: [], issueManagedBranches: value.issueManagedBranches, preBranchSdd: value.preBranchSdd });
+ return true;
+ }
+ catch {
+ return false;
+ }
+}
+function doctorRepositoryPrerequisites(read) {
+ if (read.state !== 'available')
+ return 'unavailable';
+ const paths = ['specs/catalog.json', 'specs/_template.md', 'specs/README.md'];
+ if (paths.some(path => read.files[path]?.state !== 'present'))
+ return 'blocked';
+ const catalogFile = read.files['specs/catalog.json'];
+ if (catalogFile.state !== 'present')
+ return 'blocked';
+ try {
+ const catalog = object(JSON.parse(catalogFile.text));
+ if (catalog.version !== 1 || !Array.isArray(catalog.capabilities))
+ return 'blocked';
+ const ids = new Set();
+ for (const raw of catalog.capabilities) {
+ const capability = object(raw);
+ if (typeof capability.id !== 'string' || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(capability.id)
+ || ids.has(capability.id) || !Array.isArray(capability.specs) || capability.specs.length === 0
+ || capability.specs.some(path => typeof path !== 'string' || !/^specs\/[a-z0-9_-]+\.md$/u.test(path)))
+ return 'blocked';
+ ids.add(capability.id);
+ }
+ return paths.slice(1).every(path => {
+ const file = read.files[path];
+ return file.state === 'present' && /^#\s+\S/mu.test(file.text);
+ }) ? 'pass' : 'blocked';
+ }
+ catch {
+ return 'blocked';
+ }
+}
+/** Structural evidence only. YAML is parsed with the JSON schema and never executed. */
+function doctorWorkflowContracts(read) {
+ if (read.state !== 'available')
+ return 'unavailable';
+ const expected = [
+ ['.github/workflows/copilot_issue.yml', 'issues'],
+ ['.github/workflows/copilot_issue_comment.yml', 'issue_comment'],
+ ['.github/workflows/copilot_pull_request.yml', 'pull_request'],
+ ];
+ try {
+ return expected.every(([path, event]) => {
+ const file = read.files[path];
+ if (file?.state !== 'present')
+ return false;
+ const document = object((0, js_yaml_1.load)(file.text, { schema: js_yaml_1.JSON_SCHEMA }));
+ const events = document.on;
+ const admitted = typeof events === 'string' ? events === event : Array.isArray(events)
+ ? events.includes(event) : Object.prototype.hasOwnProperty.call(object(events), event);
+ return admitted && Object.values(object(document.jobs)).some(job => {
+ const steps = object(job).steps;
+ return Array.isArray(steps) && steps.some(raw => {
+ const step = object(raw);
+ return step.uses === './' || typeof step.uses === 'string'
+ && /^vypdev\/copilot@[^\s${}<>]+$/iu.test(step.uses);
+ });
+ });
+ }) ? 'pass' : 'blocked';
+ }
+ catch {
+ return 'blocked';
+ }
+}
+function object(value) {
+ return value !== null && typeof value === 'object' && !Array.isArray(value) ? value : {};
+}
+
+
+/***/ }),
+
+/***/ 2808:
+/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.isGithubRuntimeDoctorProjection = isGithubRuntimeDoctorProjection;
+exports.formatGithubRuntimeDoctor = formatGithubRuntimeDoctor;
+const github_runtime_doctor_1 = __nccwpck_require__(20413);
+const publication_message_catalog_1 = __nccwpck_require__(34223);
+const ACTIONS = ['configuration', 'workflow', 'retry', 'issue', 'specifications', 'branch', 'policy', 'declared'];
+const STATES = ['pass', 'warning', 'blocked', 'unavailable', 'not-applicable'];
+function isGithubRuntimeDoctorProjection(value) {
+ if (!value || typeof value !== 'object' || Array.isArray(value))
+ return false;
+ const projection = value;
+ const report = projection.report;
+ if (['serverOrigin', 'revision', 'owner', 'repository'].some(key => {
+ const field = projection[key];
+ return field !== undefined && (typeof field !== 'string' || field.length > 300);
+ }))
+ return false;
+ if (report === undefined)
+ return true;
+ if (!report || typeof report !== 'object' || !report.target || !Number.isSafeInteger(report.target.repositoryId)
+ || report.target.repositoryId < 1 || (report.target.kind !== 'issue' && report.target.kind !== 'pull-request')
+ || !Number.isSafeInteger(report.target.kind === 'issue' ? report.target.issueNumber : report.target.pullNumber)
+ || (report.target.kind === 'issue' ? report.target.issueNumber : report.target.pullNumber) < 1)
+ return false;
+ return STATES.includes(report.severity) && Array.isArray(report.checks) && report.checks.length === 7
+ && report.checks.every((check, index) => check && check.group === github_runtime_doctor_1.GITHUB_RUNTIME_DOCTOR_GROUPS[index] && STATES.includes(check.state))
+ && (report.primaryAction === undefined || ACTIONS.includes(report.primaryAction));
+}
+function formatGithubRuntimeDoctor(projection, locale = 'en-US', catalog = (0, publication_message_catalog_1.resolveStaticPublicationCatalog)(locale).catalog) {
+ if (!isGithubRuntimeDoctorProjection(projection))
+ projection = {};
+ const message = (suffix) => catalog.render(`interaction.doctor.${suffix}`);
+ const severity = projection.report?.severity ?? 'unavailable';
+ const action = projection.report?.primaryAction;
+ const lines = [`## 🩺 ${message('heading')}: ${message(`state.${severity}`)}`, '',
+ message(`summary.${severity}`), '',
+ `**${message('action')}:** ${action && ACTIONS.includes(action) ? message(`action.${action}`)
+ : severity === 'pass' || severity === 'not-applicable' ? message('action.none') : message('action.retry')}`,
+ '', message('readonly'), '', `### ${message('checks')}`, ''];
+ const source = repositorySource(projection);
+ const origin = source?.split('/').slice(0, 5).join('/');
+ for (const group of github_runtime_doctor_1.GITHUB_RUNTIME_DOCTOR_GROUPS) {
+ const state = projection.report?.checks.find(check => check.group === group)?.state ?? 'unavailable';
+ const evidence = projection.report?.checks.find(check => check.group === group)?.evidence?.source;
+ const target = projection.report?.target;
+ const link = origin && target && group === 'pull-request-identity' && target.kind === 'pull-request'
+ ? `${origin}/pull/${target.pullNumber}` : origin && target && target.kind === 'issue'
+ && (group === 'issue-readiness' || group === 'linked-branch-readiness') ? `${origin}/issues/${target.issueNumber}` : undefined;
+ const policySource = group === 'approval-policy' && (evidence === 'repository' || evidence === 'organization' || evidence === 'default-off')
+ ? message(`source.${evidence}`) : undefined;
+ const mode = group === 'approval-prerequisites' && (evidence === 'off' || evidence === 'recommend' || evidence === 'guarded')
+ ? `${message('mode')}: ${evidence}` : undefined;
+ lines.push(`- **${message(`group.${group}`)}:** ${message(`state.${state}`)}${policySource ? ` · ${policySource}` : ''}${mode ? ` · ${mode}` : ''}${link ? ` · [${message('source')}](${link})` : ''}`);
+ }
+ if (source)
+ lines.push('', `[${message('source')}](${source})`);
+ return lines.join('\n');
+}
+function repositorySource(value) {
+ if (!value.serverOrigin || !value.revision || !/^[a-f0-9]{40}$/u.test(value.revision)
+ || !value.owner || !value.repository || !/^[A-Za-z0-9][A-Za-z0-9-]{0,38}$/u.test(value.owner)
+ || !/^[A-Za-z0-9][A-Za-z0-9_.-]{0,99}$/u.test(value.repository))
+ return undefined;
+ try {
+ const url = new URL(value.serverOrigin);
+ if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash
+ || url.pathname !== '/' || url.origin !== value.serverOrigin.replace(/\/$/u, ''))
+ return undefined;
+ return `${url.origin}/${value.owner}/${value.repository}/tree/${value.revision}`;
+ }
+ catch {
+ return undefined;
+ }
+}
+
+
/***/ }),
/***/ 30203:
@@ -45244,6 +45424,7 @@ const PUBLICATION_SURFACE_MESSAGE_IDS = Object.freeze([
'interaction.help.command.recheck',
'interaction.help.command.description',
'interaction.help.command.status',
+ 'interaction.help.command.doctor',
'interaction.help.command.fixOne',
'interaction.help.command.fixAll',
'interaction.help.command.dismiss',
@@ -45271,6 +45452,41 @@ const PUBLICATION_SURFACE_MESSAGE_IDS = Object.freeze([
'interaction.branchSync.merged',
'interaction.branchSync.agentResolution',
'interaction.branchSync.verification',
+ 'interaction.doctor.source.repository',
+ 'interaction.doctor.source.organization',
+ 'interaction.doctor.source.default-off',
+ 'interaction.doctor.mode',
+ 'interaction.doctor.heading',
+ 'interaction.doctor.action',
+ 'interaction.doctor.checks',
+ 'interaction.doctor.readonly',
+ 'interaction.doctor.source',
+ 'interaction.doctor.group.effective-configuration',
+ 'interaction.doctor.group.issue-readiness',
+ 'interaction.doctor.group.linked-branch-readiness',
+ 'interaction.doctor.group.workflow-contracts',
+ 'interaction.doctor.group.approval-policy',
+ 'interaction.doctor.group.approval-prerequisites',
+ 'interaction.doctor.group.pull-request-identity',
+ 'interaction.doctor.action.configuration',
+ 'interaction.doctor.action.workflow',
+ 'interaction.doctor.action.retry',
+ 'interaction.doctor.action.issue',
+ 'interaction.doctor.action.specifications',
+ 'interaction.doctor.action.branch',
+ 'interaction.doctor.action.policy',
+ 'interaction.doctor.action.declared',
+ 'interaction.doctor.action.none',
+ 'interaction.doctor.state.pass',
+ 'interaction.doctor.summary.pass',
+ 'interaction.doctor.state.blocked',
+ 'interaction.doctor.summary.blocked',
+ 'interaction.doctor.state.unavailable',
+ 'interaction.doctor.summary.unavailable',
+ 'interaction.doctor.state.warning',
+ 'interaction.doctor.summary.warning',
+ 'interaction.doctor.state.not-applicable',
+ 'interaction.doctor.summary.not-applicable',
'interaction.status.heading',
'interaction.status.repository',
'interaction.status.target',
@@ -45342,6 +45558,7 @@ const ENGLISH_MESSAGES = Object.freeze({
'interaction.help.command.recheck': 're-run the review and reconcile findings.',
'interaction.help.command.description': 'refresh the pull-request description.',
'interaction.help.command.status': 'show the current automation status.',
+ 'interaction.help.command.doctor': 'check available runtime prerequisites without making repository changes.',
'interaction.help.command.fixOne': 'fix one reported finding.',
'interaction.help.command.fixAll': 'fix all unresolved findings.',
'interaction.help.command.dismiss': 'dismiss a finding.',
@@ -45381,6 +45598,41 @@ const ENGLISH_MESSAGES = Object.freeze({
one: '{count} verification check passed.',
other: '{count} verification checks passed.',
}),
+ 'interaction.doctor.source.repository': "Repository variable",
+ 'interaction.doctor.source.organization': "Organization variable",
+ 'interaction.doctor.source.default-off': "Verified absence; default off",
+ 'interaction.doctor.mode': "Mode",
+ 'interaction.doctor.heading': "Runtime doctor",
+ 'interaction.doctor.action': "Next action",
+ 'interaction.doctor.checks': "Checks",
+ 'interaction.doctor.readonly': "This command reads evidence only. Declared approval settings do not prove producer health or write access.",
+ 'interaction.doctor.source': "Inspected repository revision",
+ 'interaction.doctor.group.effective-configuration': "Effective configuration",
+ 'interaction.doctor.group.issue-readiness': "Issue and SDD prerequisites",
+ 'interaction.doctor.group.linked-branch-readiness': "Native linked branch",
+ 'interaction.doctor.group.workflow-contracts': "Installed workflow structure",
+ 'interaction.doctor.group.approval-policy': "Trusted approval policy read",
+ 'interaction.doctor.group.approval-prerequisites': "Declared approval prerequisites",
+ 'interaction.doctor.group.pull-request-identity': "Exact current PR identity",
+ 'interaction.doctor.action.configuration': "Inspect the effective workflow configuration and correct the invalid setting.",
+ 'interaction.doctor.action.workflow': "Inspect the linked workflow revision and correct its required structure.",
+ 'interaction.doctor.action.retry': "Inspect this workflow run and retry /copilot doctor.",
+ 'interaction.doctor.action.issue': "Inspect the issue form and its routing labels.",
+ 'interaction.doctor.action.specifications': "Inspect the catalog, template and specification standard at the linked revision.",
+ 'interaction.doctor.action.branch': "Inspect the issue’s native linked branch and retry after branch readiness.",
+ 'interaction.doctor.action.policy': "Inspect the trusted Actions Variables approval policy source.",
+ 'interaction.doctor.action.declared': "Verify the declared producer prerequisites using the approval setup documentation.",
+ 'interaction.doctor.action.none': "No action required for the inspected prerequisites.",
+ 'interaction.doctor.state.pass': "Pass",
+ 'interaction.doctor.summary.pass': "The inspected applicable prerequisites passed.",
+ 'interaction.doctor.state.blocked': "Blocked",
+ 'interaction.doctor.summary.blocked': "An authoritative read established an invalid required prerequisite.",
+ 'interaction.doctor.state.unavailable': "Unavailable",
+ 'interaction.doctor.summary.unavailable': "Some prerequisites could not be verified; completed checks remain visible.",
+ 'interaction.doctor.state.warning': "Warning",
+ 'interaction.doctor.summary.warning': "A prerequisite needs attention or independent verification.",
+ 'interaction.doctor.state.not-applicable': "Not applicable",
+ 'interaction.doctor.summary.not-applicable': "The inspected prerequisites do not apply to this target.",
'interaction.status.heading': 'Copilot status',
'interaction.status.repository': 'Repository',
'interaction.status.target': 'Target',
@@ -45449,6 +45701,7 @@ const SPANISH_MESSAGES = Object.freeze({
'interaction.help.command.recheck': 'repite la revisión y reconcilia los hallazgos.',
'interaction.help.command.description': 'actualiza la descripción del pull request.',
'interaction.help.command.status': 'muestra el estado actual de la automatización.',
+ 'interaction.help.command.doctor': 'comprueba los requisitos disponibles del runtime sin modificar el repositorio.',
'interaction.help.command.fixOne': 'corrige un hallazgo.',
'interaction.help.command.fixAll': 'corrige todos los hallazgos sin resolver.',
'interaction.help.command.dismiss': 'descarta un hallazgo.',
@@ -45492,6 +45745,41 @@ const SPANISH_MESSAGES = Object.freeze({
many: 'Se superaron {count} comprobaciones de verificación.',
other: 'Se superaron {count} comprobaciones de verificación.',
}),
+ 'interaction.doctor.source.repository': "Variable del repositorio",
+ 'interaction.doctor.source.organization': "Variable de la organización",
+ 'interaction.doctor.source.default-off': "Ausencia verificada; off por defecto",
+ 'interaction.doctor.mode': "Modo",
+ 'interaction.doctor.heading': "Diagnóstico del runtime",
+ 'interaction.doctor.action': "Siguiente acción",
+ 'interaction.doctor.checks': "Comprobaciones",
+ 'interaction.doctor.readonly': "Este comando solo lee evidencias. La configuración de aprobación declarada no acredita la salud de los productores ni el acceso de escritura.",
+ 'interaction.doctor.source': "Revisión inspeccionada del repositorio",
+ 'interaction.doctor.group.effective-configuration': "Configuración efectiva",
+ 'interaction.doctor.group.issue-readiness': "Requisitos de la issue y el SDD",
+ 'interaction.doctor.group.linked-branch-readiness': "Rama enlazada nativa",
+ 'interaction.doctor.group.workflow-contracts': "Estructura de los workflows instalados",
+ 'interaction.doctor.group.approval-policy': "Lectura confiable de la política de aprobación",
+ 'interaction.doctor.group.approval-prerequisites': "Requisitos de aprobación declarados",
+ 'interaction.doctor.group.pull-request-identity': "Identidad actual exacta del PR",
+ 'interaction.doctor.action.configuration': "Revisa la configuración efectiva del workflow y corrige el ajuste inválido.",
+ 'interaction.doctor.action.workflow': "Revisa la revisión enlazada de los workflows y corrige la estructura requerida.",
+ 'interaction.doctor.action.retry': "Revisa esta ejecución del workflow y vuelve a ejecutar /copilot doctor.",
+ 'interaction.doctor.action.issue': "Revisa el formulario de la issue y sus etiquetas de clasificación.",
+ 'interaction.doctor.action.specifications': "Revisa el catálogo, la plantilla y el estándar de especificaciones en la revisión enlazada.",
+ 'interaction.doctor.action.branch': "Revisa la rama enlazada nativa de la issue y reintenta cuando esté preparada.",
+ 'interaction.doctor.action.policy': "Revisa la fuente confiable de la política de aprobación en Actions Variables.",
+ 'interaction.doctor.action.declared': "Verifica los requisitos de los productores declarados siguiendo la documentación de aprobación.",
+ 'interaction.doctor.action.none': "No se requiere ninguna acción para los requisitos inspeccionados.",
+ 'interaction.doctor.state.pass': "Correcto",
+ 'interaction.doctor.summary.pass': "Los requisitos aplicables inspeccionados son correctos.",
+ 'interaction.doctor.state.blocked': "Bloqueado",
+ 'interaction.doctor.summary.blocked': "Una lectura autoritativa confirmó un requisito obligatorio inválido.",
+ 'interaction.doctor.state.unavailable': "No disponible",
+ 'interaction.doctor.summary.unavailable': "Algunos requisitos no se pudieron verificar; las comprobaciones completadas siguen visibles.",
+ 'interaction.doctor.state.warning': "Advertencia",
+ 'interaction.doctor.summary.warning': "Un requisito necesita atención o verificación independiente.",
+ 'interaction.doctor.state.not-applicable': "No aplicable",
+ 'interaction.doctor.summary.not-applicable': "Los requisitos inspeccionados no se aplican a este destino.",
'interaction.status.heading': 'Estado de Copilot',
'interaction.status.repository': 'Repositorio',
'interaction.status.target': 'Destino',
@@ -46420,6 +46708,7 @@ exports.hasOwnedPrimaryIssuePublication = hasOwnedPrimaryIssuePublication;
exports.selectSemanticReplyIntents = selectSemanticReplyIntents;
exports.renderSemanticReply = renderSemanticReply;
exports.renderSemanticStatus = renderSemanticStatus;
+const github_runtime_doctor_presentation_policy_1 = __nccwpck_require__(2808);
const github_publication_1 = __nccwpck_require__(35793);
const github_user_policy_1 = __nccwpck_require__(84403);
const result_1 = __nccwpck_require__(73817);
@@ -46517,6 +46806,11 @@ function selectSemanticReplyIntents(context) {
if (kind === 'access-policy') {
return [replyIntent(context, correlationId, 'access-policy', Object.freeze({ kind }))];
}
+ if (result.id.endsWith('.Doctor') && (0, github_runtime_doctor_presentation_policy_1.isGithubRuntimeDoctorProjection)(payload.doctor)) {
+ return [replyIntent(context, correlationId, 'copilot-doctor', Object.freeze({
+ kind: 'doctor-command', snapshot: payload.doctor,
+ }))];
+ }
const snapshot = (0, result_1.getResultPayload)(payload.status);
if (result.id.endsWith('.Status') && isStatusSnapshot(snapshot)) {
const projection = Object.freeze({
@@ -46597,6 +46891,8 @@ function renderReplyBody(intent, catalog) {
if (intent.projection.kind === 'branch-sync-result') {
return renderBranchSyncResult(intent.projection, catalog);
}
+ if (intent.projection.kind === 'doctor-command')
+ return (0, github_runtime_doctor_presentation_policy_1.formatGithubRuntimeDoctor)(intent.projection.snapshot, intent.locale, catalog);
return (0, status_command_policy_1.formatCopilotStatus)(intent.projection.snapshot, intent.locale, catalog);
}
function renderBranchSyncResult(projection, catalog) {
@@ -48345,6 +48641,21 @@ function calculateJitteredWorkflowDelay(baseDelayMilliseconds, randomValue, poli
}
+/***/ }),
+
+/***/ 64921:
+/***/ ((__unused_webpack_module, exports) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.DOCTOR_REPOSITORY_PATHS = void 0;
+exports.DOCTOR_REPOSITORY_PATHS = [
+ '.github/workflows/copilot_issue.yml', '.github/workflows/copilot_issue_comment.yml',
+ '.github/workflows/copilot_pull_request.yml', 'specs/catalog.json', 'specs/_template.md', 'specs/README.md',
+];
+
+
/***/ }),
/***/ 6152:
@@ -52082,6 +52393,7 @@ async function runDoUserRequestAction(param, options, intentPayload) {
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.runExplicitCommentCommand = runExplicitCommentCommand;
exports.invalidCommentCommandResult = invalidCommentCommandResult;
+const read_copilot_doctor_workflow_1 = __nccwpck_require__(12416);
const result_1 = __nccwpck_require__(73817);
const read_copilot_status_workflow_1 = __nccwpck_require__(57206);
const copilot_interaction_policy_1 = __nccwpck_require__(90108);
@@ -52097,6 +52409,8 @@ const LEARNED_BUGBOT_RULE_PATH = '.copilot/BUGBOT.learned.md';
async function runExplicitCommentCommand(param, options, command, actorAuthorizationPort) {
if (command.name === 'help')
return runHelpCommand(param, options);
+ if (command.name === 'doctor')
+ return [await (0, read_copilot_doctor_workflow_1.readCopilotDoctor)(param, options.taskId, options.doctorReadPorts)];
if (command.name === 'status')
return [await (0, read_copilot_status_workflow_1.readCopilotStatus)(param, options.taskId, options.statusReadPorts)];
if (command.name === 'dismiss')
@@ -52323,6 +52637,22 @@ const copilot_command_1 = __nccwpck_require__(11771);
function projectCommentAutomationContext(source, language, userComment) {
const review = (0, bugbot_review_operation_context_1.projectBugbotReviewOperationContext)(source);
return Object.freeze({
+ ...(source.issueWorkflowProfile && typeof source.preBranchSdd === 'boolean'
+ && typeof source.issue.issueManagedBranches === 'boolean' ? { doctorConfiguration: Object.freeze({
+ profile: Object.freeze({ schemaVersion: source.issueWorkflowProfile.schemaVersion,
+ enabled: Object.freeze([...source.issueWorkflowProfile.enabled]) }),
+ labels: Object.freeze({
+ feature: Object.freeze([source.labels?.feature, source.labels?.enhancement].filter((value) => typeof value === 'string' && value.length > 0)),
+ bugfix: Object.freeze([source.labels?.bugfix, source.labels?.bug].filter((value) => typeof value === 'string' && value.length > 0)),
+ documentation: Object.freeze([source.labels?.documentation, source.labels?.docs].filter((value) => typeof value === 'string' && value.length > 0)),
+ chore: Object.freeze([source.labels?.chore, source.labels?.maintenance].filter((value) => typeof value === 'string' && value.length > 0)),
+ help: Object.freeze([source.labels?.help, source.labels?.question].filter((value) => typeof value === 'string' && value.length > 0)),
+ release: Object.freeze([source.labels?.release].filter((value) => typeof value === 'string' && value.length > 0)),
+ hotfix: Object.freeze([source.labels?.hotfix].filter((value) => typeof value === 'string' && value.length > 0)),
+ }),
+ preBranchSdd: source.preBranchSdd, issueManagedBranches: source.issue.issueManagedBranches,
+ dependencyBranch: source.currentConfiguration.parentBranch ?? source.branches.development ?? '',
+ }) } : {}),
actor: source.actor,
trustedBotLogin: source.tokenUser?.trim() ?? '',
userComment,
@@ -52509,7 +52839,7 @@ async function runCommentAutomation(initialParam, options, actorAuthorizationPor
return [(0, comment_automation_command_workflow_1.invalidCommentCommandResult)(options.taskId, command.reason)];
}
const isPublicMetadataCommand = command.kind === 'command'
- && (command.command.name === 'help' || command.command.name === 'status');
+ && (command.command.name === 'help' || command.command.name === 'status' || command.command.name === 'doctor');
if (!isPublicMetadataCommand && param.membersOnly
&& !await actorAuthorizationPort.isActorAllowedToUseMemberOnlyAutomation(param.actor)) {
(0, logging_ports_1.logInfo)('Skipping agent automation because ai-members-only is enabled and the actor is not authorized.');
@@ -53180,8 +53510,9 @@ const check_issue_comment_language_use_case_1 = __nccwpck_require__(93152);
const comment_automation_context_1 = __nccwpck_require__(37055);
const pull_request_workflow_context_1 = __nccwpck_require__(73447);
const pre_branch_sdd_1 = __nccwpck_require__(34730);
+const copilot_command_1 = __nccwpck_require__(11771);
class IssueCommentUseCase {
- constructor(languageUseCase, intentUseCase, thinkUseCase, autofixUseCase, doUserRequestUseCase, actorAuthorizationPort, bugbotGitMutationPort, dismissBugbotFindingsUseCase, reviewPotentialProblemsUseCase, updatePullRequestDescriptionUseCase, rememberBugbotRuleUseCase, syncBranchUseCase, preBranchSddContinuation, rememberBranchPorts, statusReadPorts) {
+ constructor(languageUseCase, intentUseCase, thinkUseCase, autofixUseCase, doUserRequestUseCase, actorAuthorizationPort, bugbotGitMutationPort, dismissBugbotFindingsUseCase, reviewPotentialProblemsUseCase, updatePullRequestDescriptionUseCase, rememberBugbotRuleUseCase, syncBranchUseCase, preBranchSddContinuation, rememberBranchPorts, statusReadPorts, doctorReadPorts) {
this.languageUseCase = languageUseCase;
this.intentUseCase = intentUseCase;
this.thinkUseCase = thinkUseCase;
@@ -53197,16 +53528,19 @@ class IssueCommentUseCase {
this.preBranchSddContinuation = preBranchSddContinuation;
this.rememberBranchPorts = rememberBranchPorts;
this.statusReadPorts = statusReadPorts;
+ this.doctorReadPorts = doctorReadPorts;
this.taskId = "IssueCommentUseCase";
}
async invoke(param) {
- if (param.preBranchSdd && !param.issue.issueManagedBranches) {
+ const command = (0, copilot_command_1.parseCopilotCommand)(param.issue.commentBody);
+ const isDoctor = command.kind === 'command' && command.command.name === 'doctor';
+ if (!isDoctor && param.preBranchSdd && !param.issue.issueManagedBranches) {
return [new result_1.Result({
id: this.taskId, success: false, executed: true,
steps: ['pre-branch-sdd requires issue-managed-branches; correct the Action configuration.'],
})];
}
- if (this.preBranchSddContinuation
+ if (!isDoctor && this.preBranchSddContinuation
&& param.issueStartDecision.sddRequired
&& (0, pre_branch_sdd_1.isSddAnswerComment)(param.issue.commentBody)
&& param.issue.commentAuthor.toLowerCase() !== param.tokenUser?.toLowerCase()) {
@@ -53234,6 +53568,7 @@ class IssueCommentUseCase {
rememberBugbotRuleUseCase: this.rememberBugbotRuleUseCase,
rememberBranchPorts: this.rememberBranchPorts,
statusReadPorts: this.statusReadPorts,
+ doctorReadPorts: this.doctorReadPorts,
syncBranchUseCase: this.syncBranchUseCase
? { invoke: (options) => this.syncBranchUseCase.invoke({ context: context.branchSync, options }) }
: undefined,
@@ -54020,7 +54355,7 @@ const check_pull_request_comment_language_use_case_1 = __nccwpck_require__(21729
const comment_automation_context_1 = __nccwpck_require__(37055);
const pull_request_workflow_context_1 = __nccwpck_require__(73447);
class PullRequestReviewCommentUseCase {
- constructor(languageUseCase, intentUseCase, thinkUseCase, autofixUseCase, doUserRequestUseCase, actorAuthorizationPort, bugbotGitMutationPort, dismissBugbotFindingsUseCase, reviewPotentialProblemsUseCase, updatePullRequestDescriptionUseCase, rememberBugbotRuleUseCase, syncBranchUseCase, rememberBranchPorts, statusReadPorts) {
+ constructor(languageUseCase, intentUseCase, thinkUseCase, autofixUseCase, doUserRequestUseCase, actorAuthorizationPort, bugbotGitMutationPort, dismissBugbotFindingsUseCase, reviewPotentialProblemsUseCase, updatePullRequestDescriptionUseCase, rememberBugbotRuleUseCase, syncBranchUseCase, rememberBranchPorts, statusReadPorts, doctorReadPorts) {
this.languageUseCase = languageUseCase;
this.intentUseCase = intentUseCase;
this.thinkUseCase = thinkUseCase;
@@ -54035,6 +54370,7 @@ class PullRequestReviewCommentUseCase {
this.syncBranchUseCase = syncBranchUseCase;
this.rememberBranchPorts = rememberBranchPorts;
this.statusReadPorts = statusReadPorts;
+ this.doctorReadPorts = doctorReadPorts;
this.taskId = "PullRequestReviewCommentUseCase";
}
async invoke(param) {
@@ -54060,6 +54396,7 @@ class PullRequestReviewCommentUseCase {
rememberBugbotRuleUseCase: this.rememberBugbotRuleUseCase,
rememberBranchPorts: this.rememberBranchPorts,
statusReadPorts: this.statusReadPorts,
+ doctorReadPorts: this.doctorReadPorts,
syncBranchUseCase: this.syncBranchUseCase
? { invoke: (options) => this.syncBranchUseCase.invoke({ context: context.branchSync, options }) }
: undefined,
@@ -54537,6 +54874,120 @@ function deepFreezeCopy(value) {
}
+/***/ }),
+
+/***/ 12416:
+/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.readCopilotDoctor = readCopilotDoctor;
+const result_1 = __nccwpck_require__(73817);
+const github_runtime_doctor_1 = __nccwpck_require__(20413);
+const issue_workflow_profile_1 = __nccwpck_require__(26744);
+const issue_start_policy_1 = __nccwpck_require__(90332);
+const positive_integer_policy_1 = __nccwpck_require__(19879);
+const context_1 = __nccwpck_require__(14712);
+const github_runtime_doctor_policy_1 = __nccwpck_require__(99566);
+const github_runtime_doctor_presentation_policy_1 = __nccwpck_require__(2808);
+const bugbot_context_request_1 = __nccwpck_require__(98299);
+async function readCopilotDoctor(param, taskId, ports) {
+ const repositoryId = (0, positive_integer_policy_1.parsePositiveSafeInteger)(param.bugbot.review.repository.id);
+ const isPull = param.status.target === 'pull-request';
+ const number = (0, positive_integer_policy_1.parsePositiveSafeInteger)(isPull ? param.status.pullRequestNumber : param.status.issueNumber);
+ if (!repositoryId || !number)
+ return doctorResult(taskId, undefined);
+ const target = isPull ? { kind: 'pull-request', repositoryId, pullNumber: number }
+ : { kind: 'issue', repositoryId, issueNumber: number };
+ const checks = [];
+ const put = (group, state, action) => checks.push(Object.freeze({ group, state, evidence: { source: group }, ...(action && state !== 'pass' && state !== 'not-applicable' ? { action: state === 'unavailable' ? 'retry' : action } : {}) }));
+ const configuration = param.doctorConfiguration;
+ const valid = (0, github_runtime_doctor_policy_1.validDoctorConfiguration)(configuration);
+ put('effective-configuration', configuration === undefined ? 'unavailable' : valid ? 'pass' : 'blocked', !valid ? 'configuration' : undefined);
+ const repositoryRead = ports ? await ports.readRepository(repositoryId).catch(() => ({ state: 'unavailable' }))
+ : { state: 'unavailable' };
+ const verifiedRead = repositoryRead.state === 'available'
+ && (repositoryRead.repositoryId !== repositoryId || !/^[a-f0-9]{40}$/u.test(repositoryRead.revision))
+ ? { state: 'unavailable' } : repositoryRead;
+ put('workflow-contracts', (0, github_runtime_doctor_policy_1.doctorWorkflowContracts)(verifiedRead), 'workflow');
+ if (isPull) {
+ // Optional issue context is not guessed from a PR number or event prose.
+ put('issue-readiness', 'not-applicable');
+ put('linked-branch-readiness', 'not-applicable');
+ }
+ else if (!valid || !ports) {
+ put('issue-readiness', 'unavailable', 'retry');
+ put('linked-branch-readiness', 'unavailable', 'retry');
+ }
+ else {
+ try {
+ const issue = await ports.readIssue(number);
+ if (issue.number !== number)
+ throw new Error('Wrong issue identity.');
+ const admission = (0, issue_workflow_profile_1.classifyIssueWorkflow)(issue.labels, configuration.profile, configuration.labels, issue.body);
+ if (admission.status !== 'eligible') {
+ put('issue-readiness', admission.status === 'unmanaged' ? 'unavailable' : 'blocked', 'issue');
+ put('linked-branch-readiness', 'not-applicable');
+ }
+ else {
+ const decision = (0, issue_start_policy_1.decideIssueStart)({ kind: admission.kind, labels: issue.labels,
+ issueManagedBranches: configuration.issueManagedBranches, preBranchSdd: configuration.preBranchSdd });
+ put('issue-readiness', decision.sddRequired ? (0, github_runtime_doctor_policy_1.doctorRepositoryPrerequisites)(verifiedRead) : 'not-applicable', 'specifications');
+ if (!decision.branchRequired)
+ put('linked-branch-readiness', 'not-applicable');
+ else {
+ const branch = await ports.readLinkedBranch(number).catch(() => ({ state: 'unavailable' }));
+ const state = branch.state === 'available' && branch.repositoryId === repositoryId && /^[a-f0-9]{40}$/u.test(branch.headSha)
+ ? 'pass' : branch.state === 'absent' ? 'warning' : 'unavailable';
+ put('linked-branch-readiness', state, 'branch');
+ }
+ }
+ }
+ catch {
+ put('issue-readiness', 'unavailable', 'retry');
+ put('linked-branch-readiness', 'unavailable', 'retry');
+ }
+ }
+ const policyRead = ports ? await ports.readApprovalPolicy().catch(() => ({ status: 'unavailable' }))
+ : { status: 'unavailable' };
+ const policy = (0, github_runtime_doctor_1.parseTrustedDoctorApprovalPolicy)(policyRead);
+ put('approval-policy', policy.status === 'valid' ? 'pass' : policy.status === 'invalid' ? 'blocked' : 'unavailable', 'policy');
+ if (policy.status === 'valid' || policyRead.status === 'available')
+ checks[checks.length - 1] = Object.freeze({ ...checks[checks.length - 1],
+ evidence: { source: policyRead.status === 'available' ? policyRead.source : policy.status === 'valid' ? policy.source : 'unavailable' } });
+ put('approval-prerequisites', policy.status !== 'valid' ? 'unavailable' : policy.policy.mode === 'off' ? 'not-applicable' : 'warning', 'declared');
+ if (policy.status === 'valid')
+ checks[checks.length - 1] = Object.freeze({ ...checks[checks.length - 1], evidence: { source: policy.policy.mode } });
+ if (!isPull)
+ put('pull-request-identity', 'not-applicable');
+ else {
+ let state = 'unavailable';
+ try {
+ if (ports) {
+ const request = (0, bugbot_context_request_1.projectBugbotContextRequest)(param.bugbot.review);
+ const selection = request.target.pullRequestSelection;
+ if (selection.kind === 'event' && selection.number === number) {
+ const canonical = (0, context_1.selectCanonicalBugbotPullRequest)(request.target, [await ports.pullRequests.getPullRequest(number)], 'event');
+ if (canonical.kind === 'canonical' && /^[a-f0-9]{40}$/u.test(canonical.pullRequest.headSha)
+ && await ports.pullRequests.getPullRequestHeadSha(number) === canonical.pullRequest.headSha)
+ state = 'pass';
+ }
+ }
+ }
+ catch { /* Independent facts remain available. */ }
+ put('pull-request-identity', state, 'retry');
+ }
+ const report = (0, github_runtime_doctor_1.aggregateGithubRuntimeDoctorReport)({ target, checks });
+ return doctorResult(taskId, report, ports?.serverOrigin, verifiedRead.state === 'available' ? verifiedRead.revision : undefined, param.status.owner, param.status.repository);
+}
+function doctorResult(taskId, report, serverOrigin, revision, owner, repository) {
+ const projection = Object.freeze({ report, serverOrigin, revision, owner, repository });
+ return new result_1.Result({ id: `${taskId}.Doctor`, success: true, executed: true, stepFormat: 'markdown',
+ steps: [(0, github_runtime_doctor_presentation_policy_1.formatGithubRuntimeDoctor)(projection)], payload: { doctor: projection } });
+}
+
+
/***/ }),
/***/ 57206:
@@ -69721,6 +70172,156 @@ function requireObject(data, operation) {
}
+/***/ }),
+
+/***/ 25282:
+/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
+
+"use strict";
+
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+ o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || (function () {
+ var ownKeys = function(o) {
+ ownKeys = Object.getOwnPropertyNames || function (o) {
+ var ar = [];
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
+ return ar;
+ };
+ return ownKeys(o);
+ };
+ return function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
+ __setModuleDefault(result, mod);
+ return result;
+ };
+})();
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.RuntimeDoctorRepository = void 0;
+const github = __importStar(__nccwpck_require__(78227));
+const github_runtime_doctor_ports_1 = __nccwpck_require__(64921);
+const pull_request_approval_repository_1 = __nccwpck_require__(23001);
+const SHA = /^[a-f0-9]{40}$/u;
+const SAFE_REF = /^(?!-|.*(?:\.\.|[\s~^:?*[\]\\]))[A-Za-z0-9._/-]{1,200}$/u;
+/** SDK DTOs and diagnostics terminate here; no mutation, provisioning, or filesystem API. */
+class RuntimeDoctorRepository {
+ constructor(owner, repository, token) {
+ this.owner = owner;
+ this.repository = repository;
+ this.client = github.getOctokit(token);
+ }
+ async readRepository(repositoryId) {
+ try {
+ const repo = await this.client.rest.repos.get({ owner: this.owner, repo: this.repository });
+ if (repo.data.id !== repositoryId || !SAFE_REF.test(repo.data.default_branch))
+ return { state: 'unavailable' };
+ const ref = await this.client.rest.git.getRef({ owner: this.owner, repo: this.repository, ref: `heads/${repo.data.default_branch}` });
+ const revision = ref.data.object.sha;
+ if (!SHA.test(revision) || ref.data.object.type !== 'commit')
+ return { state: 'unavailable' };
+ const values = await Promise.all(github_runtime_doctor_ports_1.DOCTOR_REPOSITORY_PATHS.map(async (path) => [path, await this.readFile(path, revision)]));
+ const final = await this.client.rest.git.getRef({ owner: this.owner, repo: this.repository, ref: `heads/${repo.data.default_branch}` });
+ if (final.data.object.type !== 'commit' || final.data.object.sha !== revision)
+ return { state: 'unavailable' };
+ return Object.freeze({ state: 'available', repositoryId, revision,
+ files: Object.freeze(Object.fromEntries(values)) });
+ }
+ catch {
+ return { state: 'unavailable' };
+ }
+ }
+ async readFile(path, revision) {
+ try {
+ const response = await this.client.rest.repos.getContent({ owner: this.owner, repo: this.repository, path, ref: revision });
+ const data = response.data;
+ if (Array.isArray(data) || data.type !== 'file' || data.path !== path || !('content' in data)
+ || data.encoding !== 'base64' || typeof data.content !== 'string' || data.content.length > 300000
+ || !/^[A-Za-z0-9+/=\r\n]*$/u.test(data.content))
+ throw new Error('Malformed bounded file response.');
+ const bytes = Buffer.from(data.content, 'base64');
+ const text = bytes.toString('utf8');
+ if (bytes.toString('base64') !== data.content.replace(/[\r\n]/gu, '')
+ || bytes.length > 200000 || !Buffer.from(text, 'utf8').equals(bytes))
+ throw new Error('Malformed bounded file content.');
+ return Object.freeze({ state: 'present', text });
+ }
+ catch (error) {
+ if (status(error) === 404)
+ return Object.freeze({ state: 'missing' });
+ throw error;
+ }
+ }
+ async readIssue(issueNumber) {
+ const response = await this.client.rest.issues.get({ owner: this.owner, repo: this.repository, issue_number: issueNumber });
+ const issue = response.data;
+ if (issue.number !== issueNumber || issue.pull_request || typeof issue.body !== 'string' || issue.body.length > 70000
+ || typeof issue.title !== 'string' || issue.title.length > 256 || !Array.isArray(issue.labels) || issue.labels.length > 100)
+ throw new Error('Malformed bounded issue response.');
+ const labels = issue.labels.map((label) => typeof label === 'string' ? label : label.name);
+ if (labels.some((label) => typeof label !== 'string' || label.length > 120))
+ throw new Error('Malformed issue labels.');
+ return Object.freeze({ number: issue.number, title: issue.title, body: issue.body, labels: Object.freeze(labels) });
+ }
+ async readLinkedBranch(issueNumber) {
+ try {
+ const result = await this.client.graphql(`query RuntimeDoctorLinkedBranch($owner:String!,$repo:String!,$number:Int!){repository(owner:$owner,name:$repo){
+ databaseId nameWithOwner issue(number:$number){number linkedBranches(first:100){
+ pageInfo{hasNextPage} nodes{ref{name target{oid} repository{databaseId nameWithOwner}}}}}}}`, { owner: this.owner, repo: this.repository, number: issueNumber });
+ const repo = result.repository;
+ const links = repo?.issue?.linkedBranches;
+ const expected = `${this.owner}/${this.repository}`.toLowerCase();
+ if (!repo || !Number.isSafeInteger(repo.databaseId) || (repo.databaseId ?? 0) <= 0 || repo.nameWithOwner?.toLowerCase() !== expected
+ || repo.issue?.number !== issueNumber || links?.pageInfo?.hasNextPage !== false || !Array.isArray(links.nodes))
+ return { state: 'unavailable' };
+ const refs = links.nodes.flatMap((node) => node?.ref ? [node.ref] : []);
+ if (refs.length === 0)
+ return { state: 'absent' };
+ if (refs.length !== 1)
+ return { state: 'unavailable' };
+ const branch = refs[0];
+ if (!branch.name || !SAFE_REF.test(branch.name) || !branch.target?.oid || !SHA.test(branch.target.oid)
+ || branch.repository?.databaseId !== repo.databaseId || branch.repository?.nameWithOwner?.toLowerCase() !== expected)
+ return { state: 'unavailable' };
+ const live = await this.client.rest.git.getRef({ owner: this.owner, repo: this.repository, ref: `heads/${branch.name}` });
+ return live.data.object.type === 'commit' && live.data.object.sha === branch.target.oid
+ ? Object.freeze({ state: 'available', repositoryId: repo.databaseId, name: branch.name, headSha: branch.target.oid }) : { state: 'unavailable' };
+ }
+ catch {
+ return { state: 'unavailable' };
+ }
+ }
+ async readApprovalPolicy() {
+ try {
+ const value = await (0, pull_request_approval_repository_1.readEffectiveApprovalPolicy)(this.client, { owner: this.owner, repository: this.repository });
+ return value ? Object.freeze({ status: 'available', ...value }) : Object.freeze({ status: 'absent' });
+ }
+ catch {
+ return Object.freeze({ status: 'unavailable' });
+ }
+ }
+}
+exports.RuntimeDoctorRepository = RuntimeDoctorRepository;
+function status(error) {
+ return error && typeof error === 'object' && 'status' in error && typeof error.status === 'number' ? error.status : undefined;
+}
+
+
/***/ }),
/***/ 52644:
@@ -71747,6 +72348,7 @@ var __importStar = (this && this.__importStar) || (function () {
})();
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.PullRequestApprovalRepository = void 0;
+exports.readEffectiveApprovalPolicy = readEffectiveApprovalPolicy;
const github = __importStar(__nccwpck_require__(78227));
const pull_request_approval_policy_1 = __nccwpck_require__(98820);
const approval_coverage_artifact_1 = __nccwpck_require__(96710);
@@ -71760,30 +72362,22 @@ class PullRequestApprovalRepository {
this.octokit = github.getOctokit(token);
this.checkOctokit = settings.githubToken ? github.getOctokit(settings.githubToken) : undefined;
}
- async readPolicy(target) {
+ /** Read-only source evidence; failure never consults an injected policy value. */
+ async readPolicyEvidence(target) {
try {
- const response = await this.octokit.rest.actions.getRepoVariable({
- owner: target.owner, repo: target.repository, name: 'PR_APPROVAL_POLICY',
- });
- return response.data.value;
- }
- catch (error) {
- if (statusOf(error) !== 404)
- throw error;
+ const read = await this.readEffectivePolicy(target);
+ return Object.freeze(read ? { status: 'available', ...read } : { status: 'absent' });
}
- // A repository Variable takes precedence over an accessible organization Variable.
- try {
- const response = await this.octokit.rest.actions.getOrgVariable({
- org: target.owner, name: 'PR_APPROVAL_POLICY',
- });
- return response.data.value;
- }
- catch (error) {
- if (statusOf(error) === 404)
- return undefined;
- throw error;
+ catch {
+ return Object.freeze({ status: 'unavailable' });
}
}
+ async readPolicy(target) {
+ return (await this.readEffectivePolicy(target))?.value;
+ }
+ async readEffectivePolicy(target) {
+ return readEffectiveApprovalPolicy(this.octokit, target);
+ }
async loadEvidence(target) {
const { data: pull } = await this.octokit.rest.pulls.get({
owner: target.owner, repo: target.repository, pull_number: target.pullNumber,
@@ -72083,6 +72677,31 @@ function record(value) {
return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
}
function array(value) { return Array.isArray(value) ? value : []; }
+/** Canonical repository-over-organization read shared with the physically read-only doctor binding. */
+async function readEffectiveApprovalPolicy(octokit, target) {
+ const admit = (source, value) => {
+ if (typeof value !== 'string' || value.length > 65536)
+ throw new Error('Approval policy response is malformed or unbounded.');
+ return { source, value };
+ };
+ try {
+ const response = await octokit.rest.actions.getRepoVariable({ owner: target.owner, repo: target.repository, name: 'PR_APPROVAL_POLICY' });
+ return admit('repository', response.data.value);
+ }
+ catch (error) {
+ if (statusOf(error) !== 404)
+ throw error;
+ }
+ try {
+ const response = await octokit.rest.actions.getOrgVariable({ org: target.owner, name: 'PR_APPROVAL_POLICY' });
+ return admit('organization', response.data.value);
+ }
+ catch (error) {
+ if (statusOf(error) === 404)
+ return undefined;
+ throw error;
+ }
+}
/***/ }),
@@ -74603,6 +75222,7 @@ exports.COPILOT_COMMAND_NAMES = [
'estimate',
'test-plan',
'status',
+ 'doctor',
'description',
'explain',
'diagnose',
@@ -74659,6 +75279,9 @@ function parseCopilotCommand(raw) {
if (argumentsList.length > MAX_ARGUMENTS) {
return { kind: 'invalid', reason: `Copilot commands accept at most ${MAX_ARGUMENTS} arguments.` };
}
+ if (name === 'doctor' && argumentsList.length > 0) {
+ return { kind: 'invalid', reason: '/copilot doctor does not accept arguments.' };
+ }
if ((name === 'fix' || name === 'dismiss' || name === 'implement' || name === 'remember') && argumentsList.length === 0) {
return { kind: 'invalid', reason: `/${name} requires at least one argument.` };
}
@@ -75389,6 +76012,71 @@ function publicationTargetToken(target) {
}
+/***/ }),
+
+/***/ 20413:
+/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.GITHUB_RUNTIME_DOCTOR_GROUPS = void 0;
+exports.parseTrustedDoctorApprovalPolicy = parseTrustedDoctorApprovalPolicy;
+exports.aggregateGithubRuntimeDoctorReport = aggregateGithubRuntimeDoctorReport;
+const pull_request_approval_policy_1 = __nccwpck_require__(98820);
+// Domain contracts and trusted policy parsing for the issue/PR runtime doctor.
+/** The fixed, ordered evidence groups in the GitHub runtime doctor contract. */
+exports.GITHUB_RUNTIME_DOCTOR_GROUPS = [
+ 'effective-configuration',
+ 'issue-readiness',
+ 'linked-branch-readiness',
+ 'workflow-contracts',
+ 'approval-policy',
+ 'approval-prerequisites',
+ 'pull-request-identity',
+];
+/**
+ * Parses only a successful trusted effective read. Verified absence preserves the
+ * established off default; read failure is unavailable and never consults an injected value.
+ */
+function parseTrustedDoctorApprovalPolicy(read) {
+ if (read.status === 'unavailable')
+ return { status: 'unavailable' };
+ if (read.status === 'absent') {
+ return { status: 'valid', source: 'default-off', policy: pull_request_approval_policy_1.DISABLED_PULL_REQUEST_APPROVAL_POLICY };
+ }
+ try {
+ return { status: 'valid', source: read.source, policy: (0, pull_request_approval_policy_1.parsePullRequestApprovalPolicy)(read.value) };
+ }
+ catch {
+ // Do not leak parser/provider text into a report or user-facing surface.
+ return { status: 'invalid' };
+ }
+}
+/** Aggregate explicit independent evidence. Missing groups are unavailable, never assumed healthy. */
+function aggregateGithubRuntimeDoctorReport(input) {
+ const byGroup = new Map();
+ for (const check of input.checks) {
+ if (byGroup.has(check.group))
+ throw new Error(`Duplicate GitHub runtime doctor group: ${check.group}.`);
+ byGroup.set(check.group, Object.freeze({ ...check, ...(check.evidence ? { evidence: Object.freeze({ ...check.evidence }) } : {}) }));
+ }
+ const checks = exports.GITHUB_RUNTIME_DOCTOR_GROUPS.map(group => byGroup.get(group)
+ ?? Object.freeze({ group, state: 'unavailable' }));
+ const priority = ['blocked', 'unavailable', 'warning', 'pass'];
+ const applicable = checks.filter(check => check.state !== 'not-applicable');
+ const severity = applicable.length === 0 ? 'not-applicable'
+ : priority.find(state => applicable.some(check => check.state === state)) ?? 'unavailable';
+ const primaryAction = applicable.find(check => check.state === severity && check.action)?.action;
+ return Object.freeze({
+ target: Object.freeze({ ...input.target }),
+ checks: Object.freeze(checks),
+ severity,
+ ...(primaryAction ? { primaryAction } : {}),
+ });
+}
+
+
/***/ }),
/***/ 84403:
@@ -79200,6 +79888,29 @@ const createRepositoryVersionTagsClient = () => new octokit_release_adapters_1.O
exports.createRepositoryVersionTagsClient = createRepositoryVersionTagsClient;
+/***/ }),
+
+/***/ 36459:
+/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.createGithubRuntimeDoctorPorts = createGithubRuntimeDoctorPorts;
+const runtime_doctor_repository_1 = __nccwpck_require__(25282);
+function createGithubRuntimeDoctorPorts(binding, pullRequests) {
+ const repository = new runtime_doctor_repository_1.RuntimeDoctorRepository(binding.owner, binding.repository, binding.token);
+ return Object.freeze({ serverOrigin: process.env.GITHUB_SERVER_URL ?? 'https://github.com',
+ readRepository: (id) => repository.readRepository(id),
+ readIssue: (number) => repository.readIssue(number),
+ readLinkedBranch: (number) => repository.readLinkedBranch(number),
+ readApprovalPolicy: () => repository.readApprovalPolicy(),
+ pullRequests: Object.freeze({ getPullRequest: (number) => pullRequests.getPullRequest(number),
+ getPullRequestHeadSha: (number) => pullRequests.getPullRequestHeadSha(number) }),
+ });
+}
+
+
/***/ }),
/***/ 29839:
@@ -79610,6 +80321,7 @@ exports.createIssueCommentUseCaseCompositionRoot = createIssueCommentUseCaseComp
exports.createPullRequestReviewCommentUseCaseCompositionRoot = createPullRequestReviewCommentUseCaseCompositionRoot;
exports.createCommitUseCaseCompositionRoot = createCommitUseCaseCompositionRoot;
exports.createMainRunRouteCompositionRoot = createMainRunRouteCompositionRoot;
+const github_runtime_doctor_composition_root_1 = __nccwpck_require__(36459);
const linked_branch_readiness_repository_1 = __nccwpck_require__(79421);
const pull_request_changes_repository_1 = __nccwpck_require__(71564);
const commit_use_case_1 = __nccwpck_require__(28001);
@@ -79727,7 +80439,7 @@ function createIssueCommentUseCaseCompositionRoot(binding) {
contextPorts: bugbot.scm.context,
resolutionPorts: bugbot.scm.resolution,
catalogResolver: new resolve_message_catalog_use_case_1.ResolveMessageCatalogUseCase(language),
- }), new detect_potential_problems_use_case_1.DetectPotentialProblemsUseCase(findings, bugbot.scm, bugbot.telemetry, new resolve_message_catalog_use_case_1.ResolveMessageCatalogUseCase(language)), pullRequestDescription, new remember_bugbot_rule_use_case_1.RememberBugbotRuleUseCase(bugbot.rules), branchSync, (0, issue_use_case_composition_root_1.createIssueUseCaseCompositionRoot)(binding), rememberBranchPorts(binding, bugbot.scm.context), bugbot.scm.context);
+ }), new detect_potential_problems_use_case_1.DetectPotentialProblemsUseCase(findings, bugbot.scm, bugbot.telemetry, new resolve_message_catalog_use_case_1.ResolveMessageCatalogUseCase(language)), pullRequestDescription, new remember_bugbot_rule_use_case_1.RememberBugbotRuleUseCase(bugbot.rules), branchSync, (0, issue_use_case_composition_root_1.createIssueUseCaseCompositionRoot)(binding), rememberBranchPorts(binding, bugbot.scm.context), bugbot.scm.context, (0, github_runtime_doctor_composition_root_1.createGithubRuntimeDoctorPorts)(binding, bugbot.scm.context));
}
function createPullRequestReviewCommentUseCaseCompositionRoot(binding) {
const bugbot = (0, bugbot_composition_root_1.createBugbotCompositionRoot)(binding);
@@ -79743,7 +80455,7 @@ function createPullRequestReviewCommentUseCaseCompositionRoot(binding) {
contextPorts: bugbot.scm.context,
resolutionPorts: bugbot.scm.resolution,
catalogResolver: new resolve_message_catalog_use_case_1.ResolveMessageCatalogUseCase(language),
- }), new detect_potential_problems_use_case_1.DetectPotentialProblemsUseCase(findings, bugbot.scm, bugbot.telemetry, new resolve_message_catalog_use_case_1.ResolveMessageCatalogUseCase(language)), pullRequestDescription, new remember_bugbot_rule_use_case_1.RememberBugbotRuleUseCase(bugbot.rules), branchSync, rememberBranchPorts(binding, bugbot.scm.context), bugbot.scm.context);
+ }), new detect_potential_problems_use_case_1.DetectPotentialProblemsUseCase(findings, bugbot.scm, bugbot.telemetry, new resolve_message_catalog_use_case_1.ResolveMessageCatalogUseCase(language)), pullRequestDescription, new remember_bugbot_rule_use_case_1.RememberBugbotRuleUseCase(bugbot.rules), branchSync, rememberBranchPorts(binding, bugbot.scm.context), bugbot.scm.context, (0, github_runtime_doctor_composition_root_1.createGithubRuntimeDoctorPorts)(binding, bugbot.scm.context));
}
function createCommitUseCaseCompositionRoot(projectBoardCommandPort, binding) {
return new commit_use_case_1.CommitUseCase(new notify_new_commit_on_issue_use_case_1.NotifyNewCommitOnIssueUseCase((0, push_single_action_capability_port_binding_1.bindIssueReopen)((0, issue_interaction_composition_root_1.createIssueNotificationRepository)(), binding)), new check_changes_issue_size_use_case_1.CheckChangesIssueSizeUseCase((0, lifecycle_capability_port_binding_2.bindProjectBoardCommands)(projectBoardCommandPort, binding), (0, lifecycle_capability_port_binding_2.bindIssueLabels)((0, issue_labels_composition_root_1.createIssueLabelRepository)(), binding), (0, push_single_action_capability_port_binding_1.bindPullRequestBranchQuery)(new pull_request_lifecycle_repository_1.PullRequestLifecycleRepository((0, github_pull_request_client_factory_1.createPullRequestLifecycleClient)()), binding), (0, push_single_action_capability_port_binding_1.bindBranchChangeSize)(new branch_compare_repository_1.BranchCompareRepository((0, github_branch_client_factory_1.createBranchComparisonClient)()), binding)), createDetectPotentialProblemsUseCase(binding), (0, check_progress_composition_root_1.createCheckProgressCompositionRoot)(binding), (0, actor_authorization_composition_root_1.createActorAuthorizationRepository)());
diff --git a/docs/issues/comment-commands.mdx b/docs/issues/comment-commands.mdx
index bff2d579f..0a08f37a1 100644
--- a/docs/issues/comment-commands.mdx
+++ b/docs/issues/comment-commands.mdx
@@ -120,11 +120,33 @@ separate from an issue with the same number. Status and description queries
preserve existing lifecycle and waiting labels when they make no lifecycle
decision.
-`/copilot doctor` is registered as a deterministic read-only command. Its full
-seven-group runtime evidence collection and correlated report are still being
-implemented; until those adapters are installed, the command returns a safe
-unavailable response and does not invoke an agent. It does not inspect secret
-values, test write access, or infer grants from workflow contents.
+`/copilot doctor` reads seven bounded groups: effective workflow and dependency
+settings, eligible issue/SDD prerequisites, the issue's native linked branch,
+installed workflow structure, the trusted approval policy, declared approval
+prerequisites, and the exact current PR identity. It has no arguments and
+never invokes a model or repairs the repository. Participants can request this
+metadata report even when model automation is restricted to organization members.
+
+The reply distinguishes **pass**, **warning**, **blocked**, **unavailable**, and
+**not applicable**. A successfully read invalid policy is blocked; a denied or
+failed read is unavailable. Completed independent checks remain visible. Only
+verified absence of both repository and organization policy values selects the
+existing off default. Active approval settings are declared prerequisites,
+which need independent producer verification and do not certify write access.
+
+Workflow files and specification prerequisites are read at one verified immutable
+default-branch revision, linked in the report. A moved default head makes that
+sample unavailable. Native linked branches require complete same-repository
+association and a matching live ref. A PR request checks only its exact event
+PR and fences its current head; optional issue context is not inferred from
+an equal conversation number. Reports use the repository locale in English or
+Spanish with atomic fallback and one correlated reply per source comment.
+
+Inspect the displayed primary action and the workflow run, correct any proven
+invalid configuration through the usual maintainer flow, then retry
+`/copilot doctor`. The command does not read secret values, test write access,
+provision setup, change labels or approvals, or infer grants from workflow text.
+
Read-only commands answer the current comment, using the issue or PR as
background. Add the file, symbol, failure or requested work after the command to
diff --git a/specs/CATALOG.md b/specs/CATALOG.md
index 2f2f3ab12..7066a2551 100644
--- a/specs/CATALOG.md
+++ b/specs/CATALOG.md
@@ -16,7 +16,7 @@ debt or convert unknown historic intent into a design decision.
| `bugbot-review-state-reconciliation` | Implemented | Reconcile review snapshots, findings, threads, comments, and check conclusions | [Bugbot review-state reconciliation](./bugbot-review-state-reconciliation.md) | 59 paths · 2026-10-11 |
| `execution-lifecycle` | Implemented | Shared GitHub Action lifecycle from event admission through durable user-facing results | [Execution admission, queueing, routing, and result publication](./execution-admission-queue-and-publication.md) + 3 companion | 84 paths · 2026-09-16 |
| `architecture-quality-hardening` | Implemented | Close verified concurrency, error-contract, context-coupling, fan-out, setup/doctor, and provider-policy risks in dependency order | [Architecture quality and scalability hardening](./architecture-quality-and-scalability-hardening.md) + 1 companion | 72 paths · 2026-09-16 |
-| `setup-and-doctor` | Implemented | Plan, validate, provision, and audit a repository installation without exposing credentials | [Setup, configuration, credentials, and doctor](./setup-configuration-credentials-and-doctor.md) + 3 companion | 174 paths · 2026-10-11 |
+| `setup-and-doctor` | Implemented | Plan, validate, provision, and audit a repository installation without exposing credentials | [Setup, configuration, credentials, and doctor](./setup-configuration-credentials-and-doctor.md) + 3 companion | 190 paths · 2026-10-11 |
| `local-web-setup-assistant` | Proposed | Offer a packaged, loopback-only Svelte setup interface over the existing engine with four-language guidance, English CLI help, basic/custom first-run paths, editable question and plan review, source-labelled GitHub facts, assisted CI and Project evidence, role-separated PATs, and truthful itemized outcomes; inspect existing configuration and apply independent, scope-aware runtime adjustments with permission, drift and read-back verification | [Local web setup assistant](./local-web-setup-assistant.md) + 1 companion | 202 paths · 2026-10-09 |
| `guided-bot-pat-onboarding` | Proposed | Guide creation of the persistent workflow PAT, verify bot identity and grants, recover explicitly from wrong-account input within the session, and install the approved Actions Secret | [Guided bot PAT onboarding](./guided-bot-pat-onboarding.md) | 39 paths · 2026-10-07 |
| `temporary-setup-operator-authorization` | Proposed | Guide creation of the one-run operator PAT through GitHub's official form, verify final setup access, and report user-owned deletion accurately | [Assisted setup PAT creation](./temporary-setup-operator-authorization.md) | 34 paths · 2026-10-07 |
@@ -105,10 +105,10 @@ debt or convert unknown historic intent into a design decision.
- Owner: Copilot maintainers
- Last verified: 2026-10-11
- Specifications: [`specs/setup-configuration-credentials-and-doctor.md`](./setup-configuration-credentials-and-doctor.md) · [`specs/setup-doctor-architecture-hardening.md`](./setup-doctor-architecture-hardening.md) · [`specs/setup-pat-permission-guidance-and-verification.md`](./setup-pat-permission-guidance-and-verification.md) · [`specs/github-runtime-doctor-command.md`](./github-runtime-doctor-command.md)
-- Workflows: [`setup/workflows/agent-cli-provisioning.yml`](../setup/workflows/agent-cli-provisioning.yml) · [`setup/workflows/copilot_credential_health.yml`](../setup/workflows/copilot_credential_health.yml)
-- Entrypoints: [`src/cli/commands/setup.ts`](../src/cli/commands/setup.ts) · [`src/cli/commands/doctor.ts`](../src/cli/commands/doctor.ts)
-- Core code: [`src/domain/setup.ts`](../src/domain/setup.ts) · [`src/domain/setup_questionnaire.ts`](../src/domain/setup_questionnaire.ts) · [`src/domain/setup_token_permissions.ts`](../src/domain/setup_token_permissions.ts) · [`src/application/ports/setup_terminal_ports.ts`](../src/application/ports/setup_terminal_ports.ts) · [`src/application/ports/setup_wizard_ports.ts`](../src/application/ports/setup_wizard_ports.ts) · [`src/application/ports/branch_tag_ports.ts`](../src/application/ports/branch_tag_ports.ts) · [`src/application/policies/setup_issue_workflow_policy.ts`](../src/application/policies/setup_issue_workflow_policy.ts) · [`src/data/repository/release/repository_version_tags_query_repository.ts`](../src/data/repository/release/repository_version_tags_query_repository.ts) · [`src/application/ports/setup_token_permission_ports.ts`](../src/application/ports/setup_token_permission_ports.ts) · [`src/application/policies/setup_token_permission_evidence_policy.ts`](../src/application/policies/setup_token_permission_evidence_policy.ts) · [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/policies/setup_questionnaire_policy.ts`](../src/application/policies/setup_questionnaire_policy.ts) · [`src/application/policies/setup_configuration_plan.ts`](../src/application/policies/setup_configuration_plan.ts) · [`src/application/policies/merge_setup_overrides_policy.ts`](../src/application/policies/merge_setup_overrides_policy.ts) · [`src/application/policies/setup_configuration_storage_policy.ts`](../src/application/policies/setup_configuration_storage_policy.ts) · [`src/application/policies/setup_doctor_message_catalog.ts`](../src/application/policies/setup_doctor_message_catalog.ts) · [`src/application/policies/setup_doctor_report_policy.ts`](../src/application/policies/setup_doctor_report_policy.ts) · [`src/application/policies/setup_journey_policy.ts`](../src/application/policies/setup_journey_policy.ts) · [`src/application/policies/setup_permission_summary_policy.ts`](../src/application/policies/setup_permission_summary_policy.ts) · [`src/application/usecases/setup/setup_wizard_use_case.ts`](../src/application/usecases/setup/setup_wizard_use_case.ts) · [`src/application/usecases/setup/setup_journey_use_case.ts`](../src/application/usecases/setup/setup_journey_use_case.ts) · [`src/application/usecases/setup/setup_session_coordinator.ts`](../src/application/usecases/setup/setup_session_coordinator.ts) · [`src/application/usecases/setup/setup_questionnaire_controller.ts`](../src/application/usecases/setup/setup_questionnaire_controller.ts) · [`src/application/usecases/setup/setup_credentials_use_case.ts`](../src/application/usecases/setup/setup_credentials_use_case.ts) · [`src/application/usecases/setup/setup_token_permissions_use_case.ts`](../src/application/usecases/setup/setup_token_permissions_use_case.ts) · [`src/application/usecases/setup/doctor_use_case.ts`](../src/application/usecases/setup/doctor_use_case.ts) · [`src/application/usecases/setup/merge_queue_readiness_use_case.ts`](../src/application/usecases/setup/merge_queue_readiness_use_case.ts) · [`src/application/usecases/actions/initial_setup_workflow.ts`](../src/application/usecases/actions/initial_setup_workflow.ts) · [`src/application/usecases/actions/setup_resource_provisioning.ts`](../src/application/usecases/actions/setup_resource_provisioning.ts) · [`src/application/ports/message_catalog_ports.ts`](../src/application/ports/message_catalog_ports.ts) · [`src/application/usecases/localization/resolve_message_catalog_use_case.ts`](../src/application/usecases/localization/resolve_message_catalog_use_case.ts) · [`src/application/policies/setup_configuration_validation.ts`](../src/application/policies/setup_configuration_validation.ts) · [`src/infrastructure/setup_workspace_adapter.ts`](../src/infrastructure/setup_workspace_adapter.ts) · [`src/data/repository/repository_variables_repository.ts`](../src/data/repository/repository_variables_repository.ts) · [`src/infrastructure/github/ports/github_repository_variables_protocol.ts`](../src/infrastructure/github/ports/github_repository_variables_protocol.ts) · [`src/infrastructure/setup_remote_credential_health_adapter.ts`](../src/infrastructure/setup_remote_credential_health_adapter.ts) · [`src/infrastructure/setup_credential_validation_adapter.ts`](../src/infrastructure/setup_credential_validation_adapter.ts) · [`src/infrastructure/setup_token_permission_query_adapter.ts`](../src/infrastructure/setup_token_permission_query_adapter.ts) · [`src/infrastructure/setup_permission_actions_probe.ts`](../src/infrastructure/setup_permission_actions_probe.ts) · [`src/infrastructure/setup_permission_issue_probe.ts`](../src/infrastructure/setup_permission_issue_probe.ts) · [`src/infrastructure/setup_permission_issue_cleanup.ts`](../src/infrastructure/setup_permission_issue_cleanup.ts) · [`src/infrastructure/setup_permission_probe_journal.ts`](../src/infrastructure/setup_permission_probe_journal.ts) · [`src/infrastructure/setup_permission_probe_transaction.ts`](../src/infrastructure/setup_permission_probe_transaction.ts) · [`src/infrastructure/setup_permission_resource_probes.ts`](../src/infrastructure/setup_permission_resource_probes.ts) · [`src/infrastructure/setup_projects_probe_page_policy.ts`](../src/infrastructure/setup_projects_probe_page_policy.ts) · [`src/cli/setup_terminal_driver.ts`](../src/cli/setup_terminal_driver.ts) · [`src/cli/setup_question_renderer.ts`](../src/cli/setup_question_renderer.ts) · [`src/cli/setup_journey_presenter.ts`](../src/cli/setup_journey_presenter.ts) · [`src/cli/setup_plan_presenter.ts`](../src/cli/setup_plan_presenter.ts) · [`src/cli/setup_doctor_presenter.ts`](../src/cli/setup_doctor_presenter.ts) · [`src/cli/setup_prompt_rendering.ts`](../src/cli/setup_prompt_rendering.ts) · [`src/cli/setup_credential_prompt_adapter.ts`](../src/cli/setup_credential_prompt_adapter.ts) · [`src/cli/setup_credential_collection.ts`](../src/cli/setup_credential_collection.ts) · [`src/cli/commands/setup_execution.ts`](../src/cli/commands/setup_execution.ts) · [`src/cli/setup_token_permission_presenter.ts`](../src/cli/setup_token_permission_presenter.ts) · [`src/infrastructure/composition/setup_credentials_composition_root.ts`](../src/infrastructure/composition/setup_credentials_composition_root.ts) · [`src/infrastructure/composition/setup_token_permissions_composition_root.ts`](../src/infrastructure/composition/setup_token_permissions_composition_root.ts) · [`src/infrastructure/composition/setup_doctor_composition_root.ts`](../src/infrastructure/composition/setup_doctor_composition_root.ts) · [`scripts/coverage-budgets.json`](../scripts/coverage-budgets.json) · [`scripts/documentation_pat_exception_policy.cjs`](../scripts/documentation_pat_exception_policy.cjs) · [`scripts/validate-documentation-contract.cjs`](../scripts/validate-documentation-contract.cjs) · [`src/infrastructure/http_deadline.ts`](../src/infrastructure/http_deadline.ts) · [`src/infrastructure/setup_permission_denial.ts`](../src/infrastructure/setup_permission_denial.ts) · [`src/infrastructure/setup_permission_read_probe.ts`](../src/infrastructure/setup_permission_read_probe.ts) · [`src/infrastructure/setup_permission_read_evidence.ts`](../src/infrastructure/setup_permission_read_evidence.ts) · [`src/infrastructure/setup_permission_read_shape.ts`](../src/infrastructure/setup_permission_read_shape.ts) · [`src/infrastructure/setup_permission_projects_read.ts`](../src/infrastructure/setup_permission_projects_read.ts) · [`src/infrastructure/setup_permission_projects_access.ts`](../src/infrastructure/setup_permission_projects_access.ts) · [`src/infrastructure/setup_permission_workflow_cleanup.ts`](../src/infrastructure/setup_permission_workflow_cleanup.ts) · [`src/infrastructure/setup_permission_project_cleanup.ts`](../src/infrastructure/setup_permission_project_cleanup.ts) · [`src/infrastructure/setup_permission_probe_http.ts`](../src/infrastructure/setup_permission_probe_http.ts) · [`src/infrastructure/setup_permission_pull_request_probe.ts`](../src/infrastructure/setup_permission_pull_request_probe.ts) · [`src/infrastructure/git_authentication_environment.ts`](../src/infrastructure/git_authentication_environment.ts) · [`src/actions/github_action_input.ts`](../src/actions/github_action_input.ts) · [`src/infrastructure/setup_github_identity_query_adapter.ts`](../src/infrastructure/setup_github_identity_query_adapter.ts) · [`src/infrastructure/github_secret_encryption.ts`](../src/infrastructure/github_secret_encryption.ts) · [`src/infrastructure/setup_permission_organization_owner.ts`](../src/infrastructure/setup_permission_organization_owner.ts) · [`src/data/repository/github/github_actions_resource_inspector.ts`](../src/data/repository/github/github_actions_resource_inspector.ts) · [`src/data/repository/github/github_actions_resource_commands.ts`](../src/data/repository/github/github_actions_resource_commands.ts) · [`src/data/repository/github/github_actions_resource_collection.ts`](../src/data/repository/github/github_actions_resource_collection.ts) · [`src/application/usecases/setup/resolve_setup_workflow_pat_conflict_use_case.ts`](../src/application/usecases/setup/resolve_setup_workflow_pat_conflict_use_case.ts) · [`src/cli/setup_workflow_pat_storage_copy.ts`](../src/cli/setup_workflow_pat_storage_copy.ts) · [`src/cli/commands/setup_policy.ts`](../src/cli/commands/setup_policy.ts) · [`src/application/usecases/execution/execution_issue_number_policy.ts`](../src/application/usecases/execution/execution_issue_number_policy.ts) · [`src/application/usecases/execution/setup_execution_workflow.ts`](../src/application/usecases/execution/setup_execution_workflow.ts) · [`src/domain/github_runtime_doctor.ts`](../src/domain/github_runtime_doctor.ts) · [`src/domain/copilot_command.ts`](../src/domain/copilot_command.ts) · [`src/application/usecases/comment_automation_command_workflow.ts`](../src/application/usecases/comment_automation_command_workflow.ts) · [`src/application/policies/copilot_interaction_policy.ts`](../src/application/policies/copilot_interaction_policy.ts) · [`src/application/policies/publication_message_catalog.ts`](../src/application/policies/publication_message_catalog.ts)
-- Tests: [`src/data/repository/__tests__/repository_version_tags_query_repository.test.ts`](../src/data/repository/__tests__/repository_version_tags_query_repository.test.ts) · [`src/cli/__tests__/setup_command_options.test.ts`](../src/cli/__tests__/setup_command_options.test.ts) · [`src/data/repository/__tests__/setup_variable_write_failures.test.ts`](../src/data/repository/__tests__/setup_variable_write_failures.test.ts) · [`src/infrastructure/composition/__tests__/initial_setup_composition_root.test.ts`](../src/infrastructure/composition/__tests__/initial_setup_composition_root.test.ts) · [`src/application/policies/__tests__/setup_questionnaire_policy.test.ts`](../src/application/policies/__tests__/setup_questionnaire_policy.test.ts) · [`src/application/policies/__tests__/setup_configuration_policy.test.ts`](../src/application/policies/__tests__/setup_configuration_policy.test.ts) · [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/application/policies/__tests__/setup_remote_facts_policy.test.ts`](../src/application/policies/__tests__/setup_remote_facts_policy.test.ts) · [`src/application/policies/__tests__/setup_doctor_message_catalog.test.ts`](../src/application/policies/__tests__/setup_doctor_message_catalog.test.ts) · [`src/application/policies/__tests__/setup_doctor_report_policy.test.ts`](../src/application/policies/__tests__/setup_doctor_report_policy.test.ts) · [`src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts`](../src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts) · [`src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_session_coordinator.test.ts`](../src/application/usecases/setup/__tests__/setup_session_coordinator.test.ts) · [`src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts) · [`src/application/usecases/setup/__tests__/doctor_use_case.test.ts`](../src/application/usecases/setup/__tests__/doctor_use_case.test.ts) · [`src/application/usecases/setup/__tests__/merge_queue_readiness_use_case.test.ts`](../src/application/usecases/setup/__tests__/merge_queue_readiness_use_case.test.ts) · [`src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts`](../src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts) · [`src/application/usecases/actions/__tests__/setup_resource_provisioning.test.ts`](../src/application/usecases/actions/__tests__/setup_resource_provisioning.test.ts) · [`src/infrastructure/__tests__/setup_workspace_adapter.test.ts`](../src/infrastructure/__tests__/setup_workspace_adapter.test.ts) · [`src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts`](../src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts) · [`src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts) · [`src/infrastructure/__tests__/setup_permission_resource_probes.test.ts`](../src/infrastructure/__tests__/setup_permission_resource_probes.test.ts) · [`src/infrastructure/__tests__/setup_projects_probe_page_policy.test.ts`](../src/infrastructure/__tests__/setup_projects_probe_page_policy.test.ts) · [`src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts`](../src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts) · [`src/data/repository/__tests__/repository_variables_repository.test.ts`](../src/data/repository/__tests__/repository_variables_repository.test.ts) · [`src/cli/__tests__/setup_presenters.test.ts`](../src/cli/__tests__/setup_presenters.test.ts) · [`src/cli/__tests__/setup_journey_presenter.test.ts`](../src/cli/__tests__/setup_journey_presenter.test.ts) · [`src/cli/__tests__/setup_prompt_rendering.test.ts`](../src/cli/__tests__/setup_prompt_rendering.test.ts) · [`src/cli/__tests__/setup_token_permission_presenter.test.ts`](../src/cli/__tests__/setup_token_permission_presenter.test.ts) · [`src/__tests__/cli.test.ts`](../src/__tests__/cli.test.ts) · [`src/cli/__tests__/setup_terminal_driver.test.ts`](../src/cli/__tests__/setup_terminal_driver.test.ts) · [`src/architecture/__tests__/setup_doctor_boundaries.test.ts`](../src/architecture/__tests__/setup_doctor_boundaries.test.ts) · [`src/tooling/__tests__/documentation_pat_exception_policy.test.ts`](../src/tooling/__tests__/documentation_pat_exception_policy.test.ts) · [`src/infrastructure/__tests__/setup_pat_audit.test.ts`](../src/infrastructure/__tests__/setup_pat_audit.test.ts) · [`src/infrastructure/__tests__/setup_permission_projects_access.test.ts`](../src/infrastructure/__tests__/setup_permission_projects_access.test.ts) · [`src/infrastructure/__tests__/setup_credential_validation_adapter.test.ts`](../src/infrastructure/__tests__/setup_credential_validation_adapter.test.ts) · [`src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts) · [`src/infrastructure/__tests__/git_commit_adapter.test.ts`](../src/infrastructure/__tests__/git_commit_adapter.test.ts) · [`src/actions/__tests__/github_action_input.test.ts`](../src/actions/__tests__/github_action_input.test.ts) · [`src/infrastructure/__tests__/setup_permission_cleanup_contract.test.ts`](../src/infrastructure/__tests__/setup_permission_cleanup_contract.test.ts) · [`src/infrastructure/__tests__/setup_permission_actions_cleanup_timing.test.ts`](../src/infrastructure/__tests__/setup_permission_actions_cleanup_timing.test.ts) · [`src/infrastructure/__tests__/setup_permission_operator_actions.test.ts`](../src/infrastructure/__tests__/setup_permission_operator_actions.test.ts) · [`src/infrastructure/__tests__/setup_permission_journal_contract.test.ts`](../src/infrastructure/__tests__/setup_permission_journal_contract.test.ts) · [`src/infrastructure/__tests__/setup_permission_audit_transactions.test.ts`](../src/infrastructure/__tests__/setup_permission_audit_transactions.test.ts) · [`src/infrastructure/__tests__/setup_permission_write_failures.test.ts`](../src/infrastructure/__tests__/setup_permission_write_failures.test.ts) · [`src/infrastructure/__tests__/setup_permission_resource_failures.test.ts`](../src/infrastructure/__tests__/setup_permission_resource_failures.test.ts) · [`src/infrastructure/__tests__/setup_permission_boundary_failures.test.ts`](../src/infrastructure/__tests__/setup_permission_boundary_failures.test.ts) · [`src/infrastructure/__tests__/github_secret_encryption.test.ts`](../src/infrastructure/__tests__/github_secret_encryption.test.ts) · [`src/infrastructure/__tests__/setup_permission_organization_owner.test.ts`](../src/infrastructure/__tests__/setup_permission_organization_owner.test.ts) · [`src/infrastructure/__tests__/setup_pat_bootstrap_transactions.test.ts`](../src/infrastructure/__tests__/setup_pat_bootstrap_transactions.test.ts) · [`src/data/repository/__tests__/repository_actions_sdk_contract.integration.test.ts`](../src/data/repository/__tests__/repository_actions_sdk_contract.integration.test.ts) · [`src/data/repository/__tests__/repository_actions_resource_edge_cases.test.ts`](../src/data/repository/__tests__/repository_actions_resource_edge_cases.test.ts) · [`src/data/repository/__tests__/repository_selected_resource_grants.test.ts`](../src/data/repository/__tests__/repository_selected_resource_grants.test.ts) · [`src/infrastructure/__tests__/setup_permission_read_contract.test.ts`](../src/infrastructure/__tests__/setup_permission_read_contract.test.ts) · [`src/application/usecases/setup/__tests__/setup_workflow_pat_conflict.test.ts`](../src/application/usecases/setup/__tests__/setup_workflow_pat_conflict.test.ts) · [`src/application/usecases/setup/__tests__/setup_workflow_pat_credential_rotation.test.ts`](../src/application/usecases/setup/__tests__/setup_workflow_pat_credential_rotation.test.ts) · [`src/data/repository/__tests__/setup_organization_pat_inventory.test.ts`](../src/data/repository/__tests__/setup_organization_pat_inventory.test.ts) · [`src/cli/__tests__/web_setup_bot_pat_scope_conflict.test.ts`](../src/cli/__tests__/web_setup_bot_pat_scope_conflict.test.ts) · [`src/actions/__tests__/local_setup_apply_contract.test.ts`](../src/actions/__tests__/local_setup_apply_contract.test.ts) · [`src/cli/commands/__tests__/setup_policy.test.ts`](../src/cli/commands/__tests__/setup_policy.test.ts) · [`src/application/usecases/execution/__tests__/execution_issue_number_policy.test.ts`](../src/application/usecases/execution/__tests__/execution_issue_number_policy.test.ts) · [`src/application/usecases/execution/__tests__/setup_execution_workflow.test.ts`](../src/application/usecases/execution/__tests__/setup_execution_workflow.test.ts) · [`src/domain/__tests__/github_runtime_doctor.test.ts`](../src/domain/__tests__/github_runtime_doctor.test.ts) · [`src/domain/__tests__/copilot_command.test.ts`](../src/domain/__tests__/copilot_command.test.ts) · [`src/application/usecases/__tests__/comment_automation_use_case.test.ts`](../src/application/usecases/__tests__/comment_automation_use_case.test.ts)
+- Workflows: [`setup/workflows/agent-cli-provisioning.yml`](../setup/workflows/agent-cli-provisioning.yml) · [`setup/workflows/copilot_credential_health.yml`](../setup/workflows/copilot_credential_health.yml) · [`.github/workflows/copilot_issue_comment.yml`](../.github/workflows/copilot_issue_comment.yml) · [`.github/workflows/copilot_pull_request_comment.yml`](../.github/workflows/copilot_pull_request_comment.yml)
+- Entrypoints: [`src/cli/commands/setup.ts`](../src/cli/commands/setup.ts) · [`src/cli/commands/doctor.ts`](../src/cli/commands/doctor.ts) · [`src/application/usecases/issue_comment_use_case.ts`](../src/application/usecases/issue_comment_use_case.ts) · [`src/application/usecases/pull_request_review_comment_use_case.ts`](../src/application/usecases/pull_request_review_comment_use_case.ts)
+- Core code: [`src/domain/setup.ts`](../src/domain/setup.ts) · [`src/domain/setup_questionnaire.ts`](../src/domain/setup_questionnaire.ts) · [`src/domain/setup_token_permissions.ts`](../src/domain/setup_token_permissions.ts) · [`src/application/ports/setup_terminal_ports.ts`](../src/application/ports/setup_terminal_ports.ts) · [`src/application/ports/setup_wizard_ports.ts`](../src/application/ports/setup_wizard_ports.ts) · [`src/application/ports/branch_tag_ports.ts`](../src/application/ports/branch_tag_ports.ts) · [`src/application/policies/setup_issue_workflow_policy.ts`](../src/application/policies/setup_issue_workflow_policy.ts) · [`src/data/repository/release/repository_version_tags_query_repository.ts`](../src/data/repository/release/repository_version_tags_query_repository.ts) · [`src/application/ports/setup_token_permission_ports.ts`](../src/application/ports/setup_token_permission_ports.ts) · [`src/application/policies/setup_token_permission_evidence_policy.ts`](../src/application/policies/setup_token_permission_evidence_policy.ts) · [`src/application/policies/setup_token_permission_policy.ts`](../src/application/policies/setup_token_permission_policy.ts) · [`src/application/policies/setup_questionnaire_policy.ts`](../src/application/policies/setup_questionnaire_policy.ts) · [`src/application/policies/setup_configuration_plan.ts`](../src/application/policies/setup_configuration_plan.ts) · [`src/application/policies/merge_setup_overrides_policy.ts`](../src/application/policies/merge_setup_overrides_policy.ts) · [`src/application/policies/setup_configuration_storage_policy.ts`](../src/application/policies/setup_configuration_storage_policy.ts) · [`src/application/policies/setup_doctor_message_catalog.ts`](../src/application/policies/setup_doctor_message_catalog.ts) · [`src/application/policies/setup_doctor_report_policy.ts`](../src/application/policies/setup_doctor_report_policy.ts) · [`src/application/policies/setup_journey_policy.ts`](../src/application/policies/setup_journey_policy.ts) · [`src/application/policies/setup_permission_summary_policy.ts`](../src/application/policies/setup_permission_summary_policy.ts) · [`src/application/usecases/setup/setup_wizard_use_case.ts`](../src/application/usecases/setup/setup_wizard_use_case.ts) · [`src/application/usecases/setup/setup_journey_use_case.ts`](../src/application/usecases/setup/setup_journey_use_case.ts) · [`src/application/usecases/setup/setup_session_coordinator.ts`](../src/application/usecases/setup/setup_session_coordinator.ts) · [`src/application/usecases/setup/setup_questionnaire_controller.ts`](../src/application/usecases/setup/setup_questionnaire_controller.ts) · [`src/application/usecases/setup/setup_credentials_use_case.ts`](../src/application/usecases/setup/setup_credentials_use_case.ts) · [`src/application/usecases/setup/setup_token_permissions_use_case.ts`](../src/application/usecases/setup/setup_token_permissions_use_case.ts) · [`src/application/usecases/setup/doctor_use_case.ts`](../src/application/usecases/setup/doctor_use_case.ts) · [`src/application/usecases/setup/merge_queue_readiness_use_case.ts`](../src/application/usecases/setup/merge_queue_readiness_use_case.ts) · [`src/application/usecases/actions/initial_setup_workflow.ts`](../src/application/usecases/actions/initial_setup_workflow.ts) · [`src/application/usecases/actions/setup_resource_provisioning.ts`](../src/application/usecases/actions/setup_resource_provisioning.ts) · [`src/application/ports/message_catalog_ports.ts`](../src/application/ports/message_catalog_ports.ts) · [`src/application/usecases/localization/resolve_message_catalog_use_case.ts`](../src/application/usecases/localization/resolve_message_catalog_use_case.ts) · [`src/application/policies/setup_configuration_validation.ts`](../src/application/policies/setup_configuration_validation.ts) · [`src/infrastructure/setup_workspace_adapter.ts`](../src/infrastructure/setup_workspace_adapter.ts) · [`src/data/repository/repository_variables_repository.ts`](../src/data/repository/repository_variables_repository.ts) · [`src/infrastructure/github/ports/github_repository_variables_protocol.ts`](../src/infrastructure/github/ports/github_repository_variables_protocol.ts) · [`src/infrastructure/setup_remote_credential_health_adapter.ts`](../src/infrastructure/setup_remote_credential_health_adapter.ts) · [`src/infrastructure/setup_credential_validation_adapter.ts`](../src/infrastructure/setup_credential_validation_adapter.ts) · [`src/infrastructure/setup_token_permission_query_adapter.ts`](../src/infrastructure/setup_token_permission_query_adapter.ts) · [`src/infrastructure/setup_permission_actions_probe.ts`](../src/infrastructure/setup_permission_actions_probe.ts) · [`src/infrastructure/setup_permission_issue_probe.ts`](../src/infrastructure/setup_permission_issue_probe.ts) · [`src/infrastructure/setup_permission_issue_cleanup.ts`](../src/infrastructure/setup_permission_issue_cleanup.ts) · [`src/infrastructure/setup_permission_probe_journal.ts`](../src/infrastructure/setup_permission_probe_journal.ts) · [`src/infrastructure/setup_permission_probe_transaction.ts`](../src/infrastructure/setup_permission_probe_transaction.ts) · [`src/infrastructure/setup_permission_resource_probes.ts`](../src/infrastructure/setup_permission_resource_probes.ts) · [`src/infrastructure/setup_projects_probe_page_policy.ts`](../src/infrastructure/setup_projects_probe_page_policy.ts) · [`src/cli/setup_terminal_driver.ts`](../src/cli/setup_terminal_driver.ts) · [`src/cli/setup_question_renderer.ts`](../src/cli/setup_question_renderer.ts) · [`src/cli/setup_journey_presenter.ts`](../src/cli/setup_journey_presenter.ts) · [`src/cli/setup_plan_presenter.ts`](../src/cli/setup_plan_presenter.ts) · [`src/cli/setup_doctor_presenter.ts`](../src/cli/setup_doctor_presenter.ts) · [`src/cli/setup_prompt_rendering.ts`](../src/cli/setup_prompt_rendering.ts) · [`src/cli/setup_credential_prompt_adapter.ts`](../src/cli/setup_credential_prompt_adapter.ts) · [`src/cli/setup_credential_collection.ts`](../src/cli/setup_credential_collection.ts) · [`src/cli/commands/setup_execution.ts`](../src/cli/commands/setup_execution.ts) · [`src/cli/setup_token_permission_presenter.ts`](../src/cli/setup_token_permission_presenter.ts) · [`src/infrastructure/composition/setup_credentials_composition_root.ts`](../src/infrastructure/composition/setup_credentials_composition_root.ts) · [`src/infrastructure/composition/setup_token_permissions_composition_root.ts`](../src/infrastructure/composition/setup_token_permissions_composition_root.ts) · [`src/infrastructure/composition/setup_doctor_composition_root.ts`](../src/infrastructure/composition/setup_doctor_composition_root.ts) · [`scripts/coverage-budgets.json`](../scripts/coverage-budgets.json) · [`scripts/documentation_pat_exception_policy.cjs`](../scripts/documentation_pat_exception_policy.cjs) · [`scripts/validate-documentation-contract.cjs`](../scripts/validate-documentation-contract.cjs) · [`src/infrastructure/http_deadline.ts`](../src/infrastructure/http_deadline.ts) · [`src/infrastructure/setup_permission_denial.ts`](../src/infrastructure/setup_permission_denial.ts) · [`src/infrastructure/setup_permission_read_probe.ts`](../src/infrastructure/setup_permission_read_probe.ts) · [`src/infrastructure/setup_permission_read_evidence.ts`](../src/infrastructure/setup_permission_read_evidence.ts) · [`src/infrastructure/setup_permission_read_shape.ts`](../src/infrastructure/setup_permission_read_shape.ts) · [`src/infrastructure/setup_permission_projects_read.ts`](../src/infrastructure/setup_permission_projects_read.ts) · [`src/infrastructure/setup_permission_projects_access.ts`](../src/infrastructure/setup_permission_projects_access.ts) · [`src/infrastructure/setup_permission_workflow_cleanup.ts`](../src/infrastructure/setup_permission_workflow_cleanup.ts) · [`src/infrastructure/setup_permission_project_cleanup.ts`](../src/infrastructure/setup_permission_project_cleanup.ts) · [`src/infrastructure/setup_permission_probe_http.ts`](../src/infrastructure/setup_permission_probe_http.ts) · [`src/infrastructure/setup_permission_pull_request_probe.ts`](../src/infrastructure/setup_permission_pull_request_probe.ts) · [`src/infrastructure/git_authentication_environment.ts`](../src/infrastructure/git_authentication_environment.ts) · [`src/actions/github_action_input.ts`](../src/actions/github_action_input.ts) · [`src/infrastructure/setup_github_identity_query_adapter.ts`](../src/infrastructure/setup_github_identity_query_adapter.ts) · [`src/infrastructure/github_secret_encryption.ts`](../src/infrastructure/github_secret_encryption.ts) · [`src/infrastructure/setup_permission_organization_owner.ts`](../src/infrastructure/setup_permission_organization_owner.ts) · [`src/data/repository/github/github_actions_resource_inspector.ts`](../src/data/repository/github/github_actions_resource_inspector.ts) · [`src/data/repository/github/github_actions_resource_commands.ts`](../src/data/repository/github/github_actions_resource_commands.ts) · [`src/data/repository/github/github_actions_resource_collection.ts`](../src/data/repository/github/github_actions_resource_collection.ts) · [`src/application/usecases/setup/resolve_setup_workflow_pat_conflict_use_case.ts`](../src/application/usecases/setup/resolve_setup_workflow_pat_conflict_use_case.ts) · [`src/cli/setup_workflow_pat_storage_copy.ts`](../src/cli/setup_workflow_pat_storage_copy.ts) · [`src/cli/commands/setup_policy.ts`](../src/cli/commands/setup_policy.ts) · [`src/application/usecases/execution/execution_issue_number_policy.ts`](../src/application/usecases/execution/execution_issue_number_policy.ts) · [`src/application/usecases/execution/setup_execution_workflow.ts`](../src/application/usecases/execution/setup_execution_workflow.ts) · [`src/domain/github_runtime_doctor.ts`](../src/domain/github_runtime_doctor.ts) · [`src/domain/copilot_command.ts`](../src/domain/copilot_command.ts) · [`src/application/usecases/comment_automation_command_workflow.ts`](../src/application/usecases/comment_automation_command_workflow.ts) · [`src/application/policies/copilot_interaction_policy.ts`](../src/application/policies/copilot_interaction_policy.ts) · [`src/application/policies/publication_message_catalog.ts`](../src/application/policies/publication_message_catalog.ts) · [`src/application/ports/github_runtime_doctor_ports.ts`](../src/application/ports/github_runtime_doctor_ports.ts) · [`src/application/policies/github_runtime_doctor_policy.ts`](../src/application/policies/github_runtime_doctor_policy.ts) · [`src/application/policies/github_runtime_doctor_presentation_policy.ts`](../src/application/policies/github_runtime_doctor_presentation_policy.ts) · [`src/application/usecases/read_copilot_doctor_workflow.ts`](../src/application/usecases/read_copilot_doctor_workflow.ts) · [`src/data/repository/github/runtime_doctor_repository.ts`](../src/data/repository/github/runtime_doctor_repository.ts) · [`src/infrastructure/composition/github_runtime_doctor_composition_root.ts`](../src/infrastructure/composition/github_runtime_doctor_composition_root.ts)
+- Tests: [`src/data/repository/__tests__/repository_version_tags_query_repository.test.ts`](../src/data/repository/__tests__/repository_version_tags_query_repository.test.ts) · [`src/cli/__tests__/setup_command_options.test.ts`](../src/cli/__tests__/setup_command_options.test.ts) · [`src/data/repository/__tests__/setup_variable_write_failures.test.ts`](../src/data/repository/__tests__/setup_variable_write_failures.test.ts) · [`src/infrastructure/composition/__tests__/initial_setup_composition_root.test.ts`](../src/infrastructure/composition/__tests__/initial_setup_composition_root.test.ts) · [`src/application/policies/__tests__/setup_questionnaire_policy.test.ts`](../src/application/policies/__tests__/setup_questionnaire_policy.test.ts) · [`src/application/policies/__tests__/setup_configuration_policy.test.ts`](../src/application/policies/__tests__/setup_configuration_policy.test.ts) · [`src/application/policies/__tests__/setup_token_permission_policy.test.ts`](../src/application/policies/__tests__/setup_token_permission_policy.test.ts) · [`src/application/policies/__tests__/setup_remote_facts_policy.test.ts`](../src/application/policies/__tests__/setup_remote_facts_policy.test.ts) · [`src/application/policies/__tests__/setup_doctor_message_catalog.test.ts`](../src/application/policies/__tests__/setup_doctor_message_catalog.test.ts) · [`src/application/policies/__tests__/setup_doctor_report_policy.test.ts`](../src/application/policies/__tests__/setup_doctor_report_policy.test.ts) · [`src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts`](../src/application/usecases/setup/__tests__/setup_questionnaire_controller.test.ts) · [`src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_wizard_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_journey_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_session_coordinator.test.ts`](../src/application/usecases/setup/__tests__/setup_session_coordinator.test.ts) · [`src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_credentials_use_case.test.ts) · [`src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts`](../src/application/usecases/setup/__tests__/setup_token_permissions_use_case.test.ts) · [`src/application/usecases/setup/__tests__/doctor_use_case.test.ts`](../src/application/usecases/setup/__tests__/doctor_use_case.test.ts) · [`src/application/usecases/setup/__tests__/merge_queue_readiness_use_case.test.ts`](../src/application/usecases/setup/__tests__/merge_queue_readiness_use_case.test.ts) · [`src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts`](../src/application/usecases/actions/__tests__/initial_setup_use_case.test.ts) · [`src/application/usecases/actions/__tests__/setup_resource_provisioning.test.ts`](../src/application/usecases/actions/__tests__/setup_resource_provisioning.test.ts) · [`src/infrastructure/__tests__/setup_workspace_adapter.test.ts`](../src/infrastructure/__tests__/setup_workspace_adapter.test.ts) · [`src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts`](../src/infrastructure/__tests__/setup_remote_credential_health_adapter.test.ts) · [`src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_token_permission_query_adapter.test.ts) · [`src/infrastructure/__tests__/setup_permission_resource_probes.test.ts`](../src/infrastructure/__tests__/setup_permission_resource_probes.test.ts) · [`src/infrastructure/__tests__/setup_projects_probe_page_policy.test.ts`](../src/infrastructure/__tests__/setup_projects_probe_page_policy.test.ts) · [`src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts`](../src/infrastructure/composition/__tests__/setup_token_permissions_composition_root.test.ts) · [`src/data/repository/__tests__/repository_variables_repository.test.ts`](../src/data/repository/__tests__/repository_variables_repository.test.ts) · [`src/cli/__tests__/setup_presenters.test.ts`](../src/cli/__tests__/setup_presenters.test.ts) · [`src/cli/__tests__/setup_journey_presenter.test.ts`](../src/cli/__tests__/setup_journey_presenter.test.ts) · [`src/cli/__tests__/setup_prompt_rendering.test.ts`](../src/cli/__tests__/setup_prompt_rendering.test.ts) · [`src/cli/__tests__/setup_token_permission_presenter.test.ts`](../src/cli/__tests__/setup_token_permission_presenter.test.ts) · [`src/__tests__/cli.test.ts`](../src/__tests__/cli.test.ts) · [`src/cli/__tests__/setup_terminal_driver.test.ts`](../src/cli/__tests__/setup_terminal_driver.test.ts) · [`src/architecture/__tests__/setup_doctor_boundaries.test.ts`](../src/architecture/__tests__/setup_doctor_boundaries.test.ts) · [`src/tooling/__tests__/documentation_pat_exception_policy.test.ts`](../src/tooling/__tests__/documentation_pat_exception_policy.test.ts) · [`src/infrastructure/__tests__/setup_pat_audit.test.ts`](../src/infrastructure/__tests__/setup_pat_audit.test.ts) · [`src/infrastructure/__tests__/setup_permission_projects_access.test.ts`](../src/infrastructure/__tests__/setup_permission_projects_access.test.ts) · [`src/infrastructure/__tests__/setup_credential_validation_adapter.test.ts`](../src/infrastructure/__tests__/setup_credential_validation_adapter.test.ts) · [`src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts`](../src/infrastructure/__tests__/setup_github_identity_query_adapter.test.ts) · [`src/infrastructure/__tests__/git_commit_adapter.test.ts`](../src/infrastructure/__tests__/git_commit_adapter.test.ts) · [`src/actions/__tests__/github_action_input.test.ts`](../src/actions/__tests__/github_action_input.test.ts) · [`src/infrastructure/__tests__/setup_permission_cleanup_contract.test.ts`](../src/infrastructure/__tests__/setup_permission_cleanup_contract.test.ts) · [`src/infrastructure/__tests__/setup_permission_actions_cleanup_timing.test.ts`](../src/infrastructure/__tests__/setup_permission_actions_cleanup_timing.test.ts) · [`src/infrastructure/__tests__/setup_permission_operator_actions.test.ts`](../src/infrastructure/__tests__/setup_permission_operator_actions.test.ts) · [`src/infrastructure/__tests__/setup_permission_journal_contract.test.ts`](../src/infrastructure/__tests__/setup_permission_journal_contract.test.ts) · [`src/infrastructure/__tests__/setup_permission_audit_transactions.test.ts`](../src/infrastructure/__tests__/setup_permission_audit_transactions.test.ts) · [`src/infrastructure/__tests__/setup_permission_write_failures.test.ts`](../src/infrastructure/__tests__/setup_permission_write_failures.test.ts) · [`src/infrastructure/__tests__/setup_permission_resource_failures.test.ts`](../src/infrastructure/__tests__/setup_permission_resource_failures.test.ts) · [`src/infrastructure/__tests__/setup_permission_boundary_failures.test.ts`](../src/infrastructure/__tests__/setup_permission_boundary_failures.test.ts) · [`src/infrastructure/__tests__/github_secret_encryption.test.ts`](../src/infrastructure/__tests__/github_secret_encryption.test.ts) · [`src/infrastructure/__tests__/setup_permission_organization_owner.test.ts`](../src/infrastructure/__tests__/setup_permission_organization_owner.test.ts) · [`src/infrastructure/__tests__/setup_pat_bootstrap_transactions.test.ts`](../src/infrastructure/__tests__/setup_pat_bootstrap_transactions.test.ts) · [`src/data/repository/__tests__/repository_actions_sdk_contract.integration.test.ts`](../src/data/repository/__tests__/repository_actions_sdk_contract.integration.test.ts) · [`src/data/repository/__tests__/repository_actions_resource_edge_cases.test.ts`](../src/data/repository/__tests__/repository_actions_resource_edge_cases.test.ts) · [`src/data/repository/__tests__/repository_selected_resource_grants.test.ts`](../src/data/repository/__tests__/repository_selected_resource_grants.test.ts) · [`src/infrastructure/__tests__/setup_permission_read_contract.test.ts`](../src/infrastructure/__tests__/setup_permission_read_contract.test.ts) · [`src/application/usecases/setup/__tests__/setup_workflow_pat_conflict.test.ts`](../src/application/usecases/setup/__tests__/setup_workflow_pat_conflict.test.ts) · [`src/application/usecases/setup/__tests__/setup_workflow_pat_credential_rotation.test.ts`](../src/application/usecases/setup/__tests__/setup_workflow_pat_credential_rotation.test.ts) · [`src/data/repository/__tests__/setup_organization_pat_inventory.test.ts`](../src/data/repository/__tests__/setup_organization_pat_inventory.test.ts) · [`src/cli/__tests__/web_setup_bot_pat_scope_conflict.test.ts`](../src/cli/__tests__/web_setup_bot_pat_scope_conflict.test.ts) · [`src/actions/__tests__/local_setup_apply_contract.test.ts`](../src/actions/__tests__/local_setup_apply_contract.test.ts) · [`src/cli/commands/__tests__/setup_policy.test.ts`](../src/cli/commands/__tests__/setup_policy.test.ts) · [`src/application/usecases/execution/__tests__/execution_issue_number_policy.test.ts`](../src/application/usecases/execution/__tests__/execution_issue_number_policy.test.ts) · [`src/application/usecases/execution/__tests__/setup_execution_workflow.test.ts`](../src/application/usecases/execution/__tests__/setup_execution_workflow.test.ts) · [`src/domain/__tests__/github_runtime_doctor.test.ts`](../src/domain/__tests__/github_runtime_doctor.test.ts) · [`src/domain/__tests__/copilot_command.test.ts`](../src/domain/__tests__/copilot_command.test.ts) · [`src/application/usecases/__tests__/comment_automation_use_case.test.ts`](../src/application/usecases/__tests__/comment_automation_use_case.test.ts) · [`src/application/policies/__tests__/github_runtime_doctor_policy.test.ts`](../src/application/policies/__tests__/github_runtime_doctor_policy.test.ts) · [`src/application/policies/__tests__/github_runtime_doctor_presentation_policy.test.ts`](../src/application/policies/__tests__/github_runtime_doctor_presentation_policy.test.ts) · [`src/application/usecases/__tests__/read_copilot_doctor_workflow.test.ts`](../src/application/usecases/__tests__/read_copilot_doctor_workflow.test.ts) · [`src/application/usecases/__tests__/runtime_doctor_security_integration.test.ts`](../src/application/usecases/__tests__/runtime_doctor_security_integration.test.ts) · [`src/data/repository/github/__tests__/runtime_doctor_repository.test.ts`](../src/data/repository/github/__tests__/runtime_doctor_repository.test.ts) · [`src/infrastructure/composition/__tests__/github_runtime_doctor_composition_root.test.ts`](../src/infrastructure/composition/__tests__/github_runtime_doctor_composition_root.test.ts)
- User documentation: [`README.md`](../README.md) · [`docs/how-to-use.mdx`](../docs/how-to-use.mdx) · [`docs/configuration.mdx`](../docs/configuration.mdx) · [`docs/configuration-checklist.mdx`](../docs/configuration-checklist.mdx) · [`docs/authentication.mdx`](../docs/authentication.mdx) · [`docs/development/architecture.mdx`](../docs/development/architecture.mdx) · [`docs/security-operations/operations/provisioning.mdx`](../docs/security-operations/operations/provisioning.mdx) · [`docs/security-operations/operations/troubleshooting.mdx`](../docs/security-operations/operations/troubleshooting.mdx) · [`docs/security-operations/security/credentials.mdx`](../docs/security-operations/security/credentials.mdx) · [`docs/single-actions/workflow-and-cli.mdx`](../docs/single-actions/workflow-and-cli.mdx) · [`docs/security-operations/operations/verification.mdx`](../docs/security-operations/operations/verification.mdx) · [`docs/development/setup-pat-provider-audit-2026-10-06.md`](../docs/development/setup-pat-provider-audit-2026-10-06.md) · [`docs/issues/comment-commands.mdx`](../docs/issues/comment-commands.mdx)
### `local-web-setup-assistant` — Local web setup assistant
diff --git a/specs/catalog.json b/specs/catalog.json
index 6bcd945d8..d3b10c240 100644
--- a/specs/catalog.json
+++ b/specs/catalog.json
@@ -659,11 +659,15 @@
],
"workflows": [
"setup/workflows/agent-cli-provisioning.yml",
- "setup/workflows/copilot_credential_health.yml"
+ "setup/workflows/copilot_credential_health.yml",
+ ".github/workflows/copilot_issue_comment.yml",
+ ".github/workflows/copilot_pull_request_comment.yml"
],
"entrypoints": [
"src/cli/commands/setup.ts",
- "src/cli/commands/doctor.ts"
+ "src/cli/commands/doctor.ts",
+ "src/application/usecases/issue_comment_use_case.ts",
+ "src/application/usecases/pull_request_review_comment_use_case.ts"
],
"code": [
"src/domain/setup.ts",
@@ -755,7 +759,13 @@
"src/domain/copilot_command.ts",
"src/application/usecases/comment_automation_command_workflow.ts",
"src/application/policies/copilot_interaction_policy.ts",
- "src/application/policies/publication_message_catalog.ts"
+ "src/application/policies/publication_message_catalog.ts",
+ "src/application/ports/github_runtime_doctor_ports.ts",
+ "src/application/policies/github_runtime_doctor_policy.ts",
+ "src/application/policies/github_runtime_doctor_presentation_policy.ts",
+ "src/application/usecases/read_copilot_doctor_workflow.ts",
+ "src/data/repository/github/runtime_doctor_repository.ts",
+ "src/infrastructure/composition/github_runtime_doctor_composition_root.ts"
],
"tests": [
"src/data/repository/__tests__/repository_version_tags_query_repository.test.ts",
@@ -824,7 +834,13 @@
"src/application/usecases/execution/__tests__/setup_execution_workflow.test.ts",
"src/domain/__tests__/github_runtime_doctor.test.ts",
"src/domain/__tests__/copilot_command.test.ts",
- "src/application/usecases/__tests__/comment_automation_use_case.test.ts"
+ "src/application/usecases/__tests__/comment_automation_use_case.test.ts",
+ "src/application/policies/__tests__/github_runtime_doctor_policy.test.ts",
+ "src/application/policies/__tests__/github_runtime_doctor_presentation_policy.test.ts",
+ "src/application/usecases/__tests__/read_copilot_doctor_workflow.test.ts",
+ "src/application/usecases/__tests__/runtime_doctor_security_integration.test.ts",
+ "src/data/repository/github/__tests__/runtime_doctor_repository.test.ts",
+ "src/infrastructure/composition/__tests__/github_runtime_doctor_composition_root.test.ts"
],
"documentation": [
"README.md",
diff --git a/specs/github-runtime-doctor-command.md b/specs/github-runtime-doctor-command.md
index 6c47178ab..0bd8e3b70 100644
--- a/specs/github-runtime-doctor-command.md
+++ b/specs/github-runtime-doctor-command.md
@@ -333,13 +333,35 @@ Document that local setup operator PAT and Action runtime PAT are distinct and t
## 19. Definition of Done
-Implementation status: **incomplete**. The current code covers the domain contract,
-trusted strict policy parsing, aggregate ordering/priority, command recognition,
-and bilingual help discoverability. The route fails closed with an unavailable
-response; it does not claim that any runtime prerequisite passed. This partial
-state does not satisfy the numeric 52-case budget or the capability acceptance
-scenarios below. Do not mark this specification implemented or its catalog
-capability complete until every unchecked item is verified.
+Implementation status: **implemented in the managed feature branch; live
+promotion and browser acceptance pending**. The runtime composition binds only
+metadata queries using the installed runtime credential. The command performs
+all seven groups, retains independent successful checks, validates the effective
+profile/start settings using the owning policies, and inspects the dependency
+reference already resolved by runtime setup. It does not certify an entire remote
+dependency graph from that setting.
+
+The canonical policy reader is shared with the approval observer and retains
+repository-over-organization precedence. Its read-only method never consults an
+injected variable after failure. Files use six fixed paths, 200,000 decoded UTF-8
+bytes per file, one exact 40-hex default-branch revision, and a final revision
+fence. Workflow YAML uses the JSON schema and is never executed. Native branch
+queries retain their 100-node complete-page boundary and refuse ambiguous,
+foreign or moved refs. Exact PR identity uses the existing canonical read policy
+and a final head fence. Group states and actions are closed; raw source text,
+provider errors and credentials never enter the report.
+
+The fixed 52-case floor is retained. Current evidence covers **124 distinct
+feature cases** without double counting existing observer tests: 25 domain and
+configuration cases (the original ten domain cases plus fifteen new prerequisite
+cases); 23 application cases; 35 provider cases (28 metadata adapters plus seven
+new policy-reader cases); ten workflow structural cases; 17 presentation cases;
+and 14 integration cases (eight command/security, three composition, two
+semantic correlation cases, and one real issue-route configuration regression). The nine-suite focused run passed 138 tests; the additional real issue-route suite passed 18 tests,
+including compatibility tests outside this 124-case feature budget. The full managed-branch suite passed 569 suites and 7,454 tests (35 existing
+platform skips), with every repository coverage budget and setup ledger check
+passing. Doctor bypasses only the incompatible-configuration mutation guard and SDD-continuation getter so it can report their blocked state; mutation commands retain those gates. Generated bundles, live issue/PR commands and narrow-screen browser
+acceptance must be recorded separately before final readiness.
- [ ] Every normative requirement has acceptance and traceability.
- [ ] Architecture boundaries are implemented and automatically enforced; doctor has no mutation or model capability.
diff --git a/specs/setup-configuration-credentials-and-doctor.md b/specs/setup-configuration-credentials-and-doctor.md
index 56d8f0390..7dbb189e6 100644
--- a/specs/setup-configuration-credentials-and-doctor.md
+++ b/specs/setup-configuration-credentials-and-doctor.md
@@ -676,3 +676,21 @@ widths, canceled prompts, secret masking, and GitHub permission variants.
- Decision: one configuration policy serves setup, doctor, and workflow inputs.
- Rejected: storing credentials in YAML/JSON or silently overwriting managed files.
- Follow-up: cross-provider transactional rollback is outside this baseline.
+
+
+## GitHub conversation runtime doctor extension — issue #461
+
+The companion `github-runtime-doctor-command.md` owns the deterministic
+`/copilot doctor` conversation audit. It uses the Action runtime credential,
+not the local setup operator PAT. Its physically frozen query port exposes only
+repository, issue, native linked branch, canonical PR and trusted effective
+approval policy reads. It never exposes local setup provisioning, credential
+health dispatch, approval submission, model or Git/file mutation capabilities.
+
+All public strings join the existing publication catalog (35 reviewed English
+and Spanish message IDs). Independent unavailable checks preserve successful
+facts. A valid active approval policy reports declared prerequisites as a warning,
+without inferring producer health or write grants. Existing off semantics require
+trusted verified absence. The feature adds 124 distinct contract and risk cases
+across the numeric companion budget; live promotion and browser proof remain
+pending until their actual results are recorded.
diff --git a/src/application/policies/__tests__/github_runtime_doctor_policy.test.ts b/src/application/policies/__tests__/github_runtime_doctor_policy.test.ts
new file mode 100644
index 000000000..c31ffc552
--- /dev/null
+++ b/src/application/policies/__tests__/github_runtime_doctor_policy.test.ts
@@ -0,0 +1,46 @@
+import { doctorRepositoryFixture } from '../../usecases/__tests__/fixtures/runtime_doctor_fixture';
+import { doctorRepositoryPrerequisites, doctorWorkflowContracts, validDoctorConfiguration } from '../github_runtime_doctor_policy';
+import type { DoctorConfiguration, DoctorRepositoryRead } from '../../ports/github_runtime_doctor_ports';
+const config: DoctorConfiguration = { profile: { schemaVersion: 1, enabled: ['feature'] }, preBranchSdd: true,
+ issueManagedBranches: true, dependencyBranch: 'develop' };
+function changed(path: keyof ReturnType['files'], text?: string) {
+ const snapshot = doctorRepositoryFixture();
+ return { ...snapshot, files: { ...snapshot.files, [path]: text === undefined ? { state: 'missing' as const } : { state: 'present' as const, text } } };
+}
+describe('runtime doctor pure prerequisite decisions', () => {
+ it('validates admitted configuration through the existing profile and start policies', () => expect(validDoctorConfiguration(config)).toBe(true));
+ it('does not turn a missing configuration into valid defaults', () => expect(validDoctorConfiguration(undefined)).toBe(false));
+ it('rejects incompatible prebranch and managed-branch flags', () => expect(validDoctorConfiguration({ ...config, issueManagedBranches: false })).toBe(false));
+ it('rejects retired issue-profile versions', () => expect(validDoctorConfiguration({ ...config, profile: { schemaVersion: 2 } } as unknown as DoctorConfiguration)).toBe(false));
+ it('rejects unknown issue kinds', () => expect(validDoctorConfiguration({ ...config, profile: { schemaVersion: 1, enabled: ['made-up'] } } as unknown as DoctorConfiguration)).toBe(false));
+ it('rejects unsafe dependency references', () => expect(validDoctorConfiguration({ ...config, dependencyBranch: '--upload=/private' })).toBe(false));
+ it('validates catalog, template and standard from one snapshot', () => expect(doctorRepositoryPrerequisites(doctorRepositoryFixture())).toBe('pass'));
+ it('keeps unreadable prerequisites unavailable', () => expect(doctorRepositoryPrerequisites({ state: 'unavailable' })).toBe('unavailable'));
+ it('blocks a proven missing specification standard', () => expect(doctorRepositoryPrerequisites(changed('specs/README.md'))).toBe('blocked'));
+ it('blocks malformed catalog JSON', () => expect(doctorRepositoryPrerequisites(changed('specs/catalog.json', '{'))).toBe('blocked'));
+ it('blocks unsupported catalog versions', () => expect(doctorRepositoryPrerequisites(changed('specs/catalog.json', '{"version":2,"capabilities":[]}'))).toBe('blocked'));
+ it('blocks duplicate capability identities', () => expect(doctorRepositoryPrerequisites(changed('specs/catalog.json', JSON.stringify({ version: 1,
+ capabilities: [{ id: 'safe', specs: ['specs/safe.md'] }, { id: 'safe', specs: ['specs/safe.md'] }] })))).toBe('blocked'));
+ it('blocks unsafe capability source paths', () => expect(doctorRepositoryPrerequisites(changed('specs/catalog.json', JSON.stringify({ version: 1,
+ capabilities: [{ id: 'safe', specs: ['../private.md'] }] })))).toBe('blocked'));
+ it('blocks malformed capability identifiers', () => expect(doctorRepositoryPrerequisites(changed('specs/catalog.json', JSON.stringify({ version: 1,
+ capabilities: [{ id: '@mention', specs: ['specs/safe.md'] }] })))).toBe('blocked'));
+ it('blocks a template without a Markdown title', () => expect(doctorRepositoryPrerequisites(changed('specs/_template.md', 'empty standard'))).toBe('blocked'));
+});
+describe('installed workflow structural contracts', () => {
+ it('accepts all required events and local Action steps without claiming write grants', () => expect(doctorWorkflowContracts(doctorRepositoryFixture())).toBe('pass'));
+ it('accepts documented event arrays', () => expect(doctorWorkflowContracts(changed('.github/workflows/copilot_issue.yml',
+ 'on: [issues]\njobs: {a: {steps: [{uses: "vypdev/copilot@v3"}]}}'))).toBe('pass'));
+ it('accepts event maps parsed with the JSON YAML schema', () => expect(doctorWorkflowContracts(changed('.github/workflows/copilot_issue.yml',
+ 'on: {issues: {types: [opened]}}\njobs: {a: {steps: [{uses: "./"}]}}'))).toBe('pass'));
+ it('keeps an incomplete repository sample unavailable', () => expect(doctorWorkflowContracts({ state: 'unavailable' })).toBe('unavailable'));
+ it('blocks a missing required comment workflow', () => expect(doctorWorkflowContracts(changed('.github/workflows/copilot_issue_comment.yml'))).toBe('blocked'));
+ it('blocks the wrong event instead of searching another workflow', () => expect(doctorWorkflowContracts(changed('.github/workflows/copilot_issue.yml',
+ 'on: push\njobs: {a: {steps: [{uses: "./"}]}}'))).toBe('blocked'));
+ it('blocks a workflow without the installed Action', () => expect(doctorWorkflowContracts(changed('.github/workflows/copilot_issue.yml',
+ 'on: issues\njobs: {a: {steps: [{run: "echo SECRET"}]}}'))).toBe('blocked'));
+ it('does not execute YAML constructors or repository code', () => expect(doctorWorkflowContracts(changed('.github/workflows/copilot_issue.yml',
+ 'on: !!js/function "function(){ throw new Error(SECRET) }"'))).toBe('blocked'));
+ it('rejects a malformed workflow document', () => expect(doctorWorkflowContracts(changed('.github/workflows/copilot_issue.yml', 'on: ['))).toBe('blocked'));
+ it('rejects a non-record repository document', () => expect(doctorWorkflowContracts(changed('.github/workflows/copilot_issue.yml', '- issues'))).toBe('blocked'));
+});
diff --git a/src/application/policies/__tests__/github_runtime_doctor_presentation_policy.test.ts b/src/application/policies/__tests__/github_runtime_doctor_presentation_policy.test.ts
new file mode 100644
index 000000000..8edea65a3
--- /dev/null
+++ b/src/application/policies/__tests__/github_runtime_doctor_presentation_policy.test.ts
@@ -0,0 +1,60 @@
+import { formatGithubRuntimeDoctor, isGithubRuntimeDoctorProjection } from '../github_runtime_doctor_presentation_policy';
+import { aggregateGithubRuntimeDoctorReport, GITHUB_RUNTIME_DOCTOR_GROUPS } from '../../../domain/github_runtime_doctor';
+import { Result } from '../../../data/model/result';
+import { renderSemanticReply, selectSemanticReplyIntents } from '../semantic_result_publication_policy';
+const projection = () => ({ report: aggregateGithubRuntimeDoctorReport({ target: { kind: 'issue', repositoryId: 42, issueNumber: 7 },
+ checks: GITHUB_RUNTIME_DOCTOR_GROUPS.map(group => ({ group, state: 'pass' as const })) }),
+ owner: 'o', repository: 'r', revision: 'a'.repeat(40), serverOrigin: 'https://github.com' });
+describe('runtime doctor public presentation', () => {
+ it.each(['pass', 'warning', 'blocked', 'unavailable', 'not-applicable'] as const)('renders textual %s state with exactly one primary action', state => {
+ const value = projection(); const report = aggregateGithubRuntimeDoctorReport({ target: value.report.target,
+ checks: GITHUB_RUNTIME_DOCTOR_GROUPS.map(group => ({ group, state })) });
+ const body = formatGithubRuntimeDoctor({ ...value, report });
+ expect(body.match(/Next action:/gu)).toHaveLength(1); expect(body.match(/^- /gmu)).toHaveLength(7);
+ expect(body.length).toBeLessThan(6000); expect(body).toContain('do not prove');
+ });
+ it('renders all labels and one primary action in Spanish', () => {
+ const body = formatGithubRuntimeDoctor(projection(), 'es-ES'); expect(body).toContain('Diagnóstico del runtime');
+ expect(body).toContain('Siguiente acción'); expect(body).not.toContain('Next action'); expect(body).toContain('solo lee evidencias');
+ });
+ it('falls back atomically for unsupported locales', () => {
+ expect(formatGithubRuntimeDoctor(projection(), 'fr-FR')).toEqual(formatGithubRuntimeDoctor(projection(), 'en-US'));
+ });
+ it.each(['https://github.com/?token=SECRET', 'https://user:SECRET@github.com', 'javascript:SECRET', 'https://github.com/prefix'])('suppresses unsafe configured link %s', serverOrigin => {
+ const body = formatGithubRuntimeDoctor({ ...projection(), serverOrigin }); expect(body).not.toContain('SECRET'); expect(body).not.toContain('](');
+ });
+ it('accepts independently configured enterprise origin', () => {
+ expect(formatGithubRuntimeDoctor({ ...projection(), serverOrigin: 'https://github.enterprise.test' })).toContain('https://github.enterprise.test/o/r/tree/');
+ });
+ it('suppresses unsafe repository identity rather than emitting Markdown or mentions', () => {
+ const body = formatGithubRuntimeDoctor({ ...projection(), owner: '@all](SECRET)