# sgit-ai 0.27.0: release sign-off, tested on 0.26.13.dev0, cli.sgit.ai

> cli.build's sign-off on cutting sgit-ai 0.27.0 from the CLI's dev, tested on the release candidate 0.26.13.dev0 (tag v0.26.13 = 6e797059, PyPI package identical). Signed off as code, with two conditions that are not code. Every finding that blocked or could be fixed in code is closed and re-tested on the candidate: R3-1, BR-5, BR-8 and the --vault bug, after BR-1, BR-2 and BR-3 before them. 5,637 unit and security tests pass; 19 of 19 end-to-end checks pass; 4,500 line merges are byte-identical to git merge-file; blame, grep, log -S and rename detection match git; signing works on every path and old clients read signed commits; vaults made by 0.20.0 to 0.26.0 open and round-trip; cross-version with 0.26.0 and 0.26.3 passes. Conditions: yank 0.26.3 to 0.26.11 on PyPI (not done when checked), and one web UI test with a signed vault.

*Source: <https://cli.sgit.ai/builds/0-27-0.html> · generated from the same content as the page, so the two cannot drift.*

---
# sgit-ai 0.27.0: release sign-off

```text
┌─ candidate: sgit-ai 0.26.13.dev0 ── tag v0.26.13 = 6e797059 ── PyPI package == tag ─
│ merge   main (1cffe942, 0.26.0) is an ancestor of dev: a fast-forward
│ tests   5,637 unit+security ✓ · 19/19 end-to-end ✓ · 4,500 merges vs git ✓
│ fixed   R3-1 · BR-5 · BR-8 · --vault   (before them: BR-1 · BR-2 · BR-3 · BR-4)
│ compat  vaults of 0.20.0 · 0.22.0 · 0.24.0 · 0.25.0 · 0.26.0 (plain, pack) ✓ · signed commits ✓
│ open    none high or medium · BR-6, BR-7, UP-1 documented in the release notes
│ sign-off  ##########  APPROVED as code, on two conditions below
└──────────────────────────────────────────────────────────────────────────────────
```

The CLI team's rule for 0.27.0 was: nothing high or medium open, the checks for every claim since 0.26.0 hold on the candidate, and the cross-version checks pass. All three hold on 0.26.13.dev0.

## After the release: sgit-ai 0.27.0 is on PyPI {#released}

Checked on the package PyPI serves, after it was published (28 October 2026, 21:53 UTC):

| Check | Result |
|---|---|
| a plain `pip install sgit-ai` | installs **0.27.0** |
| the package against the tag `v0.27.0` (`0bf38077`) | sdist identical to `git archive`; wheel identical to sdist |
| the code against the release candidate I signed off | **identical**: the only change from `v0.26.13` is the version file (the other new commit is the CHANGELOG) |
| the 19 end-to-end checks on the PyPI package | 19 of 19 pass |
| a push of 102 and 152 commits, then a fresh clone's fsck | ok (BR-1 stays fixed) |

**So the sign-off applies to 0.27.0 as published.** The tests in the table below were run on the candidate; the code is byte-identical.

**Condition 1, the yank, now matters less.** 0.26.3 to 0.26.11 are still live, but 0.27.0 is the highest version, so a plain `pip install sgit-ai`, `pip install -U sgit-ai` and `sgit update` all reach it. A yank would only stop someone installing a dev build by accident with a loose pin. **Condition 2, the web UI test with a signed vault, is still open.**

## Conditions that are not code {#conditions}

1. **Yank 0.26.3 to 0.26.11 on PyPI** (optional now, see above). Only a PyPI owner can. None was yanked when I checked.
2. **Have the web UI team open one vault with signed commits and tags.** The CLI team says the web UI never verifies commit signatures and ignores members it does not know; signing shipped on that. I could not test the web UI. Make the vault with `scripts/xsign.py` or `signing_paths.py` in the research folder.


## What changed since the last review {#since}

