Skip to content

feat(git): git.digest paged ingest + URL clone cache (ADR-088 Amendment 1)#761

Merged
oceanwaves630 merged 5 commits into
mainfrom
feat/git-digest
Jul 9, 2026
Merged

feat(git): git.digest paged ingest + URL clone cache (ADR-088 Amendment 1)#761
oceanwaves630 merged 5 commits into
mainfrom
feat/git-digest

Conversation

@oceanwaves630

Copy link
Copy Markdown
Collaborator

Implements the ADR-088 Amendment 1 lane: git.digest paged GitHub ingest and URL clone caching.

  • Paged gh fetches via --search "sort:updated-asc updated:>=<floor>" with a pure decide_page_outcome/PageOutcome decision function (7 unit tests); done:true only when a page proves the window exhausted; floor-stall returns done:false instead of spinning.
  • --state all on both pr/issue paged fetches, regression-locked by a fake-gh argv assertion (every logged invocation must carry it).
  • Clone cache: staged clone into a private .staging-<uuid> dir with pre-cache size check, same-filesystem rename into the cache slot; staging cleaned on clone failure, over-cap, size-probe error, and rename error paths. evict_lru gated by is_owned_entry (16-hex name + .git + .khive-last-used marker) so foreign dirs are never removed.
  • max_items parsed as i64, non-integers rejected, clamped 1..=2000 (4 boundary tests).
  • precedes commit→commit allowed via pack-level EdgeEndpointRule (GIT_EDGE_RULES).
  • Docs: api-reference verb count + git pack section.

Gates: fmt, clippy -D warnings, cargo test -p khive-pack-git (55), cargo test -p kkernel, cargo check --workspace all green. Three codex review rounds (r1 APPROVE-WITH-FIXES 2H/2M/1L, r2 APPROVE-WITH-FIXES 1H/1M on the r1 fixes, r3 fix commit a225b81) — narrow r3 re-review in flight.

🤖 Generated with Claude Code

…iction safety, staged clone-cap enforcement, max_items parsing

ADR-088 Amendment 1 fix round (codex APPROVE-WITH-FIXES):

- HIGH: gh pr/issue list paged past the hard-coded --limit 1000 window.
  Replaced the single-fetch with an explicit --search "sort:updated-asc
  updated:>=<floor>" paging loop (ingest.rs) that advances the floor between
  calls and only reports done:true when a page proves the remote window is
  exhausted (fewer than PAGE_LIMIT items returned) -- a full page with the
  local budget not yet exhausted no longer silently reports done:true. The
  decision logic (decide_page_outcome/PageOutcome) is pulled out pure and
  unit-tested independent of gh/network/DB.

- HIGH: cache eviction (cache.rs::evict_lru) previously treated every direct
  child of the scratch root as an owned clone. Added is_owned_entry (16-hex
  cache-key-shaped name + .git dir + .khive-last-used marker) and gated
  eviction on it, so a KHIVE_GIT_DIGEST_SCRATCH_ROOT override pointed at a
  broader directory can never lose unrelated operator data.

