ochna 0.3.2

A structural code graph indexing and analysis CLI using Tree-sitter and SQLite
# v0.3 Acceptance — Linux Corpus (strncpy removal)

This is the v0.3 corpus-benchmark acceptance run for the Linux row in
`docs/v0.3-contract.md`'s "Corpus benchmarks and budgets" table. It reruns the
scenario documented in `docs/experiments/linux-strncpy-removal.md` against the
installed v0.3 CLI, adding the required-evidence checks the contract demands:
(a) the current tree confirms `strncpy` the core kernel API is absent, and (b)
a deleted-API report on this shallow clone is explicitly historical — surfaced
as a truthful `base_revision_unavailable` failure, never a guessed diff.

## Subject

- Repo: `torvalds/linux`
- Pinned submodule commit: `1a3746ccbb0a97bed3c06ccde6b880013b1dddc1`
- Subject: `Merge tag 'strncpy-removal-v7.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux`
- Machine: local macOS (darwin), `ochna` installed globally via `make install`
  (`ochna --version` reports `0.2.0`; the binary exposes all v0.3 subcommands:
  `doctor`, `unresolved`, `tests-for`, `impact`, `diff`)
- All work happened in disposable temp directories outside the repo. The live
  submodule at `clones/linux` was never `cd`-ed into or mutated; only `git
  archive` (read-only) and `cp -r` (read-only copy) touched it.

## Part A — current-tree absence check

Archived the pinned tree into a fresh directory and indexed it standalone
(no `.git`, tree-only):

```bash
mkdir -p /tmp/ochna-accept-linux-tree
git -C .../clones/linux archive 1a3746ccbb0a97bed3c06ccde6b880013b1dddc1 | tar -x -C /tmp/ochna-accept-linux-tree
cd /tmp/ochna-accept-linux-tree && ochna init
```

- Archive + extract: `26.9s` wall clock.
- `ochna init`: `1:56.57` wall clock (`253.31s` user, `14.59s` system, 229% CPU).
- Index result: `65221` files, `1403920` nodes, `1645608` resolved edges,
  `2355864` unresolved references (from `4477237` raw calls).

`ochna status --json`:

```json
{
  "action": "none",
  "counts": {"edges": 1645608, "files": 65221, "nodes": 1403920},
  "db_present": true,
  "freshness": "unknown",
  "ok": true,
  "schema": {"expected": 6, "found": 6, "match": true}
}
```

(`git`/`head_sha` fields are `N/A`/`null` here because this is a bare archive
extract with no `.git` — expected and consistent with the tree-only mode used
in the prior experiment.)

