Skip to content

Hardening + 10x discovery: security audit changeset#7

Merged
aytzey merged 4 commits into
mainfrom
audit/hardening-20260709
Jul 9, 2026
Merged

Hardening + 10x discovery: security audit changeset#7
aytzey merged 4 commits into
mainfrom
audit/hardening-20260709

Conversation

@aytzey

@aytzey aytzey commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Full hardening pass from an external (Codex) security/quality audit of the codebase, reviewed change-by-change before merge.

Highlights

  • 10x listing: parallel provider discovery + bounded per-file workers + single head/tail pass per session file. Verified on a real 716-session store: ~14s → ~1.4s with a byte-identical ID set.
  • Conversion fidelity fix: transcript text no longer goes through the whitespace-folding preview cleaner — code blocks, newlines and indentation survive branch/convert. (Real bug: multiline content was being flattened.)
  • MCP trust model: get_transcript redacts credential-like values by default and labels transcripts as untrusted data; --allow-secrets opts out, --read-only drops the writing tools. A tool call cannot bypass redaction.
  • Terminal injection closed: ANSI/OSC and bidi-override sequences stripped at parse and render boundaries.
  • Safer writes: converted sessions 0600/0700 + parent-dir fsync; deletes clean Claude sessions-index.json entries and jcode sidecars.
  • Updater hardening: response caps, timeout, extraction limits, managed-install detection (brew/go installs get pointed at the right upgrade command).
  • CI: coverage floors (80% total / 75% per package), -shuffle, actionlint, shellcheck, govulncheck, linux/arm64 build; release workflow verifies tag format + main ancestry. Community files + Dependabot.

Review adjustments made to the audit's version

  1. Claude index cleanup changed from fail-closed to best-effort: the index is Claude's own derived data in a private format and gets rebuilt on its next scan — a corrupt or future-format index must never make a session impossible to delete.
  2. Per-package coverage floor set to 75% (total stays 80%): two packages sit at ~80.1–80.3%, and a floor equal to the current value turns any small refactor into a red build.

Gates at HEAD: gofmt/vet clean, golangci-lint 0 issues, go test ./... -race green, coverage 81.1% total (80.1/89.8/80.3/82.2 per package), real-store parity + timing verified.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added guided templates for bug reports, feature requests, and pull requests.
    • Introduced clearer security, contribution, and code-of-conduct guidance.
    • Added safer update/install handling across supported platforms.
  • Bug Fixes

    • Improved session conversion, deletion, and resume behavior for better reliability.
    • Tightened transcript redaction and display safety in the CLI and TUI.
    • Made update checks and release installs more robust with better validation and size limits.
  • Documentation

    • Expanded the README with clearer usage, security, and conversion details.

aytzey and others added 4 commits July 10, 2026 00:41
Discovery now scans providers concurrently and parses session files
with a bounded worker pool; Claude and Codex files are read in a single
head+tail pass instead of up to three full scans. On a real store with
716 sessions listing drops from ~14s to ~1.4s with a byte-identical ID
set.

Conversions previously ran transcript text through the preview cleaner,
which folds all whitespace — flattening code blocks and newlines. Text
handling is now split three ways: cleanTranscriptText preserves layout
(and secret values) for native conversion, cleanPreviewText folds and
redacts for the picker, and RedactSecrets covers credential patterns
(passwords, API keys, bearer/JWT, PEM blocks, known token shapes)
without touching layout.

Also: converted files are written 0600 (dirs 0700) with a parent-dir
fsync after rename; deterministic sort tiebreakers; deletes now clean
Claude sessions-index.json entries (best-effort — a corrupt index never
blocks a delete) and jcode .bak/.journal sidecars; discovery output is
stripped of ANSI/OSC and bidi controls at the parsing boundary.

Based on the 2026-07-09 codex audit; review adjusted the Claude index
cleanup from fail-closed to best-effort.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
get_transcript now redacts credential-like values before returning text
to a model and labels every transcript as untrusted historical data; a
tool call cannot bypass this. Users who need verbatim values opt in by
starting the server with --allow-secrets. --read-only registers only
the non-writing tools. max_turns gains a hard cap of 500. The updater
gets response-size limits, an HTTP timeout, extraction size caps, and
managed-install detection that points brew/go installs at the right
upgrade command instead of overwriting the binary in place.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Session metadata and previews pass through SafeDisplayText before
hitting the terminal, closing ANSI/OSC escape and bidi-override
injection via crafted session content. Delete becomes an async command
behind the existing busy guard, matching branch/convert.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CI enforces 80% total / 75% per-package coverage with -shuffle,
actionlint, shellcheck and govulncheck, and adds linux/arm64 to the
cross-compile matrix. Releases verify the tag format and that the
tagged commit is an ancestor of main before building. Adds
CONTRIBUTING, SECURITY, CODE_OF_CONDUCT, CODEOWNERS, issue/PR templates
and Dependabot config; README documents the trust model for MCP
transcripts and the new flags, and the real-CLI e2e script grows
opencode/gemini seeding with quota-aware skips.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds repository governance and CI controls, hardens session discovery, redaction, storage, deletion, MCP access, update installation, and terminal rendering, and expands unit and end-to-end validation across providers and platforms.

Changes

Repository and delivery controls