- MEDIUM: per-clone size cap was enforced after the clone/fetch had already
  written past it. ensure_clone now clones into a private staging directory
  outside the cache root, measures it there, and only renames it into the
  addressable cache slot once under the cap -- an oversized clone never
  enters the cache slot or evict_lru's accounting. Doc comment states
  precisely what is (and isn't) guaranteed: the cap gates cache-slot entry,
  not the staging clone's transient disk usage.

- MEDIUM: max_items parsed via Value::as_u64, so a negative value silently
  fell through to the 500 default instead of clamping to the 1 floor.
  handlers.rs now parses as i64, rejects non-integer values outright, then
  clamps to 1..=2000.

- LOW: regenerated docs/guide/api-reference.md's verb count (75->76) and
  added the git pack's verb table entry for git.digest.

Gates (crates/, CARGO_TARGET_DIR=worktree-local target): fmt --check rc=0,
clippy -D warnings (khive-pack-git, kkernel) rc=0, cargo test -p
khive-pack-git rc=0 (54 tests: 35 unit incl. 12 new + 19 integration incl. 4
new), cargo test -p kkernel rc=0, cargo check --workspace rc=0.
…s, clean up staging dir on clone failure

ADR-088 Amendment 1 fix round r2 (codex APPROVE-WITH-FIXES, 2 residuals from
the r1 fix itself; r1's five findings all confirmed closed):

- HIGH: the r1 paging rewrite (fetch_pr_page/fetch_issue_page, ingest.rs)
  dropped the --state all flag the single-fetch version had, so gh's
  open-only default silently made closed issues and closed/merged PRs
  vanish from ingests -- exactly the historical provenance the pack exists
  to capture. Restored --state all on both gh pr list and gh issue list
  calls. Added a fake-gh argv assertion (gh_boundary_contract_and_partial_
  ingest_failure in tests/acceptance.rs) that every logged pr/issue list
  invocation contains "--state all", so a future regression here fails a
  test without needing live GitHub access.

- MEDIUM: ensure_clone's staging-then-rename path (cache.rs) cleaned up the
  staging dir on dir_size/over-cap/rename errors but not on the clone()
  call itself failing -- git can create and partially populate the
  destination before erroring (bad ref, auth failure, network drop), and
  evict_lru deliberately never touches non-owned .staging-* names, so
  repeated clone failures could accumulate unbounded scratch data. Wrapped
  the clone call in .inspect_err to remove_dir_all the staging dir on
  failure. Added ensure_clone_cleans_up_staging_dir_on_clone_failure, using
  a nonexistent local path as the clone source (fails fast, no network
  needed) and asserting no .staging-* directory remains under the scratch
  root afterward.

Gates (crates/, CARGO_TARGET_DIR=worktree-local target): fmt --check rc=0,
clippy -D warnings (khive-pack-git, kkernel) rc=0, cargo test -p
khive-pack-git rc=0 (55 tests: 36 unit incl. 1 new + 19 integration, one
extended with the --state all assertion), cargo test -p kkernel rc=0,
cargo check --workspace rc=0.
@oceanwaves630 oceanwaves630 marked this pull request as ready for review July 9, 2026 14:18
@oceanwaves630 oceanwaves630 enabled auto-merge (squash) July 9, 2026 14:18

@ohdearquant ohdearquant left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving review per merge flow v2: ADR-088 Amendment 1 contract verified at the diff (clamped max_items + done/cursor resumability with the frozen-cursor tie invariant, https-only source resolution with explicit ssh/git/scp rejection, staging-dir clone with failure-path cleanup and measure-before-move size cap, hook/prompt hardening, fail-open reference extraction with precedes parent edges). Codex r3 clean APPROVE on record.

The smoke-test surface tripwire (tests/smoke_test.py) and the verb-count
lines in CLAUDE.md, AGENTS.md, and docs/guide/api-reference.md all said 76
with 'git contributes 0 verbs'. git.digest makes it 77; the api-reference
pack table also understated brain at 14 (brain.event_counts made it 15).
This is exactly the drift the tripwire exists to catch — CI caught it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@oceanwaves630 oceanwaves630 merged commit f65f5a5 into main Jul 9, 2026
14 checks passed
@oceanwaves630 oceanwaves630 deleted the feat/git-digest branch July 9, 2026 14:47
ohdearquant pushed a commit that referenced this pull request Jul 9, 2026
…g (S1) (#762)

* feat(runtime): resolve_reference capability + recently-referenced ring (S1)

Implements Slice 1 of the unified-verb draft ADR
(.khive/workspaces/20260709/unified-verb/DRAFT-ADR-unified-verb.md):

- khive-runtime: reference_ring module — a bounded, per-(namespace, actor)
  daemon-warm ring (default N=64, TTL=30min, size-or-age eviction). Never
  persisted, no schema change. Wired into VerbRegistry as an Arc-shared
  field; admission happens at the dispatch boundary in
  dispatch_with_identity, gated by ring_admissions_for() which admits ONLY
  singleton create/get/update/delete/merge/link results (both link
  endpoints) — search/list and bulk shapes never admit.

- khive-runtime: reference_resolution module — resolve_reference(), the
  Layer-0 deterministic resolver. Three ordered stages, never a silent
  pick among close candidates: (1) id-string passthrough (UUID / 8+ hex
  prefix) through the existing resolve_by_id/resolve_prefix_unfiltered
  path; (2) ring exact/substring match; (3) hybrid_search fallback with a
  decisive-margin auto-resolve rule. Returns Resolved/Ambiguous/NotFound.

- khive-pack-kg: new read-only resolve(refs, kind?, limit?) verb (18th kg
  handler), thin wrapper over resolve_reference. No mutation, no plan
  execution — that is S2's ask verb, out of scope here.

- Bumped the three verb-count pins (kg_pack_exposes_18_handlers,
  pack_verbs_returns_eighteen, smoke_test.py total==77) that the new verb
  necessarily moves.

Tests: 17 new (11 reference_ring, 6 reference_resolution unit tests in
khive-runtime; 7 dispatch-level integration tests in khive-pack-kg
covering ring admission, actor isolation, ambiguity, and the search/list
non-admission gate condition). Full khive-runtime + khive-pack-kg suites
green (848 tests), fmt/clippy/workspace-check clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(runtime): address codex review findings on resolve_reference/ring (S1)

Fixes all six findings from the b1d2a3b4 review (review_resolve_s1.md),
2 High / 4 Medium:

1. [High] Ring admission (pack.rs) keyed on the gate-resolved `ns`, which
   can be a non-local `default_namespace`; ring lookup
   (reference_resolution.rs) keyed on `token.namespace()`, always `local`
   on the default dispatch path (ADR-007 Rule 0/3b). Admission now keys on
   `token.namespace()` too, matching lookup. Regression:
   resolve_via_ring_survives_non_local_default_namespace.

2. [High] Ring admitted any create/get/update/delete result carrying an
   `id`, including notes, and fell back to a note's `content` snippet as
   the display name. Added `substrate_admits_as_entity` (checks the
   `entity_type`/`content` key shape khive-pack-kg's handlers already
   return, plus edge/event `kind` tags and delete's echoed `kind` param)
   and dropped the content-fallback in `display_name`. merge/link admission
   unchanged (merge is entity-only by construction; link was already
   correct per review).

3. [Medium] The outer `HashMap<(namespace, actor), VecDeque<_>>` never
   dropped empty/stale keys or bounded key count. Added
   `prune_outer_map`: empty/stale keys drop on every admit/snapshot, and a
   new `DEFAULT_MAX_OUTER_KEYS` (4096) budget evicts least-recently-touched
   keys once exceeded (the just-admitted key is exempt from that pass).

4. [Medium] `lock().expect(...)` in `admit`/`snapshot` could panic a
   successful dispatch on mutex poisoning. Added `lock_state()`, which
   recovers via `into_inner()` with a `tracing::warn!` instead of
   panicking.

5. [Medium] Singleton hybrid-search resolution compared raw RRF scores
   (~1/61 typical) against a 0.7 absolute confidence bar, making the
   fallback functionally unable to resolve. Replaced with a presence rule:
   a lone candidate (nothing to be ambiguous against) or a decisive-margin
   winner resolves at a new, documented `SEARCH_RESOLVED_CONFIDENCE`
   constant, distinct from the ring's confidence bands; raw RRF stays
   exposed only via `ReferenceCandidate.score` in `Ambiguous` listings.

6. [Medium] Full-UUID passthrough used `resolve_by_id` (entity+note scope)
   while prefix passthrough scanned entity+note+event+edge — an edge's
   full UUID returned NotFound while its prefix resolved. Both branches
   now resolve entity ids only, identically, documented inline.
   Regression: resolve_id_string_passthrough_is_entity_only.

Also rebases onto origin/main (PR #761, git.digest) — smoke_test.py's
pinned verb count moves 76->78 (context #588 + resolve, the 17th/18th
kg-substrate bare verbs; git contributes git.digest).

Gates (worktree-local CARGO_TARGET_DIR, run from crates/): fmt --check,
clippy -p khive-runtime -p khive-pack-kg --all-targets -- -D warnings,
cargo test -p khive-runtime -p khive-pack-kg (789 passed), cargo check
--workspace. All green. tests/smoke_test.py also verified directly
against a built binary (total==78).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(runtime): prune the whole outer ring map from snapshot too (r2)

Codex r2 (review_resolve_s1_r2.md), one remaining Medium: prune_outer_map
had a single call site (admit), so a read-only daemon that only ever
calls snapshot() never pruned any OTHER actor's stale outer-map key —
only the queried key got cleaned up (and only via the ad-hoc
if-empty-remove this replaces).

snapshot() now calls the same prune_outer_map sweep admit() does, over
the whole map, after evicting the queried key's own stale entries.
Regression: snapshot_prunes_other_stale_keys_it_did_not_query (two
actors age out; only one is snapshotted; asserts the other's stale key
is gone too).

Gates (worktree-local CARGO_TARGET_DIR, run from crates/): fmt --check,
clippy -p khive-runtime -p khive-pack-kg --all-targets -- -D warnings,
cargo test -p khive-runtime -p khive-pack-kg (1156 passed across all
binaries), cargo check --workspace. All green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(kkernel): update kg verb-count pins for resolve (17 -> 18)

The resolve verb (S1) raises the kg pack surface to 18 verbs; update the
pack_introspect regression, the pack-kg crate docstring, and the contract
fixture manifest that all pinned 17.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(kkernel): add resolve to the kg bare-verb allowlist (contract test)

The verb_namespace_contract test pins the bare-name allowlist separately
from the pack_introspect count; add resolve and drop the count from the
allowlist's name so it cannot rot again.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore(kkernel): drop remaining hardcoded verb counts from contract-test comments

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: remove internal review artifacts committed by mistake

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants