Skip to content

feat(session): record csa_version + tool_version in session state (#714)#967

Merged
RyderFreeman4Logos merged 1 commit intomainfrom
feat/714-session-version-recording
Apr 20, 2026
Merged

feat(session): record csa_version + tool_version in session state (#714)#967
RyderFreeman4Logos merged 1 commit intomainfrom
feat/714-session-version-recording

Conversation

@RyderFreeman4Logos
Copy link
Copy Markdown
Owner

Summary

  • Sessions now persist csa_version (top-level) from env!("CARGO_PKG_VERSION") and a best-effort tool_version per [tools.<name>] captured via a --version probe at tool init.
  • Both fields are Option<String> with skip_serializing_if — older session state files deserialize silently as None.
  • csa session list --csa-version <X> filters sessions by the recorded binary version. Range filters, tool-version filter, sort-by, and the companion bisect-regression skill are deferred to followups.

Test plan

  • cargo test -p csa-session --release exit 0 (320 passed)
  • cargo test -p cli-sub-agent --release -- --skip gate --skip lefthook exit 0 (1411 passed, 53 filtered out — pre-existing lefthook-test hygiene issue not introduced here)
  • Targeted new tests cover: csa_version population on create, backward-compat deserialization with None default, tool_version probe parsing, session list --csa-version filter

Closes #714.

🤖 Generated with Claude Code

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces version tracking for both the CSA binary and the individual tools used within a session. It adds a csa_version field to the session state and a tool_version field to the tool state, with logic to automatically detect and persist these versions. The session list command has been updated with --csa-version and --show-version flags to filter and display this information. Additionally, tool state initialization was refactored into a separate module. Feedback suggests refactoring the tool state initialization to use the entry API for better clarity and replacing manual version string parsing with a regular expression for improved maintainability.

dirty = true;
}

if let Some(tool_state) = session.tools.get_mut(&tool_name) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This if let Some(...) will always succeed because the key is guaranteed to exist from the check in lines 13-26. This could be made clearer by using .unwrap() to signal this guarantee, or by refactoring to use the entry API to avoid the separate check and insertion.

Comment on lines +52 to +85
fn parse_first_numeric_version_token(text: &str) -> Option<String> {
let bytes = text.as_bytes();
let mut idx = 0;

while idx < bytes.len() {
let ch = text[idx..].chars().next()?;
let ch_len = ch.len_utf8();
let next_is_digit = text[idx + ch_len..]
.chars()
.next()
.is_some_and(|next| next.is_ascii_digit());
if ch.is_ascii_digit() || ((ch == 'v' || ch == 'V') && next_is_digit) {
let start = if ch.is_ascii_digit() { idx } else { idx + ch_len };
let mut end = start;
while end < bytes.len() {
let current = text[end..].chars().next()?;
if current.is_ascii_alphanumeric() || matches!(current, '.' | '-' | '_' | '+') {
end += current.len_utf8();
} else {
break;
}
}
if end > start {
let candidate = &text[start..end];
if candidate.chars().next().is_some_and(|c| c.is_ascii_digit()) {
return Some(candidate.to_string());
}
}
}
idx += ch_len;
}

None
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This manual parsing of the version string is complex and can be simplified using a regular expression. Since the regex crate is already a dependency of this crate, using it here would improve readability and maintainability without adding new dependencies.

fn parse_first_numeric_version_token(text: &str) -> Option<String> {
    use once_cell::sync::Lazy;
    use regex::Regex;

    // This regex finds a version string, optionally prefixed with 'v' or 'V'.
    // It captures the version number itself, which must start with a digit.
    static VERSION_RE: Lazy<Regex> = Lazy::new(|| {
        Regex::new(r"\b(?:v|V)?(?<version>\d[a-zA-Z0-9._+-]*)").unwrap()
    });

    VERSION_RE
        .captures(text)
        .and_then(|caps| caps.name("version").map(|m| m.as_str().to_string()))
}

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 85be157606

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +41 to +43
"Version probe timed out"
);
return None;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Terminate timed-out version probes

If the --version probe times out, this branch logs and returns without terminating the spawned child process. In Tokio, timing out the wait future does not guarantee the subprocess exits, so a tool that blocks on --version (e.g., interactive wrapper or broken install) can leave orphaned processes on each execution path that initializes tool state, eventually consuming process/PID resources.

Useful? React with 👍 / 👎.

@RyderFreeman4Logos RyderFreeman4Logos force-pushed the feat/714-session-version-recording branch from 85be157 to 51bbf81 Compare April 20, 2026 19:05
Sessions now persist the CSA binary version (`csa_version`, from
env!("CARGO_PKG_VERSION")) on creation and a best-effort tool version
(`tool_version` under each `[tools.<name>]`) captured at tool init
via a `--version` probe. Both fields are Option<String> and default
to None for backward compatibility — pre-feature session state files
deserialize silently.

`csa session list --csa-version <X>` filters sessions by recorded
binary version. More granular queries (range filters, tool-version
filter, sort-by) and the companion bisect-regression skill are
deferred to followups.

Closes #714.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@RyderFreeman4Logos RyderFreeman4Logos force-pushed the feat/714-session-version-recording branch from 51bbf81 to 6a1904d Compare April 20, 2026 19:26
@RyderFreeman4Logos
Copy link
Copy Markdown
Owner Author

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces version tracking for the CSA binary and external tools. It adds a csa_version field to the session state and a tool_version field to the tool state, implementing logic to automatically detect tool versions via a --version probe. The session list command now supports filtering by CSA version and toggling a version column. Additionally, the PR refactors several test suites into separate files and increments the project version to 0.1.451. I have no feedback to provide.

@RyderFreeman4Logos
Copy link
Copy Markdown
Owner Author

Audit Trail — Merge Rationale (Severity-Tiered Protocol)

Local pre-PR review (session `01KPP2SW129970ZNN9BS6MMM1Q`): verdict=fail/HAS_ISSUES but `findings=[]` and zero severity counts — classic parser FP.

Cloud bot review (round 0):

  • gemini-code-assist: 2 MEDIUM nits (if-let cleanup, regex refactor suggestion).
  • chatgpt-codex-connector: 1 P1tool_version probe didn't kill child on timeout → process leak on blocking --version invocations. Fixed in force-push 6a1904d.

Cloud bot review (round 1, after fix):

  • gemini-code-assist: "I have no feedback to provide" — clean.
  • (codex not re-triggered per cloud_bot_retrigger_command config.)

CI: All green. Test(macos-latest) FAILURE is pre-existing baseline per memory feedback_macos_ci_ignore.

Dismissed MEDIUMs: gemini's if-let/regex suggestions are stylistic nits on an already-readable parser. Not blocking per severity-tiered protocol. Not filing followup — pure style, no correctness impact.

Merging.

@RyderFreeman4Logos RyderFreeman4Logos merged commit 6d0a8d2 into main Apr 20, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(session): record CSA + tool version in session state for regression bisect

1 participant