Layer / File(s) Summary
Repository governance and documentation
.github/ISSUE_TEMPLATE/*, CODE_OF_CONDUCT.md, CONTRIBUTING.md, README.md, SECURITY.md, go.mod
Adds contribution, security, issue, PR, and ownership policies; documents MCP and conversion behavior; updates the Go toolchain requirement.
CI and release verification
.github/workflows/*
Adds linting, coverage thresholds, race tests, vulnerability scans, release verification, restricted permissions, and Linux ARM64 compilation.

Runtime hardening

Layer / File(s) Summary
Session discovery and text sanitization
internal/session/session.go, internal/session/{claude,codex,gemini,opencode,jcode}.go, internal/session/handoff.go
Adds bounded concurrent discovery, deterministic sorting, expanded secret detection, preview/transcript sanitization, and provider parser updates.
Provider storage and subprocess safety
internal/session/*
Hardens deletion cleanup, private file permissions, learnings-directory isolation and migration, provider selection, subprocess timeouts, and captured output limits.
MCP security controls and transcript contract
internal/mcpserver/*, cmd/showagent/main.go, README.md
Adds --read-only and --allow-secrets, conditional MCP tools, transcript redaction metadata, and hard turn limits.

Installation and interfaces

Layer / File(s) Summary
Bounded update installation
cmd/showagent/update.go, cmd/showagent/replace_*.go, scripts/install.sh
Limits metadata, downloads, archives, and binaries; adds atomic replacement paths, managed-install detection, and semantic version handling.
CLI output and argument behavior
cmd/showagent/main.go, cmd/showagent/main_test.go
Updates MCP parsing and help output, sanitizes displayed metadata, and expands argument, setup, conversion, and version tests.
TUI deletion and width-safe rendering
internal/tui/*
Makes deletion asynchronous, tracks busy states, sanitizes table/detail values, and clamps views for narrow terminals.
Real provider end-to-end validation
scripts/e2e-real.sh, internal/session/e2e_real_test.go
Adds JCode coverage, resilient provider seeding, provider-specific artifact checks, and deletion verification.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • aytzey/showagent#4: Directly overlaps the update, conversion-preview, resume-recipe, and CI-related flows changed here.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.93% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is related to the PR’s main themes of hardening and discovery performance, though it is a bit broad and buzzword-heavy.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch audit/hardening-20260709

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (2)
.github/pull_request_template.md (1)

5-10: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add the vulnerability scan to the checklist.

CI now gates on vulnerability scanning, but the template only asks for test/vet/lint. Adding that step here keeps the PR checklist aligned with the new hardening gates.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/pull_request_template.md around lines 5 - 10, The PR checklist under
the Verification section is missing the new vulnerability scan gate, so update
the checklist in the pull request template to include the vulnerability scanning
step alongside `go test`, `go vet`, and `golangci-lint`. Keep the existing
checklist structure in `.github/pull_request_template.md` and add the scan item
so the template reflects the current CI hardening requirements.
internal/tui/tui.go (1)

1163-1175: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use th.detail.GetFrameSize() instead of hardcoding 2 for innerWidth.

compoundView computes innerWidth := max(1, boxWidth-2), assuming the detail style's frame is exactly 2 cells (1-cell border per side). detailView (line 949) correctly queries th.detail.GetFrameSize(). If the style ever adds padding, compoundView would truncate lines to a width wider than the actual text area, causing lipgloss to further clip content unnecessarily.

♻️ Proposed fix for consistent frame size calculation
 	boxWidth := 64
 	if m.width > 0 {
 		boxWidth = max(1, min(m.width, 64))
 	}
-	innerWidth := max(1, boxWidth-2)
+	frameW, _ := th.detail.GetFrameSize()
+	innerWidth := max(1, boxWidth-frameW)
 	for index := range lines {
 		lines[index] = truncateCells(lines[index], innerWidth)
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/tui/tui.go` around lines 1163 - 1175, The inner width calculation in
compoundView is hardcoding the detail frame size, so update it to use
th.detail.GetFrameSize() just like detailView does. Locate the compoundView
rendering logic and replace the fixed subtraction in innerWidth with the actual
frame size from th.detail so truncation matches the rendered content area even
if the style changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 34-47: The per-package coverage enforcement in the CI workflow is
only checking `go test` output lines matched by the `sed` pipeline, so packages
with no test files are skipped and can bypass the 75% floor. Update the coverage
check in the workflow step that uses `go list ./...`, `go test`, and the
`sed`/`awk` loop so it explicitly compares all listed packages against the
packages present in `test-output.txt` and fails if any package is missing a
coverage entry. Ensure the logic still reports coverage per package and uses the
existing `coverage.out`/`test-output.txt` flow.

In `@cmd/showagent/main.go`:
- Line 412: The command rendering in the showagent output is over-sanitizing
`recipe.CommandString` by passing it through `session.SafeDisplayText`, which
can rewrite copy-pasteable shell commands when quoted arguments contain spaces.
Update the `fmt.Fprintf` call in `main.go` to use a control-stripping-only
display path for `recipe.CommandString` instead of `SafeDisplayText`, and keep
the existing surrounding command formatting unchanged.

In `@cmd/showagent/update.go`:
- Line 36: The shared httpClient in update.go is using a blanket 30s Timeout
that can cancel long archive/body downloads in installRelease and downloadBytes
even when the request context allows more time. Remove the client-wide Timeout
from the shared client and let the existing context control the install path; if
needed, move any safety limits to transport-level settings instead. Use the
httpClient variable and the downloadBytes/installRelease flow as the places to
update.

In `@internal/session/jcode.go`:
- Around line 57-69: The deletion logic in the session cleanup path does not
match the current comment, since the loop in the jcode removal flow still
attempts to delete row.File even after sidecar failures while only aggregating
errors. Update the comment near the removal sequence to describe the actual
best-effort behavior in this path: it removes all known session artifacts, skips
missing files, and collects non-NotExist failures for reporting rather than
implying the canonical session is preserved on partial failure. Use the
identifiers base, row.File, removed, and failures to locate the block.

In `@README.md`:
- Around line 36-37: The README’s “100% local” claim overstates the behavior
given the updater/setup sections still imply networked activity; revise the
marketing copy to qualify it as “sessions stay local by default” or similar.
Update the wording in the main feature blurb and any matching phrasing elsewhere
in README so it stays consistent with the implementation.

---

Nitpick comments:
In @.github/pull_request_template.md:
- Around line 5-10: The PR checklist under the Verification section is missing
the new vulnerability scan gate, so update the checklist in the pull request
template to include the vulnerability scanning step alongside `go test`, `go
vet`, and `golangci-lint`. Keep the existing checklist structure in
`.github/pull_request_template.md` and add the scan item so the template
reflects the current CI hardening requirements.

In `@internal/tui/tui.go`:
- Around line 1163-1175: The inner width calculation in compoundView is
hardcoding the detail frame size, so update it to use th.detail.GetFrameSize()
just like detailView does. Locate the compoundView rendering logic and replace
the fixed subtraction in innerWidth with the actual frame size from th.detail so
truncation matches the rendered content area even if the style changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 593ba566-90b3-46ee-aa6c-2a2c2f38b328

📥 Commits

Reviewing files that changed from the base of the PR and between b775954 and bb069e8.

📒 Files selected for processing (42)
  • .github/CODEOWNERS
  • .github/ISSUE_TEMPLATE/bug_report.yml
  • .github/ISSUE_TEMPLATE/config.yml
  • .github/ISSUE_TEMPLATE/feature_request.yml
  • .github/dependabot.yml
  • .github/pull_request_template.md
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • CODE_OF_CONDUCT.md
  • CONTRIBUTING.md
  • README.md
  • SECURITY.md
  • cmd/showagent/main.go
  • cmd/showagent/main_test.go
  • cmd/showagent/replace_unix.go
  • cmd/showagent/replace_windows.go
  • cmd/showagent/update.go
  • cmd/showagent/update_test.go
  • demo/fixtures/gen.sh
  • go.mod
  • internal/mcpserver/server.go
  • internal/mcpserver/server_test.go
  • internal/session/claude.go
  • internal/session/codex.go
  • internal/session/compound.go
  • internal/session/compound_plugin.go
  • internal/session/compound_plugin_test.go
  • internal/session/e2e_real_test.go
  • internal/session/gemini.go
  • internal/session/handoff.go
  • internal/session/hardening_test.go
  • internal/session/jcode.go
  • internal/session/opencode.go
  • internal/session/opencode_test.go
  • internal/session/session.go
  • internal/session/session_test.go
  • internal/tui/table.go
  • internal/tui/table_test.go
  • internal/tui/tui.go
  • internal/tui/tui_test.go
  • scripts/e2e-real.sh
  • scripts/install.sh

Comment thread .github/workflows/ci.yml
Comment on lines +34 to +47
- name: Enforce coverage floors (total 80%, per package 75%)
# The per-package floor sits below the total floor on purpose: several
# packages hover just above 80%, and a floor equal to the current
# value turns any small refactor into a red build.
run: |
set -eu
total="$(go tool cover -func=coverage.out | awk '$1 == "total:" { gsub("%", "", $3); print $3 }')"
test -n "$total"
printf 'total coverage: %s%%\n' "$total"
awk -v total="$total" 'BEGIN { exit !(total + 0 >= 80) }'
sed -n 's/.*coverage: \([0-9][0-9.]*\)%.*/\1/p' test-output.txt | while IFS= read -r package_coverage; do
printf 'package coverage: %s%%\n' "$package_coverage"
awk -v coverage="$package_coverage" 'BEGIN { exit !(coverage + 0 >= 75) }'
done

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Per-package coverage floor can be silently bypassed by packages with no test files.

