# SPEC-axis review — fix: correct integer geohash decode for odd bitDepth

## Review
- **Correct (core fix):** The bit-position math in `main.js:decode_bbox_int` is right.
  Independently verified for bd=1..8: longitude positions are `bd-1, bd-3, …`
  (count `ceil(bd/2)`), latitude positions are `bd-2, bd-4, …` (count
  `floor(bd/2)`). The two sets are disjoint, sum to exactly `bd`, and the
  minimum position is always 0 — never negative. This matches `encode_int`
  (main.js:115), which interleaves longitude-first (`bitsTotal % 2 === 0` →
  lon) and pushes the first bit to the MSB (position `bd-1`).
  Spec line: *"read the two axes in SEPARATE loops with non-negative bit
  positions."* — satisfied.
- **Correct (edge cases):** bd=1 → lonBits=1/latBits=0, only the lon loop runs
  (position 0); bd=2 → lon pos 1, lat pos 0. Both verified against the old
  single-loop positions and are bit-identical for even bd (spec:
  *"leaving even bitDepth byte-for-byte identical"*).
- **Correct (no throw):** No validation/throw was added (spec: *"WITHOUT adding
  a validation/throw on odd bitDepth"*). `decode_bbox_int` has no guard.
- **Correct (no scope creep):** Diff touches only `decode_bbox_int` (+ comment)
  and adds tests; `encode_int`, `decode`, `neighbor_int`, etc. unchanged.
  `decode_int`/`neighbor_int`/`neighbors_int`/`bboxes_int` fixed transitively
  via `decode_bbox_int`. Spec: *"`encode_int` … needs no change."*
- **Correct (transitively-fixed funcs):** neighbors_int/bboxes_int route through
  decode_int → decode_bbox_int, so they are fixed.
- **Tests pass:** 17/17 (12 original + 5 new), 593 assertions. All 5 claimed
  categories present (a single-char, b odd/even prefix bbox, c neighbor_int,
  d even cell-size guard, e round-trip incl. odd). No existing test modified.

## Findings
- **Note — no direct test for `neighbors_int`/`bboxes_int` at odd bitDepth.**
  Spec lists both as transitively fixed, and they are, but none of the 5 test
  categories exercise them at odd bd. Coverage gap, not a defect.
- **Note — high-bitDepth precision caveat in test (b).** It probes up to
  bd=5·12=60; 32^12≈1.15e18 > Number.MAX_SAFE_INTEGER (2^53). Integer hash
  values for L≥11 lose low-bit precision in JS. This is inherent to the
  library's use of `Number`/`get_bit` (affects master too, not introduced
  here), and tolerance 1e-9 happens to hold for the chosen sample point.
  Residual risk, not a blocker.
- **Note — minor count discrepancy:** spec says "603 assertions"; actual run
  is 593. Cosmetic; does not affect correctness.
- **Note — test (e) `bds` array** duplicates `25` ([…,25,26,…,5,15,25]); trivial
  redundancy, no effect.

No blockers. No scope creep. Core math verified correct by independent
enumeration; even-bd bit-identical to master; odd-bd now correct.

## Acceptance evidence
- `git diff master...HEAD`: only main.js (decode_bbox_int rewrite) + tests/test.js (+99).
- `node_modules/.bin/nodeunit tests/test.js` → OK: 593 assertions, 17 tests pass.
- Independent position enumeration script (bd=1..8) confirms disjoint, total=bd,
  minpos=0 for every bd.
```
acceptance-report
{
  "criteriaSatisfied": [
    {
      "id": "criterion-1",
      "status": "satisfied",
      "evidence": "Concrete findings with file paths (main.js:decode_bbox_int), independent bit-position verification for bd=1..8, test run result (17/17 pass, 593 assertions), and severity-tagged notes."
    }
  ],
  "changedFiles": [
    "main.js"
  ],
  "testsAddedOrUpdated": [
    "tests/test.js"
  ],
  "commandsRun": [
    {
      "command": "git diff master...HEAD",
      "result": "passed",
      "summary": "Single commit; only main.js (decode_bbox_int rewrite) and tests/test.js (+99) changed."
    },
    {
      "command": "node_modules/.bin/nodeunit tests/test.js",
      "result": "passed",
      "summary": "OK: 593 assertions, 17 tests (12 original + 5 new) all pass."
    },
    {
      "command": "node -e <bit-position enumeration for bd=1..8>",
      "result": "passed",
      "summary": "Lon/lat positions disjoint, sum to bd, min position 0 for every bd; matches encode_int interleaving."
    }
  ],
  "validationOutput": [
    "All 17 tests pass (593 assertions); 5 new odd-bitDepth regression categories present and green.",
    "Even bitDepth positions identical to old single-loop math (bd=2,4 spot-checked); odd bitDepth now reads all bd bits with no negative position.",
    "decode_int(17,5) and neighbor_int(17,[1,0],5)=19 consistent with decode('j') and neighbor('j')='m'."
  ],
  "residualRisks": [
    "neighbors_int and bboxes_int have no direct odd-bitDepth test (transitively fixed via decode_bbox_int).",
    "Test (b) probes bitDepth up to 60, beyond Number.MAX_SAFE_INTEGER (2^53); latent JS-Number precision caveat inherent to the library, not introduced by this PR.",
    "Spec claims 603 assertions; actual run is 593 (cosmetic discrepancy)."
  ],
  "noStagedFiles": true,
  "diffSummary": "Rewrites decode_bbox_int to read longitude bits (ceil(bd/2)) and latitude bits (floor(bd/2)) in two separate loops with non-negative positions, fixing odd-bitDepth latitude collapse; even bitDepth bit-identical. Adds 5 odd-bitDepth regression tests.",
  "reviewFindings": [
    "blocker: none",
    "note: main.js decode_bbox_int core fix verified correct by independent bit-position enumeration (bd=1..8): lon at bd-1,bd-3,...; lat at bd-2,bd-4,...; disjoint, total=bd, minpos=0.",
    "note: tests/test.js — no direct test for neighbors_int/bboxes_int at odd bitDepth despite spec listing them as transitively fixed.",
    "note: tests/test.js (b) probes bitDepth up to 60, exceeding JS MAX_SAFE_INTEGER; inherent library limitation, not introduced here.",
    "note: tests/test.js (e) bds array duplicates value 25 (trivial).",
    "note: spec claims 603 assertions; actual test run reports 593 (cosmetic)."
  ],
  "manualNotes": "SPEC axis: no blockers, no scope creep, no implementation error found. Even bitDepth is byte-for-byte identical to master by construction; odd bitDepth math is correct. Notes are coverage/precision observations only."
}
```
