diff --git a/docs/code-navigation.md b/docs/code-navigation.md index cb7d6122..1cd834ac 100644 --- a/docs/code-navigation.md +++ b/docs/code-navigation.md @@ -21,10 +21,10 @@ Start with the issue's terms in `GLOSSARY.md`, then use this index to find their | CI-red Ticket, Flaky-test Ticket | `src/github/base_ci.rs` — latest push attempts, failed-jobs re-run, evidence and report deduplication; `src/pickup.rs` — CI reservation and reporting; `src/implement.rs` — recorded flaky evidence bypasses bug diagnosis; `src/labels.rs` — `FLAKY_TEST` | `tests/pickup_run/base_ci.rs`, `tests/bug_run.rs` | | Inherited failure, check-state reading, Base fix | `src/ci.rs` — `reading` of each `Conclusion` the GitHub adapter (`src/github.rs`, `checks_on`) reports; `src/base_fix.rs` | Inline tests; `src/github/execution_tests.rs`; `tests/ci.rs`, `tests/base_fix.rs` | | Run notification | `src/notification.rs` — `RunNotification`; `src/email.rs` | `tests/run_notification.rs`, `tests/email_test.rs` | -| Report, Triage run | `src/triage.rs` — `Report`, `parse_recommendation`, `check_publication` (the GitHub checks both modes make), `publish` (filing, cross-links, closing `wontfix`, comments), questionnaires and session; `src/args.rs` — `TriageArgs`; `src/config.rs` — `triage.allow_ready_for_agent`, `triage.read_machine`, `triage_reads_machine`; `src/sandbox/triage.rs` — the `--read-machine` bwrap launch (read-only host, writable worktree, private Harness home, startup probe) and its host-socket seccomp filter; `src/sandbox.rs` — `Launch::pass_seccomp`; `skills/thirdshift-triage/`, `skills/thirdshift-to-questionnaire/`; `src/worktree.rs` — `ReviewWorktree::for_triage` | `tests/triage_run.rs`, `tests/issue_1711_retained_home.rs`; `src/sandbox/triage.rs` inline tests (the launch and seccomp filter) | -| Existing-issue Triage run | `src/triage/existing.rs` — trust gate, single-issue recommendation, reservation and Claim lifetime, in-place publication; `src/triage/images.rs` — trusted image extraction, bounded host downloads and local issue manifest; `src/prompt.rs` — `triage_issue`; `src/args.rs` — `TriageArgs` | `tests/triage_issue.rs`; `src/triage/images.rs` inline tests | +| Report, Triage run | `src/triage.rs` — `Report`, `check_comment_targets` (the GitHub checks both modes make), `publish` (filing, cross-links, closing `wontfix`, comments) and session; `src/triage/recommendation.rs` — `Recommendation::interpret` for the initial and corrected session output: category fallback and repository spelling, factory labels, state fallback, confirmation blocks, questionnaires, existing-issue rules, readiness permission and combined diagnostics; each proposed issue's `Filing` and each comment's target, `publishing`, recorded results, stdout JSON and the notification; `src/triage/correction.rs` — the one correction turn; `src/args.rs` — `TriageArgs`; `src/config.rs` — `triage.allow_ready_for_agent`, `triage.read_machine`, `triage_reads_machine`; `src/sandbox/triage.rs` — the `--read-machine` bwrap launch (read-only host, writable worktree, private Harness home, startup probe) and its host-socket seccomp filter; `src/sandbox.rs` — `Launch::pass_seccomp`; `skills/thirdshift-triage/`, `skills/thirdshift-to-questionnaire/`; `src/worktree.rs` — `ReviewWorktree::for_triage` | `tests/triage_run.rs`, `tests/triage_correction.rs`, `tests/issue_1711_retained_home.rs`; `src/triage/recommendation/tests.rs` (interpretation through `Recommendation::interpret`); `src/sandbox/triage.rs` inline tests (the launch and seccomp filter) | +| Existing-issue Triage run | `src/triage/existing.rs` — trust gate, reservation and Claim lifetime, in-place publication; `src/triage/recommendation.rs` — the single-issue recommendation rules; `src/triage/images.rs` — trusted image extraction, bounded host downloads and local issue manifest; `src/prompt.rs` — `triage_issue`; `src/args.rs` — `TriageArgs` | `tests/triage_issue.rs`; `src/triage/recommendation/tests.rs` (existing-target rules); `src/triage/images.rs` inline tests | | Unattended Triage Pass | `src/triage.rs` — `Input::read` resolves terminal/empty stdin before startup; `src/triage/pass.rs` — Command-limit admission, Sweep and Failure limit, ordered candidate selection, reservation and recheck; `src/pickup.rs` — shared `failures` accounting; `src/logs.rs` — `Pass::Triage` | `tests/triage_pass.rs` | -| Triage Report Base branch, private finding notifications | `src/launch.rs` — `selected_base`; `src/triage/existing.rs` — private finding metadata; `src/triage.rs` — notification and failure context; `src/run_ending.rs`, `src/notification.rs` — keep private failure details local | `tests/triage_spec_regressions.rs` | +| Triage Report Base branch, private finding notifications | `src/launch.rs` — `selected_base`; `src/triage/existing.rs` — private finding metadata; `src/triage/recommendation.rs` — `Recommendation::notification` and `one_line` field sanitization; `src/triage.rs` — chooses the private finding notice over the recommendation notification, and failure context; `src/run_ending.rs`, `src/notification.rs` — keep private failure details local | `tests/triage_spec_regressions.rs` | | Harness, Model, Effort | `src/harness.rs` and `src/harness/` | `tests/harness_choice.rs`, `tests/harness.rs` | | Built-with line | `src/harness/built_with.rs` — retained attribution reader, formatter and marker; `src/pull_request.rs` — PR body annotation | `tests/scoring.rs`, inline Harness and pull-request tests | | Retained pull-request attribution, Continuation identity | `src/logs/reading/attribution.rs` — `PullRequestAttribution`: normalized PR matching, chronological latest and exact lexical record-path/Command-name selection over a captured `Snapshot`; `src/logs/reading.rs` — `run_for_pull_request` filesystem adapter; Scoring samples and Bug-origin proposal/settlement use that adapter; `src/logs/metrics/bug_origins.rs` uses the existing metrics Snapshot | `src/logs/reading/tests.rs`, `src/logs/reading/attribution_tests.rs`, `tests/scoring.rs`, `tests/bug_origins/settlement.rs`, `tests/metrics.rs` | diff --git a/src/triage.rs b/src/triage.rs index 1777a585..e4dcda8c 100644 --- a/src/triage.rs +++ b/src/triage.rs @@ -3,22 +3,23 @@ use std::io::{IsTerminal, Read}; use anyhow::{Context, Result, ensure}; -use serde::{Deserialize, Serialize}; use crate::failed_run::FailedRun; use crate::git::Git; use crate::github::{GENERATED, GitHub}; use crate::harness::Choice; use crate::issue::{IssueInput, IssueUrl, Repo}; -use crate::labels::{self, Label}; +use crate::labels; use crate::{command_limit, launch, logs, prompt, session, worktree}; mod correction; mod existing; mod images; mod pass; +mod recommendation; pub use pass::{Outcome, run as run_pass}; +use recommendation::{Publishing, Recommendation, is_factory_label}; /// Input is resolved before Setup can consume a piped Report. pub enum Input { @@ -27,6 +28,14 @@ pub enum Input { } impl Input { + /// The existing issue to triage in place, if this is not a Report. + fn issue(&self) -> Option<&IssueUrl> { + match self { + Self::Issue(issue) => Some(issue), + Self::Report(_) => None, + } + } + /// No supplied work means an unattended Pass. Resolve stdin before Setup. pub fn read(issue: Option, file: Option<&str>) -> Result> { if let Some(issue) = issue { @@ -93,226 +102,6 @@ impl Report { } } -#[derive(Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -struct Recommendation { - category: String, - state: String, - issues: Vec, - #[serde(default, skip_serializing_if = "Vec::is_empty")] - comments: Vec, - investigation: String, -} - -#[derive(Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -struct ProposedComment { - url: String, - body: String, -} - -#[derive(Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -struct ProposedIssue { - title: String, - body: String, - #[serde(skip_deserializing, skip_serializing_if = "Option::is_none")] - url: Option, - #[serde(skip_serializing_if = "Option::is_none")] - category: Option, - #[serde(skip_serializing_if = "Option::is_none")] - state: Option, - #[serde(skip_serializing_if = "Option::is_none")] - confirmation_blocked: Option, - #[serde(skip_serializing_if = "Option::is_none")] - questionnaire: Option, - /// The state the issue is filed with before readiness permission, - /// resolved once by [`resolve_recommendation`]. - #[serde(skip)] - triaged: Option