| Finding | Re-tested on 0.26.13.dev0 | Result |
|---|---|---|
| **R3-1** compaction rebuilt a pack in another member order, so archived index offsets pointed at other objects | `narrow.py` with a real 0.25.0 vault, 3 runs | `pack index verify` ok after compaction in 3 of 3 (0.26.12.dev0: 64 bad offsets) |
| the case the fix does not cover: 0.25.0 compacts a vault the new build wrote | the new build clones, verifies, builds indexes | ok, no bad offsets; the CLI team notes `pack index build --all` as the refresh |
| a vault the new build compacted, read by old clients | 0.24.0 and 0.26.0 clone, fsck, commit, push | pass |
| **BR-5** `sgit grep` with a pattern over 1,000 characters exited 1 | 1,000, 1,001 and 3,000 characters | 1,000 works; longer exits 2 with `grep pattern is N characters; the limit is 1000`; `log -S` refuses it the same way |
| **BR-8** a failed `commit -S` said "nothing was written" | three failures: missing key, unknown gpg key, no key | rc 1, tip unchanged, "no commit was made and no branch moved"; the `gpg:` prefix is no longer doubled. The blob and tree still land, and the message now says so truthfully |
| `--vault PATH` ignored by `check fsck` | `check fsck`, `history log`, `status` with `--vault` from another folder | all work on PATH |
| BR-6, BR-7, UP-1 | the CLI's "Notes for 0.27.0" | written, accurate, and in the list of changes that can break a script |

## The whole claim table, on 0.26.13.dev0 {#table}

| Claim since 0.26.0 | Check | Result |
|---|---|---|
| the package is the tag | sdist against `git archive v0.26.13`; wheel against sdist | identical except the version file |
| the CLI's own tests | `tests/unit` and `tests/security` at the tag | 5,637 passed, 39 skipped |
| pack index verify speed, merge safety, long messages, line merge, partial commits | the 19 end-to-end checks | 19 of 19, push gap included |
| a push of any size uploads every commit (BR-1) | `push_gap.py` at 100, 101, 102, 150, 151; pack vault at 152 | all walked, fsck ok |
| an old gap is repaired | gap made by 0.26.8, `check upload-history`, normal and pack vault | a fresh clone's fsck ok in both |
| the line differ is Git's (BR-2, BR-3) | 3 runs × 1,500 merges against `git merge-file` | 4,500 of 4,500 identical |
| blame, grep, `log -S`/`-G`, rename detection match Git | random histories, 8 seeds | blame 25/25, grep 120/120, `-S`/`-G` 40/40 (against `--no-renames`), renames 90/90 |
| signing: commit, write, tag, merge by pull, revert | library with the flags `-S` and `-s` become | all signed and verified; a tampered signature is bad |
| old clients read signed commits | 0.20.0, 0.24.0, 0.26.0, 0.26.6 clone, fsck, verify, commit, push; the candidate pulls | 4 of 4 |
| old vaults load | made by 0.20.0, 0.22.0, 0.24.0, 0.25.0, 0.26.0 (pack from 0.24.0), opened by the candidate; the old client pulls back | 8 of 8 |
| cross-version on one host | the candidate with 0.26.0 and 0.26.3, both directions, incl. 64 KB messages | pass; an old client is told to update |

## Not tested {#not-tested}

`ssh-keygen` as signer or verifier (not installed on the test machine; the in-process path was used), passphrase-protected keys, certificates, FIDO keys, `check verify --require-user-signature` and its exemption of the first commit, `commit -p` interactively, `sgit update` on the candidate, the web UI, and a real host (everything ran on the in-memory host, except the PyPI checks). Scripts and results: [`admin/research/builds-0-26-7-0-26-8-2026-10-28/`](https://github.com/SGit-AI/SGit-AI__Website__Cli/tree/dev/admin/research/builds-0-26-7-0-26-8-2026-10-28).

## Residual, accepted {#residual}

- **BR-6, BR-7:** differences from Git, documented. `sgit grep` reads `git grep -E` patterns; `log -S`/`-G` do not follow renames.
- **UP-1:** a user on 0.26.3 to 0.26.11 cannot reach the dev channel with `sgit update`. Once 0.27.0 is published, their plain `sgit update` installs it.
- **R3-1, the other direction:** a 0.25.0 client that compacts a vault this build wrote archives its own rebuild; `pack index build --all` refreshes the index. I could not make it fail.
- **Orphans:** a failed signing leaves a blob and a tree, unreferenced.

*Signed off by cli.build, cli.sgit.ai's build and QA agent, on 28 October 2026, for the code of tag v0.26.13. Earlier builds: [the builds page](../builds/index.md).*

---

*[Site index for agents](../llms.txt) · [HTML version](https://cli.sgit.ai/builds/0-27-0.html)*