The sed extraction at line 44 only matches lines containing coverage: from go test output. Packages without any test files produce no coverage line, so they are silently excluded from the per-package 75% floor check. This means a package could regress to 0% coverage without failing CI.

Consider adding an explicit check for packages that appear in go list ./... but are missing from the coverage output.

🛡️ Suggested addition to detect untested packages
       - name: Enforce coverage floors (total 80%, per package 75%)
         # The per-package floor sits below the total floor on purpose: several
         # packages hover just above 80%, and a floor equal to the current
         # value turns any small refactor into a red build.
         run: |
           set -eu
           total="$(go tool cover -func=coverage.out | awk '$1 == "total:" { gsub("%", "", $3); print $3 }')"
           test -n "$total"
           printf 'total coverage: %s%%\n' "$total"
           awk -v total="$total" 'BEGIN { exit !(total + 0 >= 80) }'
+          # Fail if any package with test files is missing from coverage output
+          covered="$(sed -n 's/^\(ok\|FAIL\)\s\+\S\+.*coverage: \([0-9][0-9.]*\)%.*/\1/p' test-output.txt | sort -u)"
           sed -n 's/.*coverage: \([0-9][0-9.]*\)%.*/\1/p' test-output.txt | while IFS= read -r package_coverage; do
             printf 'package coverage: %s%%\n' "$package_coverage"
             awk -v coverage="$package_coverage" 'BEGIN { exit !(coverage + 0 >= 75) }'
           done
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 34 - 47, The per-package coverage
enforcement in the CI workflow is only checking `go test` output lines matched
by the `sed` pipeline, so packages with no test files are skipped and can bypass
the 75% floor. Update the coverage check in the workflow step that uses `go list
./...`, `go test`, and the `sed`/`awk` loop so it explicitly compares all listed
packages against the packages present in `test-output.txt` and fails if any
package is missing a coverage entry. Ensure the logic still reports coverage per
package and uses the existing `coverage.out`/`test-output.txt` flow.

