ssmforge 0.2.0__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. {ssmforge-0.2.0/src/ssmforge.egg-info → ssmforge-0.2.2}/PKG-INFO +224 -5
  2. {ssmforge-0.2.0 → ssmforge-0.2.2}/README.md +223 -4
  3. {ssmforge-0.2.0 → ssmforge-0.2.2}/pyproject.toml +1 -1
  4. {ssmforge-0.2.0 → ssmforge-0.2.2}/src/ssmforge/__init__.py +1 -1
  5. {ssmforge-0.2.0 → ssmforge-0.2.2}/src/ssmforge/analyze/__init__.py +8 -1
  6. {ssmforge-0.2.0 → ssmforge-0.2.2}/src/ssmforge/analyze/graph.py +374 -33
  7. {ssmforge-0.2.0 → ssmforge-0.2.2}/src/ssmforge/analyze/state_dict_scan.py +41 -1
  8. {ssmforge-0.2.0 → ssmforge-0.2.2}/src/ssmforge/cli.py +232 -14
  9. {ssmforge-0.2.0 → ssmforge-0.2.2/src/ssmforge.egg-info}/PKG-INFO +224 -5
  10. {ssmforge-0.2.0 → ssmforge-0.2.2}/LICENSE +0 -0
  11. {ssmforge-0.2.0 → ssmforge-0.2.2}/MANIFEST.in +0 -0
  12. {ssmforge-0.2.0 → ssmforge-0.2.2}/docs/DEPLOY.md +0 -0
  13. {ssmforge-0.2.0 → ssmforge-0.2.2}/docs/INSTALL.md +0 -0
  14. {ssmforge-0.2.0 → ssmforge-0.2.2}/docs/quickstart.md +0 -0
  15. {ssmforge-0.2.0 → ssmforge-0.2.2}/setup.cfg +0 -0
  16. {ssmforge-0.2.0 → ssmforge-0.2.2}/src/ssmforge/analyze/diff.py +0 -0
  17. {ssmforge-0.2.0 → ssmforge-0.2.2}/src/ssmforge/analyze/markdown_render.py +0 -0
  18. {ssmforge-0.2.0 → ssmforge-0.2.2}/src/ssmforge/analyze/report.py +0 -0
  19. {ssmforge-0.2.0 → ssmforge-0.2.2}/src/ssmforge/analyze/summary.py +0 -0
  20. {ssmforge-0.2.0 → ssmforge-0.2.2}/src/ssmforge.egg-info/SOURCES.txt +0 -0
  21. {ssmforge-0.2.0 → ssmforge-0.2.2}/src/ssmforge.egg-info/dependency_links.txt +0 -0
  22. {ssmforge-0.2.0 → ssmforge-0.2.2}/src/ssmforge.egg-info/entry_points.txt +0 -0
  23. {ssmforge-0.2.0 → ssmforge-0.2.2}/src/ssmforge.egg-info/requires.txt +0 -0
  24. {ssmforge-0.2.0 → ssmforge-0.2.2}/src/ssmforge.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ssmforge
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Standalone HF architecture analyzer (`ssmforge arch`). Inspects any HuggingFace model and reports architectural quirks — attention biases, fused QKV/gate-up, tied embeddings, GQA, MQA, MoE, sliding window, LayerScale, soft-capping, partial RoPE, MLP type, norm type. Outputs structured JSON + human-readable summary.
5
5
  Author: SSMForge Contributors
6
6
  License-Expression: Apache-2.0
@@ -82,6 +82,28 @@ visible *before* you spend the next hour finding out the hard way.
82
82
  It does **not** modify the model. It does **not** run inference. It only
83
83
  inspects.
84
84
 
