Skip to content

feat: in-repo apko/melange image + leyline-schema v0.2.1 consumer#370

Merged
jamestexas merged 3 commits into
mainfrom
feat/v0.2.1-image-and-leyline-schema
May 11, 2026
Merged

feat: in-repo apko/melange image + leyline-schema v0.2.1 consumer#370
jamestexas merged 3 commits into
mainfrom
feat/v0.2.1-image-and-leyline-schema

Conversation

@jamestexas
Copy link
Copy Markdown
Contributor

@jamestexas jamestexas commented May 11, 2026

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 via task imagemache.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/), consumes ley-line-open/clients/go/leyline-schema@v0.2.1 instead. Net +163 / -4267.
  • 7723124 [mache-172a50] — review fixups (Copilot): keygen regenerates when either half of the keypair is missing; reproducibility claim qualified; stale bindings.BindingRecord doc comment updated.

Cross-runtime contract on the schema swap is verified upstream — LLO's binding/binding_test.go decodes the same tests/fixtures/binding-record-{minimal,realistic}.bin fixtures 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-2aab1e was closed earlier as obsolete — the wire-format design question it asked is now answered by LLO publishing capnp + Go bindings.)

Test plan

  • task lint0 issues
  • task test — full suite green (exit=0)
  • task image — image builds via melange + apko, produces mache.tar
  • git grep audit: zero stale references to schemas/{common,binding}.capnp, internal/lsp/bindings, internal/leyline/schema, or the retired task gen-bindings
  • docs/ARCHITECTURE.md updated — schema-vendoring section rewritten, key-file-reference table row swapped
  • CI must be green on feat/v0.2.1-image-and-leyline-schema

jamestexas and others added 2 commits May 10, 2026 17:24
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.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 11, 2026

find_smells (advisory)

Scoped to files changed in this PR. Rules below run on the standalone (no-LLO) cross-ref tables; _ast rules (cyclomatic_complexity, long_function, long_file, magic_int_in_comparison) are not exercised here.

No structural smells in changed files. ✓

Rules: see docs/ARCHITECTURE.md for the full registry. Advisory only — these are heuristics, not gates.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

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.yaml and a new task image to build mache.tar.
  • Replace internal/lsp/bindings + schemas/*.capnp with github.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 external leyline-schema/binding package. 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.

Comment thread Taskfile.yml Outdated
Comment thread Taskfile.yml
Comment thread README.md Outdated
- 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`.
@jamestexas jamestexas merged commit a5c4cf0 into main May 11, 2026
15 checks passed
@jamestexas jamestexas deleted the feat/v0.2.1-image-and-leyline-schema branch May 11, 2026 01:33
jamestexas added a commit that referenced this pull request May 11, 2026
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"
jamestexas added a commit that referenced this pull request May 11, 2026
…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.
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