Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/code-navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ Start with the issue's terms in `GLOSSARY.md`, then use this index to find their
| Harness, Model, Effort | `src/harness.rs` and `src/harness/` | `tests/harness_choice.rs`, `tests/harness.rs` |
| Session prompt, Factory skills | `src/prompt.rs`; `src/skills.rs`; `skills/` | `src/skills/tests.rs`, `tests/packaging.rs` |
| Trusted account, the issue a session gets | `src/trust.rs` — `issue_trust`, `ticket_trust`, `untrusted_edit` over `Reads`; the GitHub adapter's trust reads, `src/github/trust.rs`; `src/github/session_issue.rs` — `issue_for_session`; `src/delivery.rs` — `hand_over_issue` | Inline tests, including `src/trust.rs` and `src/prompt.rs`; `tests/fresh_run.rs`, `tests/pickup_run.rs` |
| Session log, Resume | `src/session.rs` and `src/session/` | `src/session/execution_tests.rs`, `tests/background_work.rs` |
| Session log, Resume | `src/session.rs` and `src/session/`; `src/session/kind.rs` — `Kind`, `Logged`: every session kind, its Session-log token, the Command record stage it enters and its `--session` rule (`selects`) | `src/session/execution_tests.rs`, inline tests in `src/session/kind.rs`, `tests/background_work.rs` |
| Command log, Activity log | `src/logs.rs`; `src/logs/recording.rs` — `Record` | `src/logs/recording/tests.rs`, `tests/command_logs.rs`, `tests/activity_log.rs` |
| Command-log allocation, Session naming, child inheritance and filename decoding | `src/logs/owner.rs` — `allocate`, `Owner`, `decode`, `session_matches`; `src/session/recording.rs` — captured Model recording | `src/logs/owner/tests.rs`, `src/logs/recording/tests.rs`, `src/session/recording/tests.rs`, `tests/command_logs.rs`, `tests/status.rs`, `tests/show_logs.rs` |
| Command record, status | `src/logs/recording.rs`; `src/logs/command_record.rs`; `src/logs/reading.rs` — `Snapshot`, exact-parent trees and observations; `src/logs/reading/history.rs` — historical recovery; `src/logs/status.rs` — formatting; `src/logs/process_identity.rs` | `src/logs/reading/tests.rs`, `tests/status.rs`, `src/logs/recording/tests.rs` |
| Command naming, Session selection, show, logs | `src/logs/reading/selection.rs` — resolution, captured outer owner and child-refusal target, Session inventory/selection and bounded latest events; `src/logs/status/inspect.rs` — formatting and log streaming | `src/logs/reading/tests.rs`, `tests/show_logs.rs` |
| Command naming, Session selection, show, logs | `src/logs/reading/selection.rs` — resolution, captured outer owner and child-refusal target, Session inventory/selection and bounded latest events, each `--session` word matched by `src/session/kind.rs` — `selects`; `src/logs/status/inspect.rs` — formatting and log streaming | `src/logs/reading/tests.rs`, `tests/show_logs.rs` |
| Stop | `src/logs/reading.rs` — `retained_work` reload by captured record; `src/logs/reading/selection.rs` — `Selection::stop_target`; `src/logs/stop.rs` — request, signaling and waiting; `src/claim.rs` — Stop endings | `src/logs/reading/tests.rs`, `tests/stop.rs`; inline Claim and Stop tests |
| User config, Setup | `src/config.rs`; `src/setup.rs` | `tests/user_config.rs`, `tests/setup.rs` |
| Architecture review, Architect run | `src/architect.rs` | `tests/architect_run.rs` |
Expand Down
30 changes: 15 additions & 15 deletions src/delivery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use crate::recovery::{self, Preservation, Recovery};
use crate::run::{Goal, Reached};
use crate::run_ending::Cause;
use crate::security::review::{Hold as SecurityHold, Outcome as SecurityOutcome, Review};
use crate::session::{Logs, Sessions};
use crate::session::{Kind, Logs, Sessions};
use crate::skills;
use crate::trust::{self, IssueText};
use crate::worktree::{ForeignCommits, Merge, PendingMerge, Preserved, Worktree};
Expand Down Expand Up @@ -81,11 +81,11 @@ pub enum OpeningSession {
}

