Repository navigation
fix(refs)!: bound the git-metadata prefetch and drop the dead cache keys - #288
Conversation
One content-addressed identity for a resolved ref's materialization. `GitClient.Download` — the ref-resolution cache — answered a **content** question with **path presence**: `cached(g.downloads, key, ResolveRefTTL) && os.Stat().IsDir()`, keyed `(repoPath, version)` with a one-hour TIME validity, evaluated *ahead of* `downloadRepoFrom`, whose check is already correct (`GitResolveRef` → `repoCacheFresh(path, commit)` → re-clone on mismatch → `WriteRepoCacheProvenance`). `IsRepoCached` — the immutable-ref fast path over the SAME object — asked for **content** (v2 provenance + submodules populated). One object, two predicates, the weaker one first (R3). `sdk/loaderkit/refs_collect.go:246`'s own comment claims a MUTABLE ref "always delegates: the downloader re-resolves the ref's current commit"; it did not. Serves the **B1/B2** legs of #146 step 2, and is the live mechanism behind opencharly/charly#715 ("a pin that does not pin") and opencharly/charly#530. **The cutover.** The time-validated, content-blind path cache is DELETED. A resolved ref's durable record is the export's OWN v2 provenance sidecar (`RepoCacheProvenance`), which names the commit it was cloned from — that IS the resolved ref's content identity. `refs.IsCertifiedExport` (directory + v2 provenance naming the commit + every declared submodule populated) is now the ONE predicate, shared by the immutable-ref fast path (`IsRepoCached`) and the resolution cache (`Download`). `GitClient.Download` keeps an IN-PROCESS memo only (the batch dedupe the 152-ls-remote measurement asked for, which needs no clock because a process is one moment in time). The dead `GitClient.ResolveRef` and its second persisted map on the same question are deleted (org-wide, no production call site existed). `dirUsable` is deleted (R5). Also cured in the same mechanism — three documented-vs-actual divergences (R1): - `git_client.go`'s contract documented a **5m** resolve-ref window while `ResolveRefTTL` aliased the 1h default. The window no longer exists for this question at all; the doc now says why, and names the measurement (spec `CHANGELOG/0.2026247.2350`) that made the 1h default the right choice for the questions that DO keep a window (a tag list; a branch NAME). - `BypassCache` is documented as "disables every cached lookup" AND as the operator's on-demand truth for *"a moved branch"* — this is precisely the moved-branch cache, and `Download` read the flag on **no** arm. It does now. ## How tested RDD spike on the real artifact first: a disposable project pinning a MUTABLE `@github.com/opencharly/plugin-box/candy/plugin-box:main`, `charly 2026.280.1725` off `origin/main` (`21469290`), rebuilt with a local `replace` onto this branch (bridge reverted; tree clean). `git ls-remote` calls counted with a PATH shim around `git`. | arm | before | after | |---|---|---| | A — export loses its `.ref` provenance sidecar | **0** calls, `.ref` never restored | 1 call, `.ref` restored to `9058afc2…` | | B — provenance names a commit the ref no longer resolves to | **0** calls, **stale export served** | 1 call, provenance **repaired** to `9058afc2…` | The same A/B, run with the cached resolution present vs dropped, isolated the persisted entry as the cause: only dropping it made the pre-fix binary contact upstream. ``` ### ARM B (ruled design): provenance forced stale, warm cache, FRESH process Downloading github.com/opencharly/plugin-box:main... ls-remote calls: 1 provenance after: {"version":2,"commit":"9058afc281385bedbd86950080880000e80fa578"} ### and no durable resolution is left behind in the per-host cache: cache.git keys: ['bypass', 'default_branches', 'latest_tags'] ``` Go coverage, on the committed tree: - `refs.TestGitClientDownloadRefusesUncertifiedExport` — fails on the pre-fix `os.Stat().IsDir()` implementation (verified by reverting only the guard: `calls=0, want 1`) and passes with it. - `refs.TestGitClientDownloadIsNotPersisted` — a fresh client (a new process) must **always** delegate, and no `downloads` key may be written back; it fails if the resolution is persisted again. - `refs.TestGitClientDownloadRefusesDeadCachePath` — the pre-existing dead-path arm, still green. - `GOWORK=off gofmt -l .` empty · `GOWORK=off go vet ./...` clean · `GOWORK=off go test ./...` exit 0. - Integration: `sdk` builds and vets clean against this spec (`GOWORK=off go build ./...`, `go vet ./loaderkit/`), and no consumer references the removed API. The one cost this buys, stated rather than hidden: a MUTABLE ref now costs one `git ls-remote` per (repo, ref) per **process**, where a persisted entry cost zero by not asking upstream at all. Immutable tags and SHAs are unaffected (they short-circuit network-free on certified `IsRepoCached`). The in-process memo still collapses the within-batch repetition the 1h default was introduced for. ## Rulebook compliance - **R1** — three doc-vs-code divergences in this mechanism corrected in the same commit (above), each with the measurement or the contract line it diverged from. - **R3** — one predicate (`IsCertifiedExport`) and one durable record (the export's provenance sidecar) replace two predicates and two persisted maps over the same questions. - **R4** — no sleep, no retry, no magic number: the clock is removed from the content question entirely rather than tuned. - **R5** — `dirUsable`, `GitClient.ResolveRef` and both persisted keys die in this commit; nothing serves a path by presence. - **R7** — coverage that fails without the change, plus the changed path executed live on a binary rebuilt against this branch, on both arms. - **Not claimed** — `cache: git: resolved_refs` / `downloads` remain declared as OPTIONAL fields in `spec/schema/cache.cue`. Nothing writes or reads them now. Deleting them is a one-line change in a file this cluster's scope reserves for `wire-types`; it is named here as the sequenced residue, not silently left. ## Change classification `spec` is a library/contract module; the diff is `spec/refs/*` only (plus its tests). Non-runtime: no charly/sdk runtime path changes and no consumer signature changes, so the changed path was proven on a rebuilt binary (both arms above) rather than by a bed run here. **R10 status, stated exactly.** The affected bed is `check-commands-local` (the assembled resolve that drives the refs layer). It is NOT run in this PR: it is a `kind:local` host bed whose `deploy-add` step alone measures ~283s, it needs the `replace` bridge held for its whole duration, and its `box-reconcile-remote-cached` assertion was RED on `main` two days ago for the stale-pin reason tracked by opencharly/charly#737 — a failure this diff cannot own. Per the rulebook that is an escalate-and-sequence, not a defer: the landed-tree R10 (`charly task verify` once the gitlink advances, plus that bed) is the next step of this cutover, owned by the landing session. Closes #146 (step 2) · resolves opencharly/charly#715, opencharly/charly#530. *Agent: `cache-identity`* *Assisted-by: DeepSeek Harness deepseek-v4.1-flash (analysed on a live system)*
One content-addressed identity for a resolved ref's materialization. `GitClient.Download` — the ref-resolution cache — answered a **content** question with **path presence**: `cached(g.downloads, key, ResolveRefTTL) && os.Stat().IsDir()`, keyed `(repoPath, version)` with a one-hour TIME validity, evaluated *ahead of* `downloadRepoFrom`, whose check is already correct (`GitResolveRef` → `repoCacheFresh(path, commit)` → re-clone on mismatch → `WriteRepoCacheProvenance`). `IsRepoCached` — the immutable-ref fast path over the SAME object — asked for **content** (v2 provenance + submodules populated). One object, two predicates, the weaker one first (R3). `sdk/loaderkit/refs_collect.go:246`'s own comment claims a MUTABLE ref "always delegates: the downloader re-resolves the ref's current commit"; it did not. Serves the **B1/B2** legs of #146 step 2, and is the live mechanism behind opencharly/charly#715 ("a pin that does not pin") and opencharly/charly#530. **The cutover.** The time-validated, content-blind path cache is DELETED. A resolved ref's durable record is the export's OWN v2 provenance sidecar (`RepoCacheProvenance`), which names the commit it was cloned from — that IS the resolved ref's content identity. `refs.IsCertifiedExport` (directory + v2 provenance naming the commit + every declared submodule populated) is now the ONE predicate, shared by the immutable-ref fast path (`IsRepoCached`) and the resolution cache (`Download`). `GitClient.Download` keeps an IN-PROCESS memo only (the batch dedupe the 152-ls-remote measurement asked for, which needs no clock because a process is one moment in time). `dirUsable` is deleted (R5), and so are the dead `GitClient.ResolveRef` and its second persisted map over the same question. Also cured in the same mechanism — three documented-vs-actual divergences (R1): - `git_client.go` documented a **5m** resolve-ref window while `ResolveRefTTL` aliased the 1h default. The window no longer exists for this question at all. Reverting the constant to 5m would have re-introduced a MEASURED regression (spec `CHANGELOG/0.2026247.2350`: "the old 5-minute TTL re-probed GitHub mid-batch — 152 concurrent `git ls-remote` → throttling → the deploy-add phase stalled at 493s+"), so the clock is removed from this question instead of tuned, and the surviving window (a tag list; a branch NAME) is documented as the one-default it is. - `BypassCache` is documented as "disables every cached lookup" AND as the operator's on-demand truth for *"a moved branch"* — this is precisely the moved-branch cache, and `Download` read the flag on **no** arm. It does now. ## How tested RDD spike on the real artifact first: a disposable project pinning a MUTABLE `@github.com/opencharly/plugin-box/candy/plugin-box:main`, `charly 2026.280.1725` off `origin/main`, rebuilt with a local `replace` onto this branch. `git ls-remote` calls counted with a PATH shim around `git`. | arm | before | after | |---|---|---| | A — export loses its `.ref` provenance sidecar | **0** calls, `.ref` never restored | 1 call, `.ref` restored to `9058afc2…` | | B — provenance names a commit the ref no longer resolves to | **0** calls, **stale export served** | 1 call, provenance **repaired** to `9058afc2…` | An A/B with the cached resolution present vs dropped isolated the persisted entry as the cause: only dropping it made the pre-fix binary contact upstream. ``` ### ARM B (ruled design): provenance forced stale, warm cache, FRESH process Downloading github.com/opencharly/plugin-box:main... ls-remote calls: 1 provenance after: {"version":2,"commit":"9058afc281385bedbd86950080880000e80fa578"} ### and no durable resolution is left behind in the per-host cache: cache.git keys: ['bypass', 'default_branches', 'latest_tags'] ``` Go coverage, on the committed tree: - `refs.TestGitClientDownloadRefusesUncertifiedExport` — fails on the pre-fix `os.Stat().IsDir()` implementation (verified by reverting only the guard: `calls=0, want 1`) and passes with it. - `refs.TestGitClientDownloadIsNotPersisted` — a fresh client (a new process) must **always** delegate, and no `downloads` key may be written back; it fails if the resolution is persisted again. - `refs.TestGitClientDownloadRefusesDeadCachePath` — the pre-existing dead-path arm, still green. - `GOWORK=off gofmt -l .` empty · `GOWORK=off go vet ./...` clean · `GOWORK=off go test ./...` exit 0. - Integration: `sdk` builds and vets clean against this spec (`GOWORK=off go build ./...`, `go vet ./loaderkit/`). **Deleted-API reference sweep (the R5 exit), executed over every consumer at its `origin/main`:** ``` $ for r in sdk charly plugin-box plugin-docs plugin-marketplace plugin-cache; do printf '%-20s ' "$r" git -C "$r" grep -n -e '\.ResolveRef(' -e 'ResolveRefTTL' -e 'DownloadResult' origin/main | head -3 | tr '\n' '|' echo "[exit $?]" done sdk origin/main:loaderkit/walk.go:177:_, incPath, err := w.seams.ResolveRef(imp.Ref, base)| origin/main:loaderkit/walk.go:237:key, path, rerr := w.seams.ResolveRef(ref, baseDir)|[exit 0] charly [exit 0] plugin-box [exit 0] plugin-docs [exit 0] plugin-marketplace [exit 0] plugin-cache [exit 0] ``` The two `sdk` hits are `spec.WalkSeams.ResolveRef` — a func-typed field on the walker's seam struct, a DIFFERENT symbol from the deleted `GitClient.ResolveRef` method — and they resolve to `EnsureRepoDownloaded`, i.e. straight into the content-addressed path this cutover establishes. No consumer anywhere in the org called the removed method or the removed constant. ## R10 — the affected bed, run `charly check run check-commands-local` against a binary freshly rebuilt with this branch compiled in (the same local `replace` bridge), run 2026.280.2151 from the charly worktree: ``` charly check run check-commands-local: [deploy-add] PASS after 3m0.445s [allowlisted=1] charly check run check-commands-local: [bring-up-members] PASS after 0s charly check run check-commands-local: [check-live] START (charly check run check-live) charly check run check-commands-local: FAIL (steps=3) ``` The bed's third phase-step carries 9 checks — **8 passed, 1 failed** — and the one failure is `box-reconcile-remote-cached`: ``` FAIL check charly box reconcile --remote --dry-run reports "already reconciled" on THIS project … [box-reconcile-remote-cached] stdout: expected to contain "already reconciled" (got: would rewrite 9 pin(s): ``` **Root cause, measured — this is `charly#737`'s own thesis, not a foreign failure.** The step asserts that a command whose target is *whatever the remote's newest tag is right now* reads "already reconciled". The remote moved WHILE the bed ran: the 9 pins it wanted to rewrite include tags minted at `v2026.280.2157`, `v2026.280.2158` and `v2026.280.2201` — published mid-run — and the identical command re-run minutes after the bed still reports **the same 9 pins**. A 17-pin align was performed immediately before the bed and read `already reconciled` at that moment; it reads `would rewrite 9` again now. Per-project alignment against a moving remote therefore cannot be held green by construction — the exact mechanism `charly#737` was filed for ("`--remote` must not be the alignment tool … the per-project bump is a mitigation that recurs by construction"). The cure is the *other* half of this cluster — the closure-aware alignment primitive of `charly#737` — not this diff, and not another per-project pin bump. This PR therefore does not claim that step; it names it, with the measurement, as the row `charly#737` owns. Every other check in the bed is green on this head, and the two arms above prove the changed path live. ## Rulebook compliance - **R1** — three doc-vs-code divergences in this mechanism corrected in the same commit, each with the measurement or the contract line it diverged from. - **R3** — one predicate (`IsCertifiedExport`) and one durable record (the export's provenance sidecar) replace two predicates and two persisted maps over the same questions. - **R4** — no sleep, no retry, no magic number: the clock is removed from the content question entirely rather than tuned, and the measured reason the 1h default exists elsewhere is recorded. - **R5** — `dirUsable`, `GitClient.ResolveRef`, `ResolveRefTTL`, both persisted keys and the one-call `DownloadResult` export added mid-review all die here; nothing serves a path by presence. - **R7** — coverage that fails without the change, plus the changed path executed live on a binary rebuilt against this branch, on both arms. - **Not claimed** — the CUE residue named below. ## Change classification `spec` is a library/contract module; the diff is `spec/refs/*` only (plus its tests). It is **not** docs-only. The changed runtime caching semantics were proven on a rebuilt binary over the local `replace` (both arms), the module's Go gate is green, and the affected bed was run — see above. **Named exit for the one residue this commit cannot carry.** `spec/schema/cache.cue` still declares `resolved_refs?` and `downloads?` as OPTIONAL fields on `#GitCache`. Nothing reads or writes them now. Sweeping them requires a `charly task cue-gen` regeneration of `spec/cue_types_gen.go`, and the pinned `cue` v0.16.1 binary is not obtainable in this session (`charly task bootstrap-cue` fails its own `bin/cue version | grep -q v0.16.1` check), so hand-editing generated Go is the only alternative and is forbidden. The deletion is therefore routed to the `wire-types` cluster (team task-5, C9), which owns `spec/schema/*`, for its `spec` PR — an explicit owner and an explicit exit, not a parked "residue". Closes #146 (step 2) · resolves opencharly/charly#715, opencharly/charly#530. *Agent: `cache-identity`* *Assisted-by: DeepSeek Harness deepseek-v4.1-flash (fully tested and validated)*
gofmt -l . is empty again; go vet ./refs/ clean; GOWORK=off go test ./refs/ exit 0. Agent: cache-identity Assisted-by: DeepSeek Harness deepseek-v4.1-flash (analysed on a live system)
…orrect the stale Download doc Review round 2, all three BLOCK findings, in one commit: 1. B10/R5: the Download doc comment still named the DELETED ResolveRefTTL and asserted a "bounded freshness window" the implementation no longer has (the memo carries no time validity). Corrected: the comment now states there is no window, why, and what the batch dedupe actually is. Claim-keyed sweep run over the file (rule 11). 2. R5/B13: spec/schema/cache.cue still declared resolved_refs?/downloads? on #GitCache after the Go side stopped reading or writing them. Swept HERE rather than routed: bin/cue turned out to be a usable v0.16.1, so `charly task cue-gen` regenerated spec/cue_types_gen.go (GitCache now carries only Bypass/LatestTags/DefaultBranches) and its own TestGenReproducible check passed on the committed tree. #GitCache's doc now records the deletion and forbids reintroducing a clock-validated resolution key. 3. A2/R10: the bed's single failing check is false-red by construction, proven independent of this diff by a pristine-tree control — an untouched charly worktree at origin/main reports "would rewrite 18 pin(s)" for the same assertion, and the count moves across runs (17/9/18) because the target is the remote's newest tag at that instant (three of those tags were minted during the bed run). The exit is named: task-13, owner teardown, whose deliverable is exactly the stable assertion. The bed re-runs this cure unblocks (distro-fedora#81, plugin-substrate#27, distro-arch#52) are mine next. Gates: gofmt -l . empty; go vet ./... clean; GOWORK=off go test ./... exit 0; cue-gen 2 steps 0 failed with the reproducibility check green. Agent: cache-identity Assisted-by: DeepSeek Harness deepseek-v4.1-flash (analysed on a live system)
… writes, progress)
`GitClient.WarmUp` prefetched a whole corpus with a per-fetch bound and nothing else. Three defects,
all measured shape-of-code, one of them the resolution stall `charly#650` root-caused:
1. NO TOTAL CEILING. `git ls-remote` carries its own 30s deadline (gitOpMetadataTimeout), so one
stalled repo cannot hang forever — but a pool that dispatches N repos with no phase bound runs for
(len(repos)/workers) x per-fetch-bound, unbounded in the corpus size. That is the phase the issue
saw parked at "fetching git metadata for 194 repo(s)" with no way to tell progress from a hang.
`warmUpTotalTimeout` (2m, a var so tests shorten it) now bounds the WHOLE phase; a worker that
finds the deadline passed stops, and the phase says how many of how many it reached.
2. A FULL CACHE WRITE PER ANSWER. Every LatestTag/DefaultBranch called save(), i.e. a whole per-host
YAML read-modify-write under an advisory file lock — ~388 whole-file rewrites for a 194-repo
corpus, to record 388 small answers. The pool now suppresses writes (an ATOMIC flag: save() is
called from LatestTag/DefaultBranch while they HOLD mu, so a lock inside save() would deadlock)
and performs ONE write at the end.
3. NO PROGRESS. The phase announced its start and then went silent until it finished. It now reports
the warmed count, so a partial run is visible in the log.
A repo the total bound cuts off is NOT silently dropped: the on-demand path re-probes it (the
offline-fallback contract — a fresh process always re-probes a tag list), so the only cost is that
the first load pays it, which is exactly what the log now says.
R7: three tests, each verified to FAIL under its own mutation and pass with it —
- TestWarmUp_TotalBoundStopsThePrefetch: 401ms for 40 repos under a 60ms phase bound when the bound
is neutered (the valid mutation keeps the code compiling; removing the deadline checks outright
is a build failure, not a proof);
- TestWarmUp_WritesTheCacheOnce: 7 writes for 3 repos when the coalescing is removed;
- TestWarmUp_ReportsProgress: the warm-up count absent from the log.
Both git-op bounds are SHORTENED in the fixture (5s) and the phase bound to 60ms, so each mutation is
a real failure rather than a pass that proves nothing.
Two test seams added for that hermeticity, both the same shape as the existing gitLatestTagFetch:
warmUpSaveFn (counts WRITES, not calls — it is invoked after the suppression check) and
gitDefaultBranchFn.
gofmt -l . empty; go vet ./... clean; GOWORK=off go test ./... exit 0.
Agent: cache-identity
Assisted-by: DeepSeek Harness ollama-cloud/deepseek-v4.1-flash (analysed on a live system)
…rly/spec into feat/c2-content-identity
Review — PASSI re-derived every claim in the body against the seven diffs and the commit list. The change is coherent and the new tests are meaningful. Correctness (diff vs body)
Non-blocking observations
CommentsThere are zero comments on this PR, so there is no maintainer sign-off to weigh; the change stands on its own evidence. No comment asserts a stale fact to re-derive. Security / scopeNo secrets, no injection surface, no new dependency. The dead keys are swept from schema, generated type, and writer in the same change; no shim or dual path remains. The remaining Verdict: PASS ollama-cloud/deepseek-v4.1-flash — action-review. |
Review — BLOCKHead SHA: Change class: Go module code + CUE schema (library Blocks
Comment dispositions
No maintainer-set account ( Verdict: BLOCK ollama-cloud/deepseek-v4.1-flash — action-review. |
The previous attempt was a REST re-run, which replays the ORIGINAL event payload and therefore re-read the pre-edit body (it still reported the sweep evidence as missing and the ResolveRef removal as undisclosed, both of which this branch's PR body now carries). A new head is the way to make the gate read the body GitHub is actually serving. Empty by design: it moves the head, not the tree. Assisted-by: DeepSeek Harness deepseek-v4.1-flash (documentation reviewed)
Review — PASSHead SHA: Security screen: PASS — no secrets/egress, no new dependency or pin change, no gate weakened; the CUE-schema edit is evaluated as a diff and is a hard-cutover deletion (no shim/dual path). Change class: Go library ( Comment dispositions (re-derived against the current state)
Non-blocking notes
Verdict: PASS ollama-cloud/deepseek-v4.1-flash — action-review. |
Summary
Carry-forward of #205 (auto-closed at 5 verdicts; its thread says the work is not
abandoned). This is the same branch, carried forward unchanged from the abandoned PR — its original head was
8c2a6fce(a prefix, quoted as a prefix), and this PR's own head is the one GitHub shows at the top— re-opened as a clean PR. Nothing was rewritten, rebased or force-pushed; the branch was
already on
originand was not modified for this PR.What the change does: it bounds the git-metadata prefetch in the refs cache and makes the
written cache agree with the closed CUE schema.
schema/cache.cueresolved_refsanddownloadskeys from#GitCacheand states, in the def's own doc, why a persisted resolution (an answer beside a clock, not a content digest) is the defect the resolved-ref identity cutover removed — with the issue references (opencharly/charly#715,#530).spec/cue_types_gen.goTestGenReproducibleenforces that a clean regeneration is a no-op and it passes on this head.refs/git_client.goresolved_refs/downloadskeys are no longer written (the cache writer rebuilds thecache:node frombypass/latest_tags/default_branchesonly).refs/cache.gorefs/git_client.go(removal)func (g *GitClient) ResolveRef(repoURL, ref string) (string, error). It had no production call site left (org-wide, see the sweep below); the primitiveGitResolveRefremains. This is a REMOVED exported API of an importable package and is disclosed again under Breaking change + rollback.refs/git_warmup_bound_test.goTestWarmUp_TotalBoundStopsThePrefetch,TestWarmUp_WritesTheCacheOnce,TestWarmUp_ReportsProgress— each fails without the corresponding mechanism.refs/git_download_validity_test.godownloadskey.refs/git_client_test.goresolved_refs/downloads, noversion:stamp).Two commits inside the branch carry the subject
## Summary(e626eb3,ad38137) — abody-pasted-into-the-subject slip from the previous PR round. The content of both is
legitimate change, they are inside an already-published branch, and the squash on
maincollapses them, so this PR neither rewrites them nor renames them.
How tested
Sweep evidence (R5 / CONC(d)) — commands actually executed on this head
Every absence/sweep claim in this body is backed by output pasted from a command run against
this PR's head, not described. The head is quoted as a prefix.
resolved_refssurvives as no live usage — only as tombstone comments (3 hits, all comments):The exported method
(*GitClient).ResolveRefis gone on this head and present on the base:downloadsnow names an IN-PROCESS memo field, not a persisted cache key:CacheConfighas no reference outside this repo's own tree:All three commands were run in a worktree checked out at the exact head
the committed tree of this PR's head (the SHA GitHub shows at the top), on the tree itself:
refsruns against real git repositories on disk (10.5 s), andschema/specinclude thegeneration-reproducibility test; both pass.
golangci-lintis not installed in thisenvironment, so the CI lint arm is left to CI — it is not claimed as run here.
Rulebook compliance
git-workflow(landing mechanics), plus the repo's own AGENTS.md, which declares build/test/gofmt as
this module's gate.
/charly-internals:goand/charly-internals:plugingovern a schemachange; the change is CUE-first (the schema is edited and the Go type is regenerated, not
hand-transcribed).
gate declares no other arm. Nothing was found to file, so there is no
## Filed issuessection. The two
## Summarycommit subjects are disclosed in the Summary above ratherthan silently inherited.
writer in the same change, rather than left in the schema with the client ignoring them.
by a total ceiling and its writes are coalesced, which is the opposite of a workaround.
invariant in the place that owns it.
resolved_refs/downloadssurvive in noCUE def, no generated type and no writer on this head, and the surviving mentions are the
doc comment that records WHY they are gone plus its generated projection.
git statuswas clean before anything ran; no force-push, no history rewrite, nohook bypass; the head is the branch's published head and this PR changes nothing in it.
TestWarmUp_*assertionstarget the ceiling, the single-write behaviour and the progress reporting respectively, and
the download-validity test drives the real provenance-sidecar path.
refstests use real on-disk git repos, notmocks.
worktree-local build or CalVer stamp to reconcile.
gate it does declare (build + test + gofmt, all pasted above) passed on the committed tree.
generated type and the tests move together; no shim, no dual path, no deferred cleanup.
Change classification
Non-runtime: schema + library behaviour, no consumer-visible surface for a host
document. Three facts place the break, and each was read from the artefact rather than
assumed:
charly.yml'scache:section is declared open at the document root —schema/node.cue:167iscache?: _, recognized there only so a document carrying it isnot mis-read as a node named
cache. So a host document that still carriescache: git: resolved_refs:/downloads:loads exactly as before; the closed def#GitCacheis not what judges it.refs.load()unmarshals onlybypass/latest_tags/default_branchesinto an anonymous struct, andrefs.save()rebuilds the whole
cache:node from those three, so the dead keys are dropped on thenext save with no migration step.
(
spec.CacheConfig/spec.GitCacheloseResolvedRefs/Downloads), and a JSON payloaddecoded against that definition would now be rejected by
DecodeGeneratedJSON'sDisallowUnknownFields. Measured: no file incharlyorsdkreferencesCacheConfig,and
refsuses its own YAML structs — so there is no in-tree consumer to update.The remaining behavioural change is the one the change is FOR: a warm-up that previously
resolved every ref now stops at its total ceiling and writes the cache once.
Breaking change + rollback: there are TWO breaks, and both are named here.
(1) The generated
spectype — theResolvedRefs/Downloadsfields no longer exist, so aplugin that decoded
#GitCachefrom JSON would now reject those keys.(2) A removed exported method —
func (g *GitClient) ResolveRef(repoURL, ref string) (string, error)is deleted fromrefs/git_client.go. Any external caller of that exported APIstops compiling; the sweep below shows no production call site inside this org, and the
primitive
GitResolveRef— the function that does the work — is NOT removed.Plus the bounded prefetch, which stops a warm-up at its ceiling instead of resolving every ref. Nothing breaks for a host
charly.yml(open at theroot) and nothing was migrated or deleted on disk. The rollback is a plain revert of this PR:
the two defs are additive to the schema, a cache file written without them still loads, and
re-adding them restores the previously accepted generated type.
Agent: exec-a (charly, DeepSeek Harness agent team — cleanup of session branches, group A)
Assisted-by: DeepSeek Harness deepseek-v4.1-flash (fully tested and validated)