`ochna search strncpy --json` returned 16 matches. Critically, **none of them
is the removed core kernel API**: the only exact-name `strncpy` hit is
`tools/include/nolibc/string.h::strncpy`, a distinct tool-only nolibc helper
(matches the prior experiment's finding). The rest are genuinely different
APIs that must not be confused with the removed one:
`strncpy_from_user` (multiple arch/lib definitions), `strncpy_from_bpfptr`,
`strncpy_from_sockptr`, `strncpy_chunk_from_user`,
`strncpy_from_user_nofault`, `strncpy_from_kernel_nofault`, `safe_strncpy`,
`acpi_ut_safe_strncpy`, `do_strncpy_from_user`, `raw_strncpy_from_user`
(Rust), `strnlen_user`, `libbpf_strlcpy`. No graph confusion between
`strncpy` and these distinct, differently-named APIs was observed.

Direct file checks confirmed the core API's absence at its former locations:

```bash
ochna node --file lib/string.c --symbols-only --json | jq -r '.[].name' | grep -i strncpy   # (empty)
ochna node --file include/linux/string.h --symbols-only --json | jq -r '.[].name' | grep -i strncpy   # (empty)
ochna node --file include/linux/fortify-string.h --symbols-only --json | jq -r '.[].name' | grep -i strncpy   # (empty)
```

All three greps returned nothing. As a sanity check that file parsing itself
still works, `lib/string.c` still yields 36 other `str*` symbols
(`strcpy`, `strcat`, `strcmp`, `sized_strscpy`, ...) — the file is indexed
correctly, `strncpy` is simply gone from it.

`ochna unresolved --in lib/string.c --json` returned 13 unresolved references
(macro/helper calls like `WARN_ON_ONCE`, `IS_ENABLED`, `load_unaligned_zeropad`),
none referencing `strncpy` as a phantom call target.

**Verdict: PASS.** The current tree confirms `strncpy` the core kernel API is
absent, and the graph does not conflate it with the distinct
`strncpy_from_user`/`strncpy_from_kernel_nofault`/nolibc/helper APIs.

Temp directory removed: `rm -rf /tmp/ochna-accept-linux-tree`.

## Part B — historical diff honesty check

Full copy including `.git`, so `ochna diff` has real Git history available
to query (even though the pin is a shallow checkout with no parent commit
locally):

```bash
mkdir -p /tmp/ochna-accept-linux-full
cp -r .../clones/linux/. /tmp/ochna-accept-linux-full/
```

- Copy: `45.6s` wall clock (`0.81s` user, `32.97s` system — mostly I/O syscall
  overhead scanning file metadata), resulting in `1.8G` on disk.
- The submodule's `.git` is a gitlink file pointing at the superproject's
  `.git/modules/clones/linux`, which a plain `cp -r` does not follow. To get a
  real, independent Git history in the copy (required for `ochna diff` to
  even attempt a revision lookup) the actual `.git/modules/clones/linux`
  directory was also copied read-only into the temp copy's `.git`, and its
  `core.worktree` setting was repointed from the relative
  `../../../../clones/linux` to the absolute `/tmp/ochna-accept-linux-full`
  so all subsequent Git operations resolve inside the temp copy only. This
  copy and single config edit happened entirely under `/tmp` and never wrote
  to `.git/modules/clones/linux` or `clones/linux` in the source repo.
- Confirmed before indexing:
  - `git rev-parse HEAD``1a3746ccbb0a97bed3c06ccde6b880013b1dddc1` (matches
    the pin).
  - `git rev-parse --is-shallow-repository``true`.

`cd /tmp/ochna-accept-linux-full && ochna init`:

- `1:54.56` wall clock (`255.75s` user, `13.72s` system, 235% CPU).
- Same index shape as Part A: `65221` files, `1645608` resolved edges from
  `4477237` raw calls, `2355864` unresolved references.
- This run correctly picked up real Git metadata: `Commit SHA:
  1a3746ccbb0a97bed3c06ccde6b880013b1dddc1`, matching commit message/date,
  `Git Status: dirty` (the pre-existing tracked-file modifications carried
  over from the source submodule, as expected of a straight copy).

`ochna diff --base HEAD^ --head HEAD --json` (exit code `1`):

```json
{
  "command": "diff",
  "contract_version": "0.3",
  "data": {
    "base": null,
    "edges": [],
    "files": [],
    "head": null,
    "historical_snapshot": null,
    "newly_unresolved_callers": [],
    "symbols": [],
    "unmapped_hunks": []
  },
  "next_action": "provide a base revision",
  "ok": false,
  "truncated": false,
  "warnings": [
    {
      "code": "base_revision_unavailable",
      "message": "Base revision \"HEAD^\" is unavailable locally (git rev-parse --verify --end-of-options HEAD^: fatal: Needed a single revision). Fetch it before requesting historical deltas; shallow clones may need git fetch --deepen."
    }
  ]
}
```

**Verdict: PASS — truthful, structured failure.** `ok` is `false`, the
warning code is exactly the contract-specified `base_revision_unavailable`,
`next_action` is the prescribed `"provide a base revision"`, and every
collection (`files`, `symbols`, `edges`, `newly_unresolved_callers`,
`unmapped_hunks`) is an empty array rather than a fabricated or guessed diff.
`historical_snapshot` is `null`, confirming no temporary historical index was
silently substituted or left behind. Ochna correctly declines to answer
rather than inventing a removed-`strncpy` diff from name/prefix matching.

Temp directory removed: `rm -rf /tmp/ochna-accept-linux-full` (1.8G reclaimed).

## Acceptance summary

| Required evidence | Result |
| --- | --- |
| Current tree confirms `strncpy` absence as a real API | PASS — absent from `lib/string.c`, `include/linux/string.h`, `include/linux/fortify-string.h`; distinct APIs (`strncpy_from_user`, `strncpy_from_kernel_nofault`, nolibc `strncpy`, `safe_strncpy`, etc.) correctly not conflated with it |
| Deleted-API report is explicitly historical, never guessed | PASS — shallow-clone `ochna diff --base HEAD^ --head HEAD` returns `ok: false`, `base_revision_unavailable`, `next_action: "provide a base revision"`, all collections empty, no fabricated diff |

Both required-evidence claims from the contract's Linux corpus row hold on
the installed v0.3 CLI. No file inside `clones/linux` or the superproject's
`.git/modules/clones/linux` was modified; all indexing and diff work ran
against disposable `/tmp` copies that were removed after use.