Commit Graph

3 Commits

Author SHA1 Message Date
Alpamys 76dfbb363e test(adapters): mock os.environ.get for null-byte/CRLF env tests (CI fix)
POSIX setenv (and Windows equivalent) reject null bytes + control chars
at the syscall boundary, so monkeypatch.setenv("SOUP_BRANCHES_DIR",
"/some\x00path") raises ValueError on every CI runner before our code
ever sees the env var.

Stub os.environ.get directly so the helper's rejection branch is
exercised exactly as it would be if the env var arrived through some
other channel (subprocess env inheritance, in-process programmatic
mutation, etc).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 19:00:04 +05:00
Alpamys 8577bc2800 test(adapters): strip ANSI before help-output substring asserts (v0.57.0 CI fix)
Rich-wrap-CI workaround — same fix pattern as v0.55.0 / v0.56.0:
CliRunner output contains ANSI color escapes that break literal
'--top-k' in output substring matches because Rich renders option
names as -\x1b[0m\x1b[1;36m-top-k.

Adds _ANSI_RE + _strip_ansi() helper to each of the 4 test files
(test_v0570_part_{a,b,c,d}.py) and routes every help-output
substring assertion through it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 18:50:52 +05:00
Alpamys 7da82d355a feat(adapters): v0.57.0 — git for LoRA (diff, merge, blame, branch)
Ships "soup adapters {diff,merge,blame,branch,checkout,branches}" — git-shaped
UX for LoRA adapter management. Pure-numpy math (no torch); TOCTOU defence on
every path; atomic writes throughout.

Part A — adapters diff (utils/adapter_diff.py, ~270 LOC):
  Per-layer ΔW Frobenius norm + relative drift; effective-rank delta via
  SVD entropy; top-K changed projections; JSON/Markdown/table output.

Part B — adapters merge (utils/adapter_merge.py, ~280 LOC):
  Four strategies — linear (weighted avg), ties (Yadav et al. — trim/
  elect-sign/disjoint), dare (Yu et al. — drop+rescale, deterministic),
  svd (low-rank reconstruction). MergeReport.verdict='UNKNOWN' is a stub;
  live canary verdict via v0.55 eval gate ships in v0.57.1.

Part C — adapters blame (utils/blame.py, ~190 LOC):
  Leave-one-out plan emitter + budget tracker (parse_budget mirrors v0.48.0
  data_mix idiom). Per-shard work table + feasibility check. Live ablation
  runner raises NotImplementedError v0.57.1 (mirrors v0.27.0 / v0.50.0 /
  v0.56.0 stub-then-live pattern).

Part D — adapters branch / checkout / branches (utils/adapter_branch.py,
~230 LOC):
  SHA-256 snapshot pointers under ~/.soup/branches/ (SOUP_BRANCHES_DIR
  override, $HOME/$CWD/$TMPDIR-bounded). Drift detection on checkout —
  refuses restore when source SHA != snapshot SHA. CRLF/null-byte
  rejection on env override (mirrors v0.51.0 hub-endpoint policy).

5-agent review-fix wave (1 CRITICAL + 9 HIGH + 11 MEDIUM + 4 LOW):
  - TIES tied-sign defaults to +1 (np.sign(0)==0 would silently zero all
    tied parameters)
  - load_branch / delete_branch reject symlinks via os.lstat + S_ISLNK
    before read/unlink
  - merge output safetensors + adapter_config.json atomic writes with
    symlink target rejection at output path; source config size-capped
    at 256 KB
  - compute_adapter_diff weights-file path symlink-rejected via lstat
    BEFORE is_file() (defends against .safetensors -> /etc/passwd escape)
  - _count_dataset_rows opens via realpath captured at containment check
    (closes TOCTOU window)
  - diff --output write is atomic (tempfile + os.replace)
  - SOUP_BRANCHES_DIR rejects every C0 control char, not just null
  - SUPPORTED_STRATEGIES is now frozenset (matches v0.41.0+ allowlist
    policy); STRATEGY_ORDER tuple preserved for canonical iteration
  - 5× pytest.raises(Exception) tightened to FrozenInstanceError
  - 2 zero-assertion Part D tests converted to real assertions
  - Added: bool base_model rejection, top_k boundary 1/201, density=1.0
    inclusive bound, inf weight rejection, tied-sign positive default,
    bool False for num_shards/budget_seconds, POSIX symlink rejections
    for diff weights / merge output / load_branch / delete_branch,
    no-top-level-torch source-grep guards, traversal delete_branch.

Plus v0.56.0 follow-up: test_v0560.py version-floor tests widened from
exact-match to floor-check (matches v0.51.0 / v0.54.0 idiom — every
subsequent release would otherwise edit this one line).

Test count: 8849 → 8998 (+149 net in 4 new files; 4 POSIX-only symlink
tests skipped on Windows). Full suite green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 18:37:14 +05:00