feat: in-repo apko/melange image + leyline-schema v0.2.1 consumer#370
Conversation
In-repo OCI build for mache:0.8.0 — no longer depends on kiln. Builds from local Go source via melange (CGO for tree-sitter elixir), packages distroless via apko. FUSE entirely stripped (mache no longer requires kernel privileges). Note: cluster.capnp launches mache with `serve --ipc-socket <path>` which is not yet implemented in cmd/serve.go — tracked in mache-2ac8b6 (serve --ipc-socket capnp framing) and mache-2aab1e (mache↔llo IPC wire). This commit lands the image scaffolding only. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…apnp
LLO published clients/go/leyline-schema as a multi-module Go package
(kubernetes/api style) at v0.2.1. Switch mache from its byte-equal
fork to the authoritative module.
Removed:
- schemas/{common,binding}.capnp + README (source-side forks)
- internal/lsp/bindings/{common,binding}.capnp.go + smoke test
(committed generated artifacts)
- internal/leyline/schema/daemon.capnp + .go
(unused — UDS client work in mache-98aa97 will pick up
leyline-schema/daemon directly)
- Taskfile gen-bindings target
(codegen lives in LLO now, gated by its CI)
Imports flip from internal/lsp/bindings (plural shared pkg) to
leyline-schema/binding (singular, k8s-style one-pkg-per-schema).
common.Range pulled transitively via BindingRecord.RefRange().
Closes mache-3bf414. Follow-up to 0270e15 (mache-172a50); both ship
in the same PR. mache-2aab1e closed earlier as obsolete.
find_smells (advisory)Scoped to files changed in this PR. Rules below run on the standalone (no-LLO) cross-ref tables; No structural smells in changed files. ✓ Rules: see |
There was a problem hiding this comment.
Pull request overview
This PR removes in-repo vendored Cap’n Proto schemas/generated bindings in favor of consuming leyline-schema@v0.2.1, and adds in-repo melange/apko configuration to build a distroless OCI image.
Changes:
- Add
melange.yaml+apko.yamland a newtask imageto buildmache.tar. - Replace
internal/lsp/bindings+schemas/*.capnpwithgithub.com/agentic-research/ley-line-open/clients/go/leyline-schema@v0.2.1. - Update docs and tests to use the external schema package.
Reviewed changes
Copilot reviewed 17 out of 20 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| Taskfile.yml | Adds image task; removes gen-bindings task; extends clean artifacts. |
| apko.yaml | New apko config to assemble a distroless mache OCI image from local APKs. |
| melange.yaml | New melange config to build the mache APK from source with CGO enabled. |
| .gitignore | Ignores apko/melange build artifacts (tar, packages/, signing keys, SBOMs). |
| go.mod / go.sum | Adds dependency on leyline-schema@v0.2.1. |
| internal/lsp/binding_log.go | Switches BindingRecord decoding to external leyline-schema/binding. |
| internal/lsp/binding_log_test.go | Updates tests to build BindingRecord via external schema package. |
| cmd/serve_find_smells_views_test.go | Updates tests to use external BindingRecord constructors. |
| cmd/dead_code_skip_list_retreat_test.go | Updates tests to use external BindingRecord constructors. |
| docs/ARCHITECTURE.md | Updates schema section and key-file-reference table to reflect external schema dependency. |
| README.md | Documents task image flow for building/loading/running the OCI image. |
| schemas/* | Deletes vendored capnp schema files and schemas README. |
| internal/lsp/bindings/* | Deletes generated Go bindings and the bindings smoke test. |
| internal/leyline/schema/* | Deletes vendored daemon capnp schema + generated bindings. |
Comments suppressed due to low confidence (1)
internal/lsp/binding_log.go:27
- The comment still says this is a projection of
bindings.BindingRecord, but the code now imports the externalleyline-schema/bindingpackage. Update the comment/package reference so it matches the new dependency.
"github.com/agentic-research/ley-line-open/clients/go/leyline-schema/binding"
)
// Binding is the Go-native projection of one bindings.BindingRecord.
// Decoupling consumers from the generated capnp types means swapping
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Taskfile `image:` keygen regenerates when EITHER half of the keypair is missing — `melange build` needs the private key, `apko build` needs the .pub for its keyring, so they must exist together. - README qualifies the reproducibility claim — `task image` auto-generates a dev keypair when none exists, which makes APK signatures non-stable across fresh clones unless CI pins a fixed key. - binding_log.go doc comment now names the new package (binding, from leyline-schema) instead of the retired forked `bindings`.
kiln was retired when apko + melange landed in-repo (mache-172a50 / PR #370). The `notify-kiln` job in ci.yml and the kiln dispatch step in release.yml fire OIDC exchanges against an archived repo that no longer accepts dispatches, surfacing as a perpetual SKIPPED check on PRs and a noisy no-op step on releases. - ci.yml: drop the entire `notify-kiln` job - release.yml: drop the `octo-sts` token exchange + `Dispatch to kiln` step; update the file-header comment to reflect ley-line-open as the upstream linkage instead of "notify kiln"
…es (#371) * [mache-98b9bf] feat: route read-only --control mount through UDS, drop ExtractActiveDB on that path mountControl's read-only branch now connects to the ley-line daemon via the existing udsGraph (the same path buildControlGraph uses). Removes: - ExtractActiveDB call on initial mount (used to copy the active arena buffer — ~352MB with _source BLOBs — to a temp .db file) - ExtractActiveDB call on every hot-swap (same cost per root bump) - OpenSQLiteGraph + the in-process SQLite connection it implied - The HotSwapGraph wrapper for this path (each udsGraph query reflects the daemon's current snapshot — hot-swap is implicit; no graph-level swap needed on the mache side) - The watcher goroutine that polled current_root / subscribed to daemon snapshot events and rebuilt the local graph - trySubscribe helper (was only used by that watcher) Writable mount keeps ExtractActiveDB + OpenWritableGraph — the WritableGraph splices edits into a real SQL connection before flushing back to the arena, and the daemon doesn't expose write ops over UDS. That path is unchanged. Also (mache-9051f0): - Auto-download URL constant moved from agentic-research/ley-line (private repo, retired) to agentic-research/ley-line-open. Documents that the bundle path is the canonical deployment and this download flow only runs for non-bundle invocations. - Lift URL into a package constant (leylineReleaseURLTemplate) so it can be regression-tested. - New TestLeylineReleaseURL_PointsAtPublicRepo guards against future drift to the private repo. Docs: - README: split deployment into "Bundle / image (canonical production)" and "Local / dev path" sections, document MACHE_NO_LEYLINE=1 for CI, note that mache itself doesn't implement perimeter auth (orchestrator handles it for the bundle deployment). - ARCHITECTURE.md: add a key-file-reference row for the UDS query proxy explaining the daemon-owns-SQLite zero-copy story. * chore(ci): remove kiln cross-repo dispatch (archived) kiln was retired when apko + melange landed in-repo (mache-172a50 / PR #370). The `notify-kiln` job in ci.yml and the kiln dispatch step in release.yml fire OIDC exchanges against an archived repo that no longer accepts dispatches, surfacing as a perpetual SKIPPED check on PRs and a noisy no-op step on releases. - ci.yml: drop the entire `notify-kiln` job - release.yml: drop the `octo-sts` token exchange + `Dispatch to kiln` step; update the file-header comment to reflect ley-line-open as the upstream linkage instead of "notify kiln" * [mache-98b9bf] fix: address Copilot review on PR #371 - internal/leyline/socket.go: wire MACHE_NO_LEYLINE=1 into the auto-download path so the README claim ("disables leyline auto-download in CI") matches actual behavior. Without leyline on PATH and with MACHE_NO_LEYLINE set, return a clear "install or unset" error instead of attempting a download. - internal/leyline/socket.go: reword the 404 case message — no implicit "falling back" claim (DiscoverOrStart's caller decides what to do; the function itself does not fall back). New wording surfaces the "no release published yet" condition explicitly. - cmd/uds_graph.go: document the GetCallees stub limitation — daemon has no find_callees op yet; (nil,nil) matches existing Graph backends' "no callees" semantics so consumers degrade gracefully. Tracked under mache-75d847 (paired with LLO Bead B). - cmd/uds_graph.go: document the N+1 in ListChildStats and the planned daemon-side list_child_stats fix (mache-75d847 / LLO Bead C). No behavior change for the perf items — fixes are tracked upstream and will land when both sides implement the daemon ops. * [mache-a55625] fix(uds): parse list_children objects, revert mount.go switch pending GetCallees Two corrections to the prior commit on this branch: ## Real bug fixed (mache-a55625): udsGraph parses list_children responses The LLO daemon's list_children op returns `[{id, name, kind, size}, ...]` per child (full stats in one round-trip — rs/ll-open/cli-lib/src/daemon/ops.rs::op_list_children). udsGraph's ListChildren and ListChildStats were doing `c.(string)` on each entry — but each entry is `map[string]any`, not a string. The assertion failed silently, so every non-empty directory listing came back as an empty slice. New listChildrenResponse helper parses the objects properly; both methods now consume the same payload. As a consequence ListChildStats is now single-shot — no more per-child GetNode fan-out — which incidentally closes the previously-suspected N+1 (it was never the right diagnosis: the daemon already returned stats). New tests in cmd/uds_graph_test.go pin the parse contract: - TestListChildren_ParsesObjectsNotStrings — regression guard - TestListChildStats_SingleShotFromListChildrenResponse — asserts no get_node ops fire during a ListChildStats call ## Revert: mount.go control read-only stays on SQLiteGraph The previous commit switched mountControl's read-only branch to udsGraph. That eliminates the 352MB ExtractActiveDB temp-copy on that path, but silently regresses callees-dependent features (/callees vfs, find_impact, find_smells call-graph rules) because the daemon has no find_callees op (base_op_names lists find_callers + find_defs only). Reverted; mount-control read-only continues to use ExtractActiveDB + OpenSQLiteGraph + HotSwapGraph + the watcher goroutine + trySubscribe helper. The eventual switch is tracked under mache-98b9bf, gated on mache-a5e4ea (mache-side GetCallees impl), which is gated on LLO Bead B ley-line-open-a47d7d (daemon-side find_callees op). * fix: address Copilot 2nd review on PR #371 - uds_graph.go: listChildrenResponse now checks the daemon's {ok, error} envelope. Returns graph.ErrNotFound when the error message contains "not found" so readdir on a stale ID surfaces cleanly, matching SQLiteGraph.ListChildren semantics. Wraps other daemon-side errors instead of silently returning an empty slice. - uds_graph_test.go: sawGetNode is now atomic.Bool — the previous bool was written by the stub-daemon goroutine and read by the test goroutine without synchronization, tripping `go test -race`. - README.md: grammar fix — "the unit that a cluster orchestrator deploys" (missing 'that'). - release.yml: header comment was wrong in the kiln-removal commit. The release workflow still links against ley-line FFI artifacts from the private agentic-research/ley-line repo (the public ley-line-open repo carries schemas + daemon, not the linker artifacts). Updated the comment to reflect actual behavior + note the eventual migration path. * [mache-a5e4ea][mache-98b9bf] feat: consume LLO 0.2.2 find_callees op, re-apply mount --control UDS switch LLO 0.2.2 (clients/go/leyline-schema/v0.2.2, daemon op_find_callees in ley-line-open#7) added the daemon-side op the prior revert was gated on. With find_callees in place, the read-only `mount --control` path can finally drop ExtractActiveDB without regressing callees-dependent features. ## udsGraph.GetCallees (mache-a5e4ea) Sends `find_callees(id)` and decodes the daemon's `{callees: [{node_id, source_id}, ...]}` response. Daemon-side SQL is SELECT DISTINCT d.node_id, d.source_id FROM node_refs r JOIN node_defs d ON r.token = d.token WHERE r.node_id = ? Returns `(nil, nil)` for not-found / empty-result, matching SQLiteGraph + MemoryStore semantics so consumers (`/callees` vfs, find_impact, find_smells) degrade silently when a construct has no outgoing edges. Self-edges + duplicates filtered client-side (defense in depth — daemon also de-dups via SELECT DISTINCT). Two new tests pin the wire and dedup behavior: - TestGetCallees_ResolvesViaDaemonFindCalleesOp: asserts the find_callees op fires exactly once and the response is correctly parsed, deduped, and self-edge-filtered. - TestGetCallees_EmptyResultIsNotAnError: daemon `{ok: false}` does not surface as an error. ## mount.go switch re-applied (mache-98b9bf) Read-only `mount --control` now routes through `newUDSGraph` instead of `ExtractActiveDB + OpenSQLiteGraph + HotSwapGraph + watcher`. The 352MB temp-copy is eliminated on this path. Hot-swap is implicit — each udsGraph query reflects the daemon's current snapshot, so root bumps don't need a graph-level swap. Removed: HotSwapGraph wrapping, the watcher goroutine, trySubscribe helper (was only used by the watcher). Writable mount still extracts because WritableGraph needs an in-process SQL connection for splice-on-close. ## go.mod Bumped `leyline-schema` v0.2.1 → v0.2.2 (carries the new daemon schema types FindCalleesRequest/Response, TokenMapEntry, GetRefsMapRequest/Response, etc. — this PR only consumes find_callees, the others are available for future use). * fix: address Copilot 3rd review on PR #371 - cmd/mount.go: replace stat-based socket readiness with dial-loop. The previous loop os.Stat'd the .sock file and then dialed once, which fails immediately on (a) a stale leftover .sock from a dead daemon — file exists but no listener; (b) the bind-then-listen race where the socket file appears before the daemon accepts connections. Loop on newUDSGraph(sockPath) directly until it succeeds or the 30s deadline expires; retries every 100ms. - internal/leyline/socket_test.go: add regression test for MACHE_NO_LEYLINE=1 short-circuiting DiscoverOrStart's auto-download path. Sets HOME → tempdir so the ~/.mache/bin fallback misses; asserts the error names MACHE_NO_LEYLINE and does NOT mention a download attempt (which would mean the env var didn't gate). Suppressed-low-confidence note from the 3rd Copilot review pass.
Summary
Two bead-anchored commits that ship together because they share the same coordination boundary (LLO v0.2.1 + the cluster-readiness image).
0270e15[mache-172a50]— in-repo apko + melange config, retiring the external kiln dep. Image builds viatask image→mache.tar(~33MB, x86_64 + aarch64).47952aa[mache-3bf414]— drops the forked capnp schemas (schemas/{common,binding}.capnp,internal/leyline/schema/daemon.capnp) and their generated bindings (internal/lsp/bindings/), consumesley-line-open/clients/go/leyline-schema@v0.2.1instead. Net +163 / -4267.7723124[mache-172a50]— review fixups (Copilot): keygen regenerates when either half of the keypair is missing; reproducibility claim qualified; stalebindings.BindingRecorddoc comment updated.Cross-runtime contract on the schema swap is verified upstream — LLO's
binding/binding_test.godecodes the sametests/fixtures/binding-record-{minimal,realistic}.binfixtures Rust asserts byte-equality against (T8.10 /ley-line-open-6b7d43), so the wire format is locked from both producer and consumer sides.Closes
mache-172a50,mache-3bf414. (mache-2aab1ewas closed earlier as obsolete — the wire-format design question it asked is now answered by LLO publishing capnp + Go bindings.)Test plan
task lint—0 issuestask test— full suite green (exit=0)task image— image builds via melange + apko, producesmache.targit grepaudit: zero stale references toschemas/{common,binding}.capnp,internal/lsp/bindings,internal/leyline/schema, or the retiredtask gen-bindingsdocs/ARCHITECTURE.mdupdated — schema-vendoring section rewritten, key-file-reference table row swappedfeat/v0.2.1-image-and-leyline-schema