85
+ ### What's new in 0.2.2
86
+
87
+ | Fix | What was broken |
88
+ |-----|-----------------|
89
+ | `--output PATH` with bad parent dir | Previously got a generic `bash: Permission denied` (when redirecting) or a Python traceback (when using `--output`). Now pre-flight checks the parent directory, prints a clear hint pointing at writable locations like `./` and `~/`, then exits 1. |
90
+
91
+ Plus 6 new tests (162 total). Tiny release — main bug fix only.
92
+
93
+ ### What's new in 0.2.1
94
+
95
+ | Feature | Why it matters |
96
+ |---------|----------------|
97
+ | `doctor --check-install` | Diagnoses "ssmforge: command not found" in one command. Prints `✓` when on PATH, `✗` + a copy-pasteable fix hint when not. Exits non-zero so you can use it in scripts. |
98
+ | One-time PATH warning | First invocation when `ssmforge` is off-PATH prints a hint to stderr pointing at `python -m ssmforge.cli`. Suppressed by `SSMFORGE_NO_PATH_WARN=1`. Skipped for `doctor` so its output stays clean. |
99
+ | `--graph --format {text,json,markdown}` | `--graph` now respects `--format`. Single + compare modes emit ASCII (text), structured JSON (for pipelines), or GitHub-flavored Markdown (for docs/Issues). |
100
+ | `--graph` ASCII cleanup | Box-drawing hierarchy fixed: single-rail `▼` flow + box `╭╮╰╯` panels for ATTENTION and MLP, `├─/└─` decision-tree style inside. |
101
+ | Qwen2 attention_bias fixed | `config.attention_bias=None` + zero bias tensors (Qwen2's vestigial biases) no longer reports `attention_bias=True`. Now requires either config explicit True OR non-zero bias values. |
102
+ | `--compare A A` is now valid | Was previously dedup'd to `[A]` and rejected. Now produces a 2-way "all identical" report. |
103
+ | README install lead | Section 4.2 now recommends a venv more strongly + explains why + gives `python -m ssmforge.cli` as the universal workaround. |
104
+
105
+ Plus 26 new tests (156 total).
106
+
85
107
  ### What's new in 0.2.0
86
108
 
87
109
  | Feature | Why it matters |
@@ -252,7 +274,13 @@ If you don't have it:
252
274
  - macOS: `brew install python@3.12`
253
275
  - Windows: download from https://www.python.org/downloads/
254
276
 
255
- ### 4.2 Create a virtual environment (recommended)
277
+ ### 4.2 Create a virtual environment (recommended — strongly)
278
+
279
+ **Use a venv.** It sidesteps the most common install problem on Windows
280
+ (`ssmforge: command not found` after pip install). With a venv, the
281
+ `ssmforge` script lands at `.venv\Scripts\ssmforge.exe` (Windows) or
282
+ `.venv/bin/ssmforge` (Linux/macOS), which is automatically on PATH after
283
+ you activate.
256
284
 
257
285
  A venv keeps ssmforge and its dependencies isolated from system Python.
258
286
 
@@ -273,6 +301,15 @@ python -m venv .venv
273
301
  You should see `(.venv)` in your prompt after activating. From here on,
274
302
  `pip install` only affects this venv.
275
303
 
304
+ **Alternative: use `python -m ssmforge.cli`** as a workaround without a
305
+ venv. Works anywhere, doesn't need any PATH setup, but requires typing
306
+ `python -m ssmforge.cli` instead of just `ssmforge`.
307
+
308
+ ```bash
309
+ python -m ssmforge.cli --version
310
+ python -m ssmforge.cli arch hf-internal-testing/tiny-random-LlamaForCausalLM --dry-run
311
+ ```
312
+
276
313
  ### 4.3 Install ssmforge
277
314
 
278
315
  ```bash
@@ -416,6 +453,52 @@ JSON variant:
416
453
  ssmforge doctor --format json
417
454
  ```
418
455
 
456
+ #### `ssmforge doctor --check-install`
457
+
458
+ Run install checks (PATH, scripts location) and exit non-zero on issues.
459
+ Useful in scripts or when troubleshooting "command not found":
460
+
461
+ ```bash
462
+ ssmforge doctor --check-install
463
+ ```
464
+
465
+ ```
466
+ ssmforge doctor --check-install (0.2.1)
467
+ --------------------------------------------------
468
+ ✓ ssmforge command on PATH: True
469
+ python: C:\Users\netge\.venv\Scripts\python.exe
470
+ platform: win32
471
+ ```
472
+
473
+ If `ssmforge` isn't on PATH, you get a fix-hint block:
474
+
475
+ ```
476
+ ssmforge doctor --check-install (0.2.1)
477
+ --------------------------------------------------
478
+ ✗ ssmforge command on PATH: False
479
+ python: C:\Python314\python.exe
480
+ platform: win32
481
+
482
+ The 'ssmforge' script was installed to:
483
+ C:\Users\netge\AppData\Roaming\Python\Python314\Scripts\ssmforge.exe
484
+ but that folder is not on PATH.
485
+
486
+ Quick fix (current shell):
487
+ export PATH="/c/Users/netge/AppData/Roaming/Python/Python314/Scripts:$PATH"
488
+
489
+ Permanent fix (PowerShell, restart shell after):
490
+ [Environment]::SetEnvironmentVariable("PATH", "<scripts>;" + [Environment]::GetEnvironmentVariable("PATH", "User"), "User")
491
+
492
+ Or just use: python -m ssmforge.cli
493
+ ```
494
+
495
+ JSON variant:
496
+
497
+ ```bash
498
+ ssmforge doctor --check-install --format json
499
+ # {"ssmforge_command_on_path": false, "fix_hint": "...", ...}
500
+ ```
501
+
419
502
  ### `ssmforge arch MODEL`
420
503
 
421
504
  The main command. Inspect a HuggingFace model.
@@ -436,7 +519,7 @@ ssmforge arch <model_id_or_path> [options]
436
519
  | `--fields name1,name2` | | Subset output to comma-separated field names. Dotted paths supported (e.g. `quirks.attention_bias`) | all |
437
520
  | `--profile` | | Emit only the profile section (family, attention_type, mlp_type, norm_type, descriptors) | full report |
438
521
  | `--only-different` | | In `--compare` mode, hide the "Identical across all models" section | show identical |
439
- | `--graph` | | Render a text-based decision-graph in your terminal. Single-model shows the full pipeline with decisions inline. `--compare --graph` shows an N-way decision table. | JSON |
522
+ | `--graph` | | Render a text-based decision-graph in your terminal. Single-model shows the full pipeline with decisions inline. `--compare --graph` shows an N-way decision table. Honors `--format` (`text`, `json`, `markdown`/`md`) for JSON pipeline consumption and GFM-friendly docs. | JSON |
440
523
 
441
524
  **Examples:**
442
525
 
@@ -1114,7 +1197,7 @@ use a newer Python.
1114
1197
  **`pip install ssmforge` succeeds but `ssmforge` command not found**
1115
1198
 
1116
1199
  The install put the script in a directory not on your PATH. Activate
1117
- your venv (see [4.2](#42-create-a-virtual-environment-recommended)) or
1200
+ your venv (see [4.2](#42-create-a-virtual-environment-recommended-strongly)) or
1118
1201
  check `pip show ssmforge` for the install location.
1119
1202
 
1120
1203
  **`ssmforge: command not found` after `pip install` succeeds (Windows)**
@@ -1317,7 +1400,143 @@ install.
1317
1400
 
1318
1401
  ## 16. Update log
1319
1402
 
1320
- ### v0.2.0 (current) — 2026-09-22
1403
+ ### v0.2.2 (current) — 2026-09-23
1404
+
1405
+ **Bug fix: `--output PATH` directory validation**
1406
+
1407
+ When `--output PATH` points to a non-writable location, users were getting
1408
+ either a generic shell `Permission denied` (when using shell redirection)
1409
+ or an opaque Python `OSError` (when using `--output` directly). Now
1410
+ ssmforge pre-flight checks the parent directory and prints a friendly
1411
+ hint pointing at writable alternatives.
1412
+
1413
+ **Before:**
1414
+ ```
1415
+ $ python -m ssmforge.cli arch X --graph --format markdown > q.md
1416
+ bash: q.md: Permission denied
1417
+
1418
+ $ python -m ssmforge.cli arch X --graph --format markdown --output C:\q.md
1419
+ Error: cannot write to 'C:\\q.md': [Errno 13] Permission denied: 'C:\\q.md'
1420
+ ```
1421
+
1422
+ **After:**
1423
+ ```
1424
+ $ python -m ssmforge.cli arch X --graph --format markdown > q.md
1425
+ # (still works in any shell that allows the redirect)
1426
+
1427
+ $ python -m ssmforge.cli arch X --graph --format markdown --output C:\q.md
1428
+ Error: cannot write to directory: C:\
1429
+ Permission denied.
1430
+ Try a writable location like:
1431
+ --output ./report.md (current directory)
1432
+ --output ~/report.md (your home directory)
1433
+ --output $TMPDIR/report.md (system temp)
1434
+ ```
1435
+
1436
+ Tests: 162 passed (was 156). 6 new tests for `_check_output_path`
1437
+ (returns (None, None) for None / '-' paths, validates writability,
1438
+ emits helpful hints for missing or read-only parents).
1439
+
1440
+ ### v0.2.1 — 2026-09-23
1441
+
1442
+ **New feature: `doctor --check-install`**
1443
+
1444
+ Run install checks (PATH, scripts location, platform) and exit non-zero
1445
+ when something is wrong. Use in scripts or when troubleshooting:
1446
+
1447
+ ```bash
1448
+ ssmforge doctor --check-install
1449
+ # prints ✓ or ✗ + fix hint
1450
+ echo $? # 0 = good, 1 = something off
1451
+ ```
1452
+
1453
+ The fix-hint output is a copy-pasteable shell command — exactly the
1454
+ right `export PATH=...` or `[Environment]::SetEnvironmentVariable(...)`
1455
+ line for your platform.
1456
+
1457
+ **New: one-time PATH warning on stderr**
1458
+
1459
+ When ssmforge detects it's been installed off-PATH, every `ssmforge
1460
+ arch ...` call prints a one-time hint to stderr:
1461
+
1462
+ ```
1463
+ Note: 'ssmforge' is not on your PATH for `python -m ssmforge.cli` users.
1464
+ This is normal if you're running via 'python -m ssmforge.cli'.
1465
+ If 'ssmforge --version' fails, see: https://github.com/lordxmen2k/SSMForge#troubleshooting
1466
+ ```
1467
+
1468
+ This is suppressed:
1469
+ - when ssmforge IS on PATH
1470
+ - when the user explicitly runs `ssmforge doctor ...`
1471
+ - when `SSMFORGE_NO_PATH_WARN=1` is set in the env
1472
+
1473
+ **README install lead**
1474
+
1475
+ Section 4.2 (Create a virtual environment) now leads with a stronger
1476
+ recommendation and a fallback path (`python -m ssmforge.cli`). No
1477
+ changes to the install command itself — just clearer context.
1478
+
1479
+ **Bug fix: Qwen2 attention_bias false positive**
1480
+
1481
+ Previously, `scan_state_dict` set `attention_bias=True` whenever
1482
+ `*self_attn.*.bias` tensors existed in the state dict. Qwen2 ships
1483
+ zero-initialized bias tensors (vestigial, never used) and was
1484
+ incorrectly reported as having attention biases.
1485
+
1486
+ Now: `attention_bias` is set to True only when **either** the config
1487
+ explicitly says `attention_bias=True` **or** the bias tensors contain
1488
+ non-zero values. The `bias_keys_found` list still records that the
1489
+ tensors exist for transparency.
1490
+
1491
+ Tests:
1492
+ - `test_scan_detects_attention_bias` (updated) — zero biases → False
1493
+ - `test_scan_detects_nonzero_attention_bias` (new) — non-zero → True
1494
+ - `test_reports_attention_bias_from_state_dict_when_config_missing`
1495
+ (updated) — config.attention_bias=None + zero biases → False
1496
+
1497
+ **Bug fix: `--compare A A` no longer rejected**
1498
+
1499
+ Previously `--compare A A` was dedup'd to `[A]` and rejected with
1500
+ "requires at least 2 models". Now produces a valid 2-way "all identical"
1501
+ report.
1502
+
1503
+ **Feature: `--graph` honors `--format {text,json,markdown}`**
1504
+
1505
+ Three output modes for `--graph`:
1506
+ - `--format text` (default for `--graph`) — ASCII with box-drawing
1507
+ - `--format json` — structured pipeline + decisions for piping into
1508
+ other tools
1509
+ - `--format markdown`/`md` — GitHub-flavored Markdown tables for
1510
+ embedding in docs, Issues, PRs
1511
+
1512
+ All three work for both single-model and `--compare --graph` modes.
1513
+
1514
+ **Bug fix: `--graph` ASCII hierarchy**
1515
+
1516
+ The previous box-drawing had two parallel rails (`├─` and `▼`) that
1517
+ suggested two unrelated flows. Replaced with single-rail `▼` flow and
1518
+ `╭╮╰╯` panels for ATTENTION and MLP blocks. Decision-tree style
1519
+ `├─/└─` inside each panel. Renders correctly in any terminal.
1520
+
1521
+ **Tests: 156 passed** (was 142). 14 new tests:
1522
+ - 12 from `test_v021_check_install.py` (PATH detection, doctor
1523
+ --check-install, main() integration)
1524
+ - 1 from `test_v017_compare.py` (`--compare A A` regression)
1525
+ - 1 from `test_analyze.py` (nonzero attention_bias detection)
1526
+ - 13 from `test_graph.py` (JSON/Markdown formatters, GFM
1527
+ compatibility, edge cases)
1528
+ - (some overlap with existing test updates for attention_bias fix)
1529
+
1530
+ Tests: 142 passed (was 130). 12 new tests:
1531
+ - `_check_ssmforge_on_path`: returns correct tuple, finds off-path
1532
+ install, returns no fix when on PATH
1533
+ - `_warn_path_once`: writes to stderr when off-path, respects env
1534
+ var, skips when on PATH
1535
+ - `main()` integration: suppresses warning for doctor, emits for arch
1536
+ - `doctor --check-install`: text output on/off PATH, JSON output,
1537
+ exit codes (0 / 1), back-compat with default `doctor`
1538
+
1539
+ ### v0.2.0 — 2026-09-22
1321
1540
 
1322
1541
  **New feature: `--graph`**
1323
1542
 
@@ -59,6 +59,28 @@ visible *before* you spend the next hour finding out the hard way.
59
59
  It does **not** modify the model. It does **not** run inference. It only
60
60
  inspects.
61
61
 
62
+ ### What's new in 0.2.2
63
+
64
+ | Fix | What was broken |
65
+ |-----|-----------------|
66
+ | `--output PATH` with bad parent dir | Previously got a generic `bash: Permission denied` (when redirecting) or a Python traceback (when using `--output`). Now pre-flight checks the parent directory, prints a clear hint pointing at writable locations like `./` and `~/`, then exits 1. |
67
+
68
+ Plus 6 new tests (162 total). Tiny release — main bug fix only.
69
+
70
+ ### What's new in 0.2.1
71
+
72
+ | Feature | Why it matters |
73
+ |---------|----------------|
74
+ | `doctor --check-install` | Diagnoses "ssmforge: command not found" in one command. Prints `✓` when on PATH, `✗` + a copy-pasteable fix hint when not. Exits non-zero so you can use it in scripts. |
75
+ | One-time PATH warning | First invocation when `ssmforge` is off-PATH prints a hint to stderr pointing at `python -m ssmforge.cli`. Suppressed by `SSMFORGE_NO_PATH_WARN=1`. Skipped for `doctor` so its output stays clean. |
76
+ | `--graph --format {text,json,markdown}` | `--graph` now respects `--format`. Single + compare modes emit ASCII (text), structured JSON (for pipelines), or GitHub-flavored Markdown (for docs/Issues). |
77
+ | `--graph` ASCII cleanup | Box-drawing hierarchy fixed: single-rail `▼` flow + box `╭╮╰╯` panels for ATTENTION and MLP, `├─/└─` decision-tree style inside. |
78
+ | Qwen2 attention_bias fixed | `config.attention_bias=None` + zero bias tensors (Qwen2's vestigial biases) no longer reports `attention_bias=True`. Now requires either config explicit True OR non-zero bias values. |
79
+ | `--compare A A` is now valid | Was previously dedup'd to `[A]` and rejected. Now produces a 2-way "all identical" report. |
80
+ | README install lead | Section 4.2 now recommends a venv more strongly + explains why + gives `python -m ssmforge.cli` as the universal workaround. |
81
+
82
+ Plus 26 new tests (156 total).
83
+
62
84
  ### What's new in 0.2.0
63
85
 
64
86
  | Feature | Why it matters |
@@ -229,7 +251,13 @@ If you don't have it:
229
251
  - macOS: `brew install python@3.12`
230
252
  - Windows: download from https://www.python.org/downloads/
231
253
 
232
- ### 4.2 Create a virtual environment (recommended)
254
+ ### 4.2 Create a virtual environment (recommended — strongly)
255
+
256
+ **Use a venv.** It sidesteps the most common install problem on Windows
257
+ (`ssmforge: command not found` after pip install). With a venv, the
258
+ `ssmforge` script lands at `.venv\Scripts\ssmforge.exe` (Windows) or
259
+ `.venv/bin/ssmforge` (Linux/macOS), which is automatically on PATH after
260
+ you activate.
233
261
 
234
262
  A venv keeps ssmforge and its dependencies isolated from system Python.
235
263
 
@@ -250,6 +278,15 @@ python -m venv .venv
250
278
  You should see `(.venv)` in your prompt after activating. From here on,
251
279
  `pip install` only affects this venv.
252
280
 
281
+ **Alternative: use `python -m ssmforge.cli`** as a workaround without a
282
+ venv. Works anywhere, doesn't need any PATH setup, but requires typing
283
+ `python -m ssmforge.cli` instead of just `ssmforge`.
284
+
285
+ ```bash
286
+ python -m ssmforge.cli --version
287
+ python -m ssmforge.cli arch hf-internal-testing/tiny-random-LlamaForCausalLM --dry-run
288
+ ```
289
+
253
290
  ### 4.3 Install ssmforge
254
291
 
255
292
  ```bash
@@ -393,6 +430,52 @@ JSON variant:
393
430
  ssmforge doctor --format json
394
431
  ```
395
432
 
433
+ #### `ssmforge doctor --check-install`
434
+
435
+ Run install checks (PATH, scripts location) and exit non-zero on issues.
436
+ Useful in scripts or when troubleshooting "command not found":
437
+
438
+ ```bash
439
+ ssmforge doctor --check-install
440
+ ```
441
+
442
+ ```
443
+ ssmforge doctor --check-install (0.2.1)
444
+ --------------------------------------------------
445
+ ✓ ssmforge command on PATH: True
446
+ python: C:\Users\netge\.venv\Scripts\python.exe
447
+ platform: win32
448
+ ```
449
+
450
+ If `ssmforge` isn't on PATH, you get a fix-hint block:
451
+
452
+ ```
453
+ ssmforge doctor --check-install (0.2.1)
454
+ --------------------------------------------------
455
+ ✗ ssmforge command on PATH: False
456
+ python: C:\Python314\python.exe
457
+ platform: win32
458
+
459
+ The 'ssmforge' script was installed to:
460
+ C:\Users\netge\AppData\Roaming\Python\Python314\Scripts\ssmforge.exe
461
+ but that folder is not on PATH.
462
+
463
+ Quick fix (current shell):
464
+ export PATH="/c/Users/netge/AppData/Roaming/Python/Python314/Scripts:$PATH"
465
+
466
+ Permanent fix (PowerShell, restart shell after):
467
+ [Environment]::SetEnvironmentVariable("PATH", "<scripts>;" + [Environment]::GetEnvironmentVariable("PATH", "User"), "User")
468
+
469
+ Or just use: python -m ssmforge.cli
470
+ ```
471
+
472
+ JSON variant:
473
+
474
+ ```bash
475
+ ssmforge doctor --check-install --format json
476
+ # {"ssmforge_command_on_path": false, "fix_hint": "...", ...}
477
+ ```
478
+
396
479
  ### `ssmforge arch MODEL`
397
480
 
398
481
  The main command. Inspect a HuggingFace model.
@@ -413,7 +496,7 @@ ssmforge arch <model_id_or_path> [options]
413
496
  | `--fields name1,name2` | | Subset output to comma-separated field names. Dotted paths supported (e.g. `quirks.attention_bias`) | all |
414
497
  | `--profile` | | Emit only the profile section (family, attention_type, mlp_type, norm_type, descriptors) | full report |
415
498
  | `--only-different` | | In `--compare` mode, hide the "Identical across all models" section | show identical |
416
- | `--graph` | | Render a text-based decision-graph in your terminal. Single-model shows the full pipeline with decisions inline. `--compare --graph` shows an N-way decision table. | JSON |
499
+ | `--graph` | | Render a text-based decision-graph in your terminal. Single-model shows the full pipeline with decisions inline. `--compare --graph` shows an N-way decision table. Honors `--format` (`text`, `json`, `markdown`/`md`) for JSON pipeline consumption and GFM-friendly docs. | JSON |
417
500
 
418
501
  **Examples:**
419
502
 
@@ -1091,7 +1174,7 @@ use a newer Python.
1091
1174
  **`pip install ssmforge` succeeds but `ssmforge` command not found**
1092
1175
 
1093
1176
  The install put the script in a directory not on your PATH. Activate
1094
- your venv (see [4.2](#42-create-a-virtual-environment-recommended)) or
1177
+ your venv (see [4.2](#42-create-a-virtual-environment-recommended-strongly)) or
1095
1178
  check `pip show ssmforge` for the install location.
1096
1179
 
1097
1180
  **`ssmforge: command not found` after `pip install` succeeds (Windows)**
@@ -1294,7 +1377,143 @@ install.
1294
1377
 
1295
1378
  ## 16. Update log
1296
1379
 
1297
- ### v0.2.0 (current) — 2026-09-22
1380
+ ### v0.2.2 (current) — 2026-09-23
1381
+
1382
+ **Bug fix: `--output PATH` directory validation**
1383
+
1384
+ When `--output PATH` points to a non-writable location, users were getting
1385
+ either a generic shell `Permission denied` (when using shell redirection)
1386
+ or an opaque Python `OSError` (when using `--output` directly). Now
1387
+ ssmforge pre-flight checks the parent directory and prints a friendly
1388
+ hint pointing at writable alternatives.
1389
+
1390
+ **Before:**
1391
+ ```
1392
+ $ python -m ssmforge.cli arch X --graph --format markdown > q.md
1393
+ bash: q.md: Permission denied
1394
+
1395
+ $ python -m ssmforge.cli arch X --graph --format markdown --output C:\q.md
1396
+ Error: cannot write to 'C:\\q.md': [Errno 13] Permission denied: 'C:\\q.md'
1397
+ ```
1398
+
1399
+ **After:**
1400
+ ```
1401
+ $ python -m ssmforge.cli arch X --graph --format markdown > q.md
1402
+ # (still works in any shell that allows the redirect)
1403
+
1404
+ $ python -m ssmforge.cli arch X --graph --format markdown --output C:\q.md
1405
+ Error: cannot write to directory: C:\
1406
+ Permission denied.
1407
+ Try a writable location like:
1408
+ --output ./report.md (current directory)
1409
+ --output ~/report.md (your home directory)
1410
+ --output $TMPDIR/report.md (system temp)
1411
+ ```
1412
+
1413
+ Tests: 162 passed (was 156). 6 new tests for `_check_output_path`
1414
+ (returns (None, None) for None / '-' paths, validates writability,
1415
+ emits helpful hints for missing or read-only parents).
1416
+
1417
+ ### v0.2.1 — 2026-09-23
1418
+
1419
+ **New feature: `doctor --check-install`**
1420
+
1421
+ Run install checks (PATH, scripts location, platform) and exit non-zero
1422
+ when something is wrong. Use in scripts or when troubleshooting:
1423
+
1424
+ ```bash
1425
+ ssmforge doctor --check-install
1426
+ # prints ✓ or ✗ + fix hint
1427
+ echo $? # 0 = good, 1 = something off
1428
+ ```
1429
+
1430
+ The fix-hint output is a copy-pasteable shell command — exactly the
1431
+ right `export PATH=...` or `[Environment]::SetEnvironmentVariable(...)`
1432
+ line for your platform.
1433
+
1434
+ **New: one-time PATH warning on stderr**
1435
+
1436
+ When ssmforge detects it's been installed off-PATH, every `ssmforge
1437
+ arch ...` call prints a one-time hint to stderr:
1438
+
1439
+ ```
1440
+ Note: 'ssmforge' is not on your PATH for `python -m ssmforge.cli` users.
1441
+ This is normal if you're running via 'python -m ssmforge.cli'.
1442
+ If 'ssmforge --version' fails, see: https://github.com/lordxmen2k/SSMForge#troubleshooting
1443
+ ```
1444
+
1445
+ This is suppressed:
1446
+ - when ssmforge IS on PATH
1447
+ - when the user explicitly runs `ssmforge doctor ...`
1448
+ - when `SSMFORGE_NO_PATH_WARN=1` is set in the env
1449
+
1450
+ **README install lead**
1451
+
1452
+ Section 4.2 (Create a virtual environment) now leads with a stronger
1453
+ recommendation and a fallback path (`python -m ssmforge.cli`). No
1454
+ changes to the install command itself — just clearer context.
1455
+
1456
+ **Bug fix: Qwen2 attention_bias false positive**
1457
+
1458
+ Previously, `scan_state_dict` set `attention_bias=True` whenever
1459
+ `*self_attn.*.bias` tensors existed in the state dict. Qwen2 ships
1460
+ zero-initialized bias tensors (vestigial, never used) and was
1461
+ incorrectly reported as having attention biases.
1462
+
1463
+ Now: `attention_bias` is set to True only when **either** the config
1464
+ explicitly says `attention_bias=True` **or** the bias tensors contain
1465
+ non-zero values. The `bias_keys_found` list still records that the
1466
+ tensors exist for transparency.
1467
+
1468
+ Tests:
1469
+ - `test_scan_detects_attention_bias` (updated) — zero biases → False
1470
+ - `test_scan_detects_nonzero_attention_bias` (new) — non-zero → True
1471
+ - `test_reports_attention_bias_from_state_dict_when_config_missing`
1472
+ (updated) — config.attention_bias=None + zero biases → False
1473
+
1474
+ **Bug fix: `--compare A A` no longer rejected**
1475
+
1476
+ Previously `--compare A A` was dedup'd to `[A]` and rejected with
1477
+ "requires at least 2 models". Now produces a valid 2-way "all identical"
1478
+ report.
1479
+
1480
+ **Feature: `--graph` honors `--format {text,json,markdown}`**
1481
+
1482
+ Three output modes for `--graph`:
1483
+ - `--format text` (default for `--graph`) — ASCII with box-drawing
1484
+ - `--format json` — structured pipeline + decisions for piping into
1485
+ other tools
1486
+ - `--format markdown`/`md` — GitHub-flavored Markdown tables for
1487
+ embedding in docs, Issues, PRs
1488
+
1489
+ All three work for both single-model and `--compare --graph` modes.
1490
+
1491
+ **Bug fix: `--graph` ASCII hierarchy**
1492
+
1493
+ The previous box-drawing had two parallel rails (`├─` and `▼`) that
1494
+ suggested two unrelated flows. Replaced with single-rail `▼` flow and
1495
+ `╭╮╰╯` panels for ATTENTION and MLP blocks. Decision-tree style
1496
+ `├─/└─` inside each panel. Renders correctly in any terminal.
1497
+
1498
+ **Tests: 156 passed** (was 142). 14 new tests:
1499
+ - 12 from `test_v021_check_install.py` (PATH detection, doctor
1500
+ --check-install, main() integration)
1501
+ - 1 from `test_v017_compare.py` (`--compare A A` regression)
1502
+ - 1 from `test_analyze.py` (nonzero attention_bias detection)
1503
+ - 13 from `test_graph.py` (JSON/Markdown formatters, GFM
1504
+ compatibility, edge cases)
1505
+ - (some overlap with existing test updates for attention_bias fix)
1506
+
1507
+ Tests: 142 passed (was 130). 12 new tests:
1508
+ - `_check_ssmforge_on_path`: returns correct tuple, finds off-path
1509
+ install, returns no fix when on PATH
1510
+ - `_warn_path_once`: writes to stderr when off-path, respects env
1511
+ var, skips when on PATH
1512
+ - `main()` integration: suppresses warning for doctor, emits for arch
1513
+ - `doctor --check-install`: text output on/off PATH, JSON output,
1514
+ exit codes (0 / 1), back-compat with default `doctor`
1515
+
1516
+ ### v0.2.0 — 2026-09-22
1298
1517
 
1299
1518
  **New feature: `--graph`**
1300
1519
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ssmforge"
7
- version = "0.2.0"
7
+ version = "0.2.2"
8
8
  description = "Standalone HF architecture analyzer (`ssmforge arch`). Inspects any HuggingFace model and reports architectural quirks — attention biases, fused QKV/gate-up, tied embeddings, GQA, MQA, MoE, sliding window, LayerScale, soft-capping, partial RoPE, MLP type, norm type. Outputs structured JSON + human-readable summary."
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -1,3 +1,3 @@
1
1
  """SSMForge — architecture analyzer for HuggingFace models."""
2
2
 
3
- __version__ = "0.2.0"
3
+ __version__ = "0.2.2"
@@ -34,7 +34,14 @@ from ssmforge.analyze.state_dict_scan import (
34
34
  from ssmforge.analyze.summary import render_summary
35
35
  from ssmforge.analyze.markdown_render import format_report_markdown
36
36
  from ssmforge.analyze.diff import diff_reports, compare_reports, format_compare_markdown
37
- from ssmforge.analyze.graph import render_graph_text, render_graph_compare
37
+ from ssmforge.analyze.graph import (
38
+ render_graph_text,
39
+ render_graph_compare,
40
+ render_graph_json,
41
+ render_graph_compare_json,
42
+ render_graph_markdown,
43
+ render_graph_compare_markdown,
44
+ )
38
45
 
39
46
  __all__ = [
40
47
  "build_report",