impl OpeningSession {
/// The session's kind, in its progress lines and log name.
pub fn kind(self) -> &'static str {
/// The session's kind.
pub fn kind(self) -> Kind {
match self {
Self::Implementation => "implement",
Self::SpecReview => "spec-review",
Self::Implementation => Kind::Implementation,
Self::SpecReview => Kind::SpecReview,
}
}

Expand Down Expand Up @@ -415,8 +415,8 @@ trait Outside {
fn stage(&mut self, stage: Stage);
/// Fast-forward the Issue branch to its head on origin.
fn catch_up(&mut self) -> Result<()>;
/// Run the session `kind` given `prompt`.
fn session(&mut self, kind: &str, prompt: &str) -> Result<()>;
/// Run a session of `kind` given `prompt`.
fn session(&mut self, kind: Kind, prompt: &str) -> Result<()>;
fn security_review(&mut self, prompt: &str) -> Result<Review>;
fn record_security_review(&mut self, review: &SecurityOutcome) -> Result<()>;
/// Push the Issue branch.
Expand Down Expand Up @@ -524,7 +524,7 @@ impl Outside for InWorktree<'_> {
self.worktree.fast_forward_to_origin()
}

fn session(&mut self, kind: &str, prompt: &str) -> Result<()> {
fn session(&mut self, kind: Kind, prompt: &str) -> Result<()> {
self.sessions.run(kind, prompt)?;
self.pull_request.ensure_actionable(false)
}
Expand Down Expand Up @@ -705,7 +705,7 @@ impl repair_loop::Outside for RunOutside<'_> {
})
}

