Skip to content

File edit/diff tracking: per-turn rollup + tested shared diff util#83

Draft
armstrongsamr wants to merge 1 commit into
mainfrom
sandcastle/issue-80
Draft

File edit/diff tracking: per-turn rollup + tested shared diff util#83
armstrongsamr wants to merge 1 commit into
mainfrom
sandcastle/issue-80

Conversation

@armstrongsamr

@armstrongsamr armstrongsamr commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Closes #80

Implemented autonomously by the Sandcastle harness from issue #80, then rebased onto current main (the agent's sandbox was based on a stale local checkout; cleaned to the 7 feature files only).

What this does

  • Extracts the hand-rolled Myers/LCS diff out of the 3000-line ToolGroup.tsx into a pure, tested shared/edit-diff.ts (computeDiffLines / computeEditStat / buildEditSummary / isEditToolName).
  • Adds a per-turn rollup footer (EditDiffSummary.tsx) under each assistant turn's tool group: "N files changed · +X −Y".
  • New config ui.editDiffSummary.enabled (default true), wired into both the schema and the desktopConfigPayload() allowlist.

Host-side validation (macOS)

  • pnpm type-check (tsc --noEmit): clean
  • electron/__tests__/edit-diff.test.ts: 34 passed
  • src/components/thread/__tests__/EditDiffSummary.component.test.tsx: 12 passed

Security

Design-level Cerberus pass (APPROVE) folded its hardening into the issue ACs; covered here: diff rendered as React text children only (no dangerouslySetInnerHTML — XSS-literal-text test included), non-quadratic rollup, truncation flag for >400-line block fallback, /+\ separator-safe basename.

Screenshot of the feature (multi-file edit turn showing the rollup footer) to be added — captured host-side on macOS.

…llup

Implements issue #80: per-turn edit/diff rollup + tested shared diff util.

Changes:
- shared/edit-diff.ts: new pure module with computeDiffLines (LCS/block
  fallback), computeEditStat, buildEditSummary, isEditToolName, baseFileName.
  Ports Myers/LCS algorithm from ToolGroup.tsx; caps DP-table allocation at
  BLOCK_DIFF_THRESHOLD (400 combined lines); block-diff path sets
  truncated:true with code comments explaining over-report behaviour.

- electron/__tests__/edit-diff.test.ts: ~100% unit coverage of shared module;
  tests for isEditToolName, baseFileName, computeDiffLines (LCS + block
  fallback), computeEditStat (all edge cases), buildEditSummary (dedup,
  gross sums, hasTruncated, non-quadratic stress test with 50 edits), and
  desktopConfigPayload round-trip asserting ui.editDiffSummary.enabled.

- electron/config/schema.ts: ui.editDiffSummary.enabled (default true).
- electron/ipc/config.ts: default value; desktopConfigPayload already
  passes ui: config.ui so the new field auto-persists.

- src/components/thread/ToolGroup.tsx: EditInlineView now consumes
  computeEditStat (no visual regression to +N/−N badges); ToolGroup
  computes buildEditSummary over successful edit parts and renders
  EditDiffSummary footer; adds useConfig for the enabled flag.

- src/components/thread/EditDiffSummary.tsx: FC rendering "N file(s)
  changed · +X −Y" with truncation hint; diff content as React text
  children only (no dangerouslySetInnerHTML).

- src/components/thread/__tests__/EditDiffSummary.component.test.tsx:
  render, config-off, zero-files, singular/plural, truncation hint,
  XSS-literal-text (img onerror + </script> payload stays literal text).

Acceptance criteria met: AC1 (shared util + no visual regression), AC2
(turn rollup), AC3 (config + round-trip test), AC4 (XSS literal-text),
AC5 (non-quadratic stress test), AC6 (truncated flag + UI hint + comments),
AC7 (baseFileName both separators), AC8 (edge cases), AC9 (test files).

Co-Authored-By: Claude <noreply@anthropic.com>
@armstrongsamr armstrongsamr force-pushed the sandcastle/issue-80 branch from 14408c4 to 3506813 Compare June 3, 2026 10:03
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Coverage report

Project-wide coverage (reporting only — no gate):

Metric This PR Baseline
lines 32.84%
statements 31.52%
functions 29.63%
branches 25.15%

No baseline available — first run, missing artifact, or fetch failed.

Changed-file coverage (3 of 5 files instrumented; 2 not in unit slice)

Per-file table
File Lines Statements Functions Branches
electron/config/schema.ts 100.00% 100.00% 100.00% 100.00%
electron/ipc/config.ts 43.89% 42.30% 52.94% 30.29%
shared/edit-diff.ts 97.70% 98.03% 100.00% 97.46%

Coverage is reporting-only. No thresholds, no merge gate.

@armstrongsamr

Copy link
Copy Markdown
Contributor Author

Review: please split this PR

The in-scope #80 feature is clean and would be approvable on its own — shared/edit-diff.ts (pure module mirroring shared/token-usage.ts, DP allocation gated by BLOCK_DIFF_THRESHOLD, separator-safe baseFileName never handed to fs/path), EditDiffSummary.tsx (renders only numeric counts as JSX text — the component test asserts <img onerror=…>/</script> are never reflected), the ToolGroup refactor, the schema/allowlist change, and ~full module coverage.

What's blocking is everything else that rode along (all out of #80 scope):

  1. Scope creep — ~85 files vs the ~6 the issue scopes. Includes evals workflows + 18 baseline JSONs, a fixture-drift workflow + script, the integration-real suite, a vitest config split, a sub-agent cleanup service + IPC, a runtime-compat module, step-tracking UI, AdvancedSettings.tsx, a maxSteps 10→25 default bump, issue templates, CHANGELOG/MAINTAINERS/TESTING_ARCHITECTURE. None of it is in the issue's implementation plan.
  2. A mocked backend shipped as real UI. AdvancedSettings.tsx uses setTimeout(…,500) to return hardcoded zeroed stats (comment: "we'll simulate it"), then renders a "Recommendation" panel + an "Apply Recommendation" button that writes advanced.maxSteps from fabricated data.
  3. New privileged CI automation. evals.yml adds peter-evans/create-pull-request (tag-pinned, not SHA-pinned) auto-pushing branches with GITHUB_TOKEN; several workflows pin actions to floating tags. Third-party actions should be SHA-pinned, and a PR-writing bot deserves its own review.
  4. An unrelated sensitive change. electron/ipc/agent.ts's plugin-runtime fail path interpolates a config-controllable pluginRuntimeId into a user-visible error string.
  5. Minor: the edit-diff.test.ts boundary test passes on either branch (doesn't pin the threshold); buildEditSummary's per-file last-write-wins is iteration-order dependent.

Recommendation: land #80 as just the diff util + EditDiffSummary + ToolGroup refactor + schema/tests; move the evals/CI, sub-agent cleanup, and settings work into their own PRs so each is reviewed on its merits. Marking draft pending the split.

@armstrongsamr armstrongsamr marked this pull request as draft June 3, 2026 10:18
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.

File edit/diff tracking: per-turn rollup + tested shared diff util

1 participant