Comment thread cmd/showagent/main.go
_, _ = fmt.Fprintf(w, " cwd: %s\n", emptyFallback(recipe.CWD))
_, _ = fmt.Fprintf(w, " storage: %s\n", recipe.StorageLocation)
_, _ = fmt.Fprintf(w, " session: %s\n", session.SafeDisplayText(recipe.ID))
_, _ = fmt.Fprintf(w, " command: %s\n", session.SafeDisplayText(recipe.CommandString))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect stripTerminalControls and how CommandString is constructed.
rg -nP -C3 'func stripTerminalControls' internal/session
rg -nP -C3 'CommandString' internal/session

Repository: aytzey/showagent

Length of output: 2297


🏁 Script executed:

#!/bin/bash
sed -n '330,380p' internal/session/session.go
printf '\n----\n'
sed -n '1,130p' internal/session/recipe.go
printf '\n----\n'
sed -n '390,430p' cmd/showagent/main.go

Repository: aytzey/showagent

Length of output: 7345


🏁 Script executed:

#!/bin/bash
sed -n '380,470p' internal/session/recipe.go
printf '\n----\n'
sed -n '1,220p' internal/session/session.go
printf '\n----\n'
rg -nP -C2 'SafeDisplayText\(' internal/session

Repository: aytzey/showagent

Length of output: 6745


🏁 Script executed:

#!/bin/bash
sed -n '380,520p' internal/session/recipe.go
printf '\n----\n'
sed -n '300,330p' internal/session/session.go
printf '\n----\n'
sed -n '1,140p' internal/session/session_test.go

Repository: aytzey/showagent

Length of output: 8372


🏁 Script executed:

#!/bin/bash
ast-grep outline internal/session/recipe.go --view expanded
printf '\n----\n'
rg -n -C3 'func ShellCommand|ResumeArgs|shell' internal/session

Repository: aytzey/showagent

Length of output: 9566


recipe.CommandString should skip SafeDisplayText.
SafeDisplayText folds whitespace, so a copy-pasteable shell command can be rewritten into a different command when an argument contains spaces inside quotes. Use a control-stripping-only path here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/showagent/main.go` at line 412, The command rendering in the showagent
output is over-sanitizing `recipe.CommandString` by passing it through
`session.SafeDisplayText`, which can rewrite copy-pasteable shell commands when
quoted arguments contain spaces. Update the `fmt.Fprintf` call in `main.go` to
use a control-stripping-only display path for `recipe.CommandString` instead of
`SafeDisplayText`, and keep the existing surrounding command formatting
unchanged.

Comment thread cmd/showagent/update.go
latestReleaseURL = defaultLatestReleaseURL
releaseBaseURL = defaultReleaseBaseURL
httpClient = http.DefaultClient
httpClient = &http.Client{Timeout: 30 * time.Second}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant file and inspect the surrounding functions.
ast-grep outline cmd/showagent/update.go --view expanded || true
echo '---'
wc -l cmd/showagent/update.go
echo '---'
sed -n '1,240p' cmd/showagent/update.go
echo '---'
sed -n '240,520p' cmd/showagent/update.go
echo '---'

# Find where httpClient is used and where contexts are created/passed.
rg -n "httpClient|fetchLatestRelease|downloadBytes|installRelease|context.WithTimeout|WithTimeout\\(" cmd/showagent -S

Repository: aytzey/showagent

Length of output: 19381


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect any scripts/install.sh mention from the comment, if present.
fd -a 'install.sh|update.go' . || true
echo '---'
rg -n "max-time|retry|curl|wget|install.sh" -S . || true

Repository: aytzey/showagent

Length of output: 1901


30s client timeout can abort archive downloads http.Client.Timeout caps the entire body read, so the shared client will still fail downloadBytes during installRelease after 30s even though that path already uses a 5-minute context. Drop the blanket client timeout and let the request context bound the install path; add transport-level connect/TLS/header timeouts only if needed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/showagent/update.go` at line 36, The shared httpClient in update.go is
using a blanket 30s Timeout that can cancel long archive/body downloads in
installRelease and downloadBytes even when the request context allows more time.
Remove the client-wide Timeout from the shared client and let the existing
context control the install path; if needed, move any safety limits to
transport-level settings instead. Use the httpClient variable and the
downloadBytes/installRelease flow as the places to update.