fn repair(&mut self, kind: &str, repair: Repair) -> Result<()> {
fn repair(&mut self, kind: Kind, repair: Repair) -> Result<()> {
let (issue, base, pr_url) = (self.issue, self.base, self.pr_url);
let branch = self.worktree.branch();
let prompt = match repair {
Expand Down Expand Up @@ -781,7 +781,7 @@ mod tests {
#[derive(Debug, PartialEq, Eq)]
enum Call {
CatchUp,
Session(String),
Session(Kind),
Push,
Annotation,
Checklist,
Expand Down Expand Up @@ -841,9 +841,9 @@ mod tests {
self.calls.push(Call::CatchUp);
self.check(Fails::CatchUp)
}
fn session(&mut self, kind: &str, _: &str) -> Result<()> {
self.calls.push(Call::Session(kind.to_string()));
self.log = Some(PathBuf::from(format!("/logs/{kind}.jsonl")));
fn session(&mut self, kind: Kind, _: &str) -> Result<()> {
self.calls.push(Call::Session(kind));
self.log = Some(PathBuf::from(format!("/logs/{}.jsonl", kind.token())));
self.check(Fails::Session)
}
fn push(&mut self) -> Result<()> {
Expand Down Expand Up @@ -969,7 +969,7 @@ mod tests {
outside.calls,
[
Call::CatchUp,
Call::Session("spec-review".to_string()),
Call::Session(Kind::SpecReview),
Call::Push,
Call::Annotation,
Call::Checklist,
Expand All @@ -985,7 +985,7 @@ mod tests {
assert!(deliver(&mut outside, Goal::ReadyForReview, true).is_ok());
assert_eq!(
outside.calls[..2],
[Call::CatchUp, Call::Session("spec-review".to_string())]
[Call::CatchUp, Call::Session(Kind::SpecReview)]
);
}

Expand Down
28 changes: 15 additions & 13 deletions src/delivery/repair_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::run::Goal;
use crate::worktree::{ForeignCommits, Merge};

use super::Stage;
use crate::session::Kind;

/// Which upstream a conflict Repair resolves a merge of.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
Expand Down Expand Up @@ -79,8 +80,9 @@ pub(super) trait Outside {
failed: &FailedChecks,
) -> Result<Option<Ci>>;

/// Start the Repair session `kind`, as in `repair-1`, for `repair`.
fn repair(&mut self, kind: &str, repair: Repair) -> Result<()>;
/// Start the Repair session of `kind`, as in `Kind::Repair(1)`, for
/// `repair`.
fn repair(&mut self, kind: Kind, repair: Repair) -> Result<()>;

/// Whether the Run compares its red checks with the Base branch's, so
/// that some may be Inherited failures.
Expand Down Expand Up @@ -173,15 +175,15 @@ struct Budgets {
}

impl Budgets {
/// Counts the Repair about to start, returning its kind, `repair-<n>`,
/// and its progress line, or fails if it would be one too many.
fn next_repair(&mut self, cause: &str) -> Result<(String, String)> {
/// Counts the Repair about to start, returning its numbered kind and its
/// progress line, or fails if it would be one too many.
fn next_repair(&mut self, cause: &str) -> Result<(Kind, String)> {
if self.repairs == MAX_REPAIRS {
bail!("repairs exhausted: {cause}");
}
self.repairs += 1;
let line = format!("{cause}; starting Repair {} of {MAX_REPAIRS}", self.repairs);
Ok((format!("repair-{}", self.repairs), line))
Ok((Kind::Repair(self.repairs), line))
}

/// Counts a round taken because `upstream` moved, or fails if it would
Expand Down Expand Up @@ -283,7 +285,7 @@ impl<O: Outside> RepairLoop<'_, O> {
let (kind, line) = self.budgets.next_repair(cause)?;
self.outside.progress(line);
self.outside.stage(Stage::RepairSession);
self.outside.repair(&kind, repair)
self.outside.repair(kind, repair)
}

/// Count a round taken because the Base branch moved `when`.
Expand Down Expand Up @@ -457,7 +459,7 @@ mod tests {
/// Gave this head its Check re-run of these checks of its own.
Rerun(String, Option<String>, Vec<String>),
/// Started the Repair session of this kind for this Repair.
Repair(String, Started),
Repair(Kind, Started),
/// Took the Base fix for these Inherited failures, at this Base
/// branch commit.
BaseFix(String, Vec<String>),
Expand Down Expand Up @@ -677,15 +679,15 @@ mod tests {
Ok(is.map(|is| is.ci()))
}

fn repair(&mut self, kind: &str, repair: Repair) -> Result<()> {
fn repair(&mut self, kind: Kind, repair: Repair) -> Result<()> {
let started = match repair {
Repair::Conflict(upstream) => Started::Conflict(upstream),
Repair::CiFix(failed) => {
Started::CiFix(names(&failed.own), names(&failed.inherited))
}
Repair::Review { from } => Started::Review(from.to_string()),
};
self.did.push(Did::Repair(kind.to_string(), started));
self.did.push(Did::Repair(kind, started));
match self.script.repair_error {
Some(error) => bail!(error),
None => Ok(()),
Expand Down Expand Up @@ -773,9 +775,9 @@ mod tests {
Did::Watch(head.to_string(), Some("b1".to_string()))
}

/// The Repair session `repair-<n>` for `started`.
/// The numbered Repair session `n` for `started`.
fn repair(n: usize, started: Started) -> Did {
Did::Repair(format!("repair-{n}"), started)
Did::Repair(Kind::Repair(n), started)
}

/// A CI-fix Repair given the branch's own failures `own`, and the
Expand Down Expand Up @@ -2044,7 +2046,7 @@ mod tests {
assert_eq!(
budgets.next_repair(cause).unwrap(),
(
format!("repair-{n}"),
Kind::Repair(n),
format!("{cause}; starting Repair {n} of {MAX_REPAIRS}")
)
);
Expand Down
30 changes: 5 additions & 25 deletions src/logs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use std::sync::{LazyLock, Mutex, MutexGuard, PoisonError};
use crate::config::UserConfig;
use crate::harness::Choice;
use crate::issue::{IssueUrl, Repo};
use crate::session::Logged;

use activity::Kind;
pub use command_record::StageName as Stage;
Expand Down Expand Up @@ -203,31 +204,10 @@ pub fn stage(stage: Stage) {
record().stage(stage, None);
}

/// Keep the current Session log, including a Resume's own log.
pub fn session(kind: &str, path: &Path) {
let original = kind.strip_suffix("-resume").unwrap_or(kind);
let stage = if original == "architecture-review" {
Stage::ArchitectureReview
} else if original == "security-audit" {
Stage::SecurityAudit
} else if original.starts_with("security-reproduction-") {
Stage::SecurityReproduction
} else if original == "security-fix-publishing" {
Stage::SecurityFixPublishing
} else if kind.ends_with("-resume") {
Stage::Resume
} else if kind.starts_with("repair-") {
Stage::Repair
} else if kind == "security-review" {
Stage::SecurityReview
} else if kind == "implement" {
Stage::Implement
} else if kind == "spec-review" {
Stage::SpecReview
} else {
return;
};
record().stage(stage, Some(path));
/// Keep the current Session log, including a Resume's own log, as
/// `session` enters its stage.
pub fn session(session: Logged, path: &Path) {
record().stage(session.stage(), Some(path));
}

/// The selected Issue branch, retained unless completion says it was removed.
Expand Down
8 changes: 1 addition & 7 deletions src/logs/reading/selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,13 +190,7 @@ impl<'a> Observation<'a> {
else {
return false;
};
let original = session.strip_suffix("-resume").unwrap_or(session);
session == kind
|| original == kind
|| (kind == "repair"
&& original
.strip_prefix("repair-")
.is_some_and(|number| number.parse::<u32>().is_ok()))
crate::session::kind::selects(kind, session)
})
.with_context(|| format!("no {kind} Session log for this Command"))
}
Expand Down
12 changes: 6 additions & 6 deletions src/pass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,9 @@ use crate::logs::{self, Pass, Work};
use crate::progress;
use crate::ready::{self, ReadyIssue};
use crate::run::{self, Ended, StartedBy};
use crate::session::{Logs, Purpose, Sessions};
use crate::session::{Kind, Logs, Purpose, Sessions};
use crate::worktree::ReviewWorktree;

/// The Architecture review session's kind, in its progress lines and log
/// name.
const REVIEW: &str = "architecture-review";

/// What a pass dispatches, as `thirdshift <Issue URL>` would run it, but on
/// the pass's Base branch `base`, whatever the Launch directory has checked
/// out.
Expand Down Expand Up @@ -222,7 +218,11 @@ impl Outside for LaunchAndGitHub<'_> {
let harness = self.harness.clone();
let concluded = Sessions::within(&logs, worktree.path(), &harness, |sessions| {
progress::step(starting);
let final_message = sessions.run_to_final_message(Purpose::Ordinary, REVIEW, prompt)?;
let final_message = sessions.run_to_final_message(
Purpose::Ordinary,
Kind::ArchitectureReview,
prompt,
)?;
conclude(self, final_message.as_deref())
});
// Removed once the review is concluded.
Expand Down
4 changes: 2 additions & 2 deletions src/security/audit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::logs;
use crate::process::{self, Control, Interruption};
use crate::progress;
use crate::prompt;
use crate::session::{Logs, Purpose, Sessions};
use crate::session::{Kind, Logs, Purpose, Sessions};
use crate::skills;
use crate::worktree::ReviewWorktree;

Expand Down Expand Up @@ -121,7 +121,7 @@ pub fn run(launch: &Git, repo: &Repo, base: &str, harness: &Choice) -> Attempt {
progress::step(format!("starting the Security audit of {base}"));
started = true;
let message =
sessions.run_to_final_message(Purpose::Security, "security-audit", &prompt)?;
sessions.run_to_final_message(Purpose::Security, Kind::SecurityAudit, &prompt)?;
let line = message.as_deref().and_then(|message| {
message
.lines()
Expand Down
5 changes: 3 additions & 2 deletions src/security/fixing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::issue::{IssueUrl, Repo};
use crate::labels::Label;
use crate::prompt;
use crate::security::reproduction::FixSize;
use crate::session::{Logs, Purpose, Sessions};
use crate::session::{Kind, Logs, Purpose, Sessions};
use crate::worktree::ReviewWorktree;

mod publication;
Expand Down Expand Up @@ -58,7 +58,8 @@ pub fn publish(
} else {
Purpose::Security
};
let message = sessions.run_to_final_message(purpose, "security-fix-publishing", &prompt)?;
let message =
sessions.run_to_final_message(purpose, Kind::SecurityFixPublishing, &prompt)?;
publication::publish(
&mut GitHub::new(),
repo,
Expand Down
4 changes: 2 additions & 2 deletions src/security/reproduction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::harness::Choice;
use crate::issue::Repo;
use crate::logs;
use crate::prompt;
use crate::session::{Logs, Purpose, Sessions};
use crate::session::{Kind, Logs, Purpose, Sessions};
use crate::worktree::ReviewWorktree;

// Declaration order is severity order, most severe first.
Expand Down Expand Up @@ -124,7 +124,7 @@ pub fn run(
Sessions::within(&logs, worktree.path(), harness, |sessions| {
let message = sessions.run_to_final_message(
Purpose::Security,
&format!("security-reproduction-{number}"),
Kind::SecurityReproduction(number),
&prompt,
)?;
let message = message.as_deref().unwrap_or_default().trim_end();
Expand Down
4 changes: 2 additions & 2 deletions src/security/review.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use super::reproduction::{FixSize, Outcome as ReproductionOutcome, Reproduction,
use crate::github::{DraftAdvisory, FindingDraft, FindingProvenance, GitHub};
use crate::harness::interpretation::SafeguardRefusal;
use crate::issue::IssueUrl;
use crate::session::{Purpose, Sessions};
use crate::session::{Kind, Purpose, Sessions};
use crate::worktree::Worktree;
use crate::{logs, progress, prompt};

Expand Down Expand Up @@ -41,7 +41,7 @@ pub(crate) fn run(
report.display()
));
let message =
sessions.run_to_final_message(Purpose::SecurityReview, "security-review", &text)?;
sessions.run_to_final_message(Purpose::SecurityReview, Kind::SecurityReview, &text)?;
// A session may change commits on the Issue branch, but it cannot hand
// us a replacement checkout to observe or publish findings from.
worktree.head()?;
Expand Down
Loading
Loading