Comment thread internal/session/jcode.go
Comment on lines +57 to +69
// Remove derivative copies first and the canonical session last. If a
// sidecar cannot be removed, the main session remains discoverable instead
// of leaving the picker with a stale row after a partial delete.
paths := []string{base + ".bak", base + ".journal.jsonl", row.File}
removed := false
var failures []error
for _, path := range paths {
if err := os.Remove(path); err == nil {
removed = true
} else if !errors.Is(err, os.ErrNotExist) {
failures = append(failures, fmt.Errorf("remove %s: %w", path, err))
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Delete comment doesn't match implementation.

The comment states "If a sidecar cannot be removed, the main session remains discoverable," but the loop continues to remove row.File even when a sidecar removal fails with a non-NotExist error. The current best-effort behavior is reasonable — it cleans up as much as possible and aggregates failures — but the comment misleads future readers about the partial-delete semantics.

📝 Proposed comment fix
 	base := strings.TrimSuffix(row.File, ".json")
-	// Remove derivative copies first and the canonical session last. If a
-	// sidecar cannot be removed, the main session remains discoverable instead
-	// of leaving the picker with a stale row after a partial delete.
+	// Remove derivative copies first and the canonical session last, so
+	// that a partial delete cleans up as many artifacts as possible. Any
+	// non-NotExist removal errors are aggregated and returned; if nothing
+	// was removed, the session is reported as not found.
 	paths := []string{base + ".bak", base + ".journal.jsonl", row.File}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Remove derivative copies first and the canonical session last. If a
// sidecar cannot be removed, the main session remains discoverable instead
// of leaving the picker with a stale row after a partial delete.
paths := []string{base + ".bak", base + ".journal.jsonl", row.File}
removed := false
var failures []error
for _, path := range paths {
if err := os.Remove(path); err == nil {
removed = true
} else if !errors.Is(err, os.ErrNotExist) {
failures = append(failures, fmt.Errorf("remove %s: %w", path, err))
}
}
// Remove derivative copies first and the canonical session last, so
// that a partial delete cleans up as many artifacts as possible. Any
// non-NotExist removal errors are aggregated and returned; if nothing
// was removed, the session is reported as not found.
paths := []string{base + ".bak", base + ".journal.jsonl", row.File}
removed := false
var failures []error
for _, path := range paths {
if err := os.Remove(path); err == nil {
removed = true
} else if !errors.Is(err, os.ErrNotExist) {
failures = append(failures, fmt.Errorf("remove %s: %w", path, err))
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/session/jcode.go` around lines 57 - 69, The deletion logic in the
session cleanup path does not match the current comment, since the loop in the
jcode removal flow still attempts to delete row.File even after sidecar failures
while only aggregating errors. Update the comment near the removal sequence to
describe the actual best-effort behavior in this path: it removes all known
session artifacts, skips missing files, and collects non-NotExist failures for
reporting rather than implying the canonical session is preserved on partial
failure. Use the identifiers base, row.File, removed, and failures to locate the
block.

Comment thread README.md
Comment on lines +36 to +37
- **100% local** — one static binary that reads your own files. No hosted
service, no telemetry, no account.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Qualify the “100% local” claim.

The later updater/setup sections still describe networked behavior, so this reads stronger than the implementation actually guarantees. Consider wording it as “sessions stay local by default” instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 36 - 37, The README’s “100% local” claim overstates
the behavior given the updater/setup sections still imply networked activity;
revise the marketing copy to qualify it as “sessions stay local by default” or
similar. Update the wording in the main feature blurb and any matching phrasing
elsewhere in README so it stays consistent with the implementation.

@aytzey aytzey merged commit 6831f8b into main Jul 9, 2026
7 checks passed
@aytzey aytzey deleted the audit/hardening-20260709 branch July 9, 2026 21:55
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.

1 participant