Refuse a declaration for the other mode, or older than the mesh's last, and say what an apply would change first

An operator ran `mesh-host reconcile` on an adopted control-node with twelve
modules assigned. It applied the bundle the host carries — the genesis
declaration, foundation only, converged: recreated the store, failed on the
broker's held port, wrote the converged base filter and started its service,
and stopped at the first failing action. The filter closed the machine for
forty-five minutes. The host reported the node adopted in every report, the
declaration said converged, and nothing compared the two; nothing was printed
before acting (hq issue 104).

The host now records the node's mode — from every declaration the mesh sends,
and at genesis from what the operator said — and refuses, at the point of
application, a declaration that says the other mode, naming both and the act
that changes it. Only a declaration the link delivers, signed, changes the
mode: that is how `converge` and `adopt` arrive, so the flip still works and
nothing else can do it. Genesis marks the bundle consumed, with the digest of
what it applied, so `reconcile` holds a node the mesh has spoken to against
what the mesh last said and never the bundle, and refuses the carried bytes
when they are not what genesis applied. A file is refused when it is not what
the mesh last said: a declaration carries no sequence and no issued-at, so the
host cannot tell older from newer, and says so. Both commands print what they
would change — a hold, a removal, an action named as one — before touching
anything, and --dry-run is that list and nothing more.
This commit is contained in:
2026-09-23 23:15:28 +02:00
parent 9176aea6c4
commit 27c4b765b2
14 changed files with 914 additions and 34 deletions
+231 -27
View File
@@ -15,6 +15,7 @@ import (
"errors"
"flag"
"fmt"
"io"
"os"
"os/signal"
"path/filepath"
@@ -53,7 +54,8 @@ const usage = `mesh-host — the node host
profile what this machine can be asked to do
inventory what this machine is, and what it holds
apply FILE make this machine match a declaration from a file
reconcile make this machine match the declaration this host carries
reconcile make this machine match what the mesh last told it — or, before any
mesh has, the bundle this host carries
bundle show what this host carries
owned what this host has applied and still owns
version
@@ -61,7 +63,10 @@ const usage = `mesh-host — the node host
--json machine-readable output
--timeout how long any single probe may take (default 10s)
--state where this node keeps what it knows (default /var/lib/mesh-host/state.json)
--dry-run read the declaration and refuse it if wrong, but change nothing
--dry-run say what applying would change, and change nothing
Both apply and reconcile say what they would change before changing anything, and refuse a
declaration for the other mode than this node is in, or one older than what the mesh last said.
It connects to nothing and listens on nothing. What it applies comes from a file.
`
@@ -90,6 +95,8 @@ type options struct {
nodeName string
dryRun bool
file string
// out is where what a command says goes. Stdout, and a buffer under test.
out io.Writer
}
// parseArgs takes the subcommand first, then its flags.
@@ -99,7 +106,7 @@ type options struct {
// passed, silently ignored, with a successful exit. That is the fault this whole project keeps
// naming, so the parser takes the subcommand off the front and parses what follows.
func parseArgs(args []string) (string, options, error) {
opts := options{timeout: 10 * time.Second, state: store.DefaultPath}
opts := options{timeout: 10 * time.Second, state: store.DefaultPath, out: os.Stdout}
command := ""
if len(args) > 0 {
@@ -184,18 +191,14 @@ func run(ctx context.Context, command string, opts options) error {
if err != nil {
return err
}
return runApply(ctx, opts, d, opts.file)
return runApply(ctx, opts, d, raw, fromFile)
case "reconcile":
// The first node's path. novox/hq ADR 0004: no mesh reachable means the declaration
// comes from the bundle the host carries. There is no link yet, so this is currently
// the only source — which is a stage, not a design, and saying so beats implying the
// other source exists.
d, err := bundle.Load(builtFor)
d, raw, from, err := reconcileSource(opts)
if err != nil {
return err
}
return runApply(ctx, opts, d, "the carried bundle")
return runApply(ctx, opts, d, raw, from)
case "bundle":
if bundle.IsEmpty(builtFor) {
@@ -247,8 +250,10 @@ func run(ctx context.Context, command string, opts options) error {
}
}
func writeJSON(v any) error {
enc := json.NewEncoder(os.Stdout)
func writeJSON(v any) error { return writeJSONTo(os.Stdout, v) }
func writeJSONTo(w io.Writer, v any) error {
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
return enc.Encode(v)
}
@@ -305,20 +310,199 @@ func writeInventory(inv inventory.Inventory) {
}
}
// runApply reads a declaration and makes the machine match it.
// provenance is where a declaration a command applies came from. It decides the origin its
// resources are recorded under, what it is held against, and what is recorded once it applied.
type provenance int
const (
// fromFile is `apply FILE`: handed to the host by someone already running it as root.
fromFile provenance = iota
// fromBundle is `reconcile` on a machine the mesh has told nothing yet: the bundle carried in
// the binary, the first node's path before its mesh is up (novox/hq ADR 0004).
fromBundle
// fromDeclared is `reconcile` on a node the mesh has spoken to: what it last said, kept
// signed beside the state (declared.json), verified again before it is applied.
fromDeclared
)
// reconcileSource is which declaration `reconcile` holds this machine to.
//
// **What the mesh last said, never the bundle, once the mesh has said anything** (novox/hq issue
// 104). The bundle is right at genesis and stale a minute later — genesis rewrites it for the
// machine before applying it, and every declaration since came from the controller — and on an
// adopted node it is a converged declaration for a machine that is not converged. A node that
// has been told something and cannot prove it is the mesh's is refused, with the reason; the
// bundle is not applied in its place.
func reconcileSource(opts options) (*declaration.Declaration, []byte, provenance, error) {
path := store.DeclaredPath(opts.state)
_, err := store.ReadDeclared(path)
switch {
case err == nil:
mine, err := identity.Load(identity.Path(opts.state))
if err != nil {
return nil, nil, 0, fmt.Errorf("this node was told a declaration by the mesh, kept at %s, "+
"and cannot prove it is the mesh's: %w\n\nIt is not applied unproven, and the bundle "+
"this host carries is not applied in its place: that was consumed at genesis", path, err)
}
raw, err := store.LoadDeclared(path, mine.Membership.Signer)
if err != nil {
return nil, nil, 0, fmt.Errorf("%w\n\nThe bundle this host carries is not applied in its "+
"place: that was consumed at genesis", err)
}
d, err := declaration.Parse(raw)
if err != nil {
return nil, nil, 0, err
}
return d, raw, fromDeclared, nil
case errors.Is(err, store.ErrNothingDeclared):
d, err := bundle.Load(builtFor)
if err != nil {
return nil, nil, 0, err
}
return d, bundle.Raw(builtFor), fromBundle, nil
default:
return nil, nil, 0, err
}
}
func (p provenance) origin() string {
if p == fromDeclared {
return store.OriginDeclared
}
// A file handed to the host is applied as the bundle is: what it puts here is invisible to
// the removal pass of a declaration that later arrives from the mesh (novox/hq issue 010).
return store.OriginCarried
}
func (p provenance) name(opts options) string {
switch p {
case fromBundle:
return "the carried bundle"
case fromDeclared:
return "what the mesh last told this node (" + store.DeclaredName + ")"
}
return opts.file
}
// short is a digest as a person reads one aloud.
func short(digest string) string {
if len(digest) > 12 {
return digest[:12]
}
return digest
}
// refuseStale refuses a declaration that is not the one this node should be held to (novox/hq
// issue 104), naming both.
//
// **A declaration carries no order.** The controller signs a version — the vocabulary — the
// node it is for, the mode, and the resources: no sequence, no issued-at. What exists is
// identity: the mesh names what it sends by the digest of the bytes, the node keeps the last one
// it was sent, and genesis records the digest of what it consumed. So "older" can be said of
// exactly two things — the bundle genesis consumed, and a bundle other than the one it consumed —
// and of anything else only that it is not what the mesh last said, which is refused too: a
// host that cannot tell older from newer and applies anyway is the fault this issue names.
func refuseStale(known store.State, kept store.Declared, keptErr error, digest string, from provenance) error {
switch from {
case fromDeclared:
return nil
case fromBundle:
if known.Genesis == nil || known.Genesis.Digest == digest {
return nil
}
how := ""
if known.Genesis.Rewritten {
how = ", rewritten for this machine — its foundation ports, root credentials and mode"
}
return fmt.Errorf("the bundle this host carries (%s) is not the one genesis applied here on %s "+
"(%s%s). The bundle was consumed then, and nothing the mesh has said is kept on this node "+
"yet, so there is nothing to reconcile against: enrol this node, or push to it from the "+
"controller", short(digest), known.Genesis.At.Format(time.RFC3339), short(known.Genesis.Digest), how)
}
// A file.
switch {
case errors.Is(keptErr, store.ErrNothingDeclared):
// The mesh has said nothing here. Nothing to be older than.
return nil
case keptErr != nil:
return fmt.Errorf("%w\n\nNothing is applied over what this node cannot read back", keptErr)
}
last := apply.DigestOf(kept.Declaration)
if digest == last {
return nil
}
if known.Genesis != nil && digest == known.Genesis.Digest {
return fmt.Errorf("this file is the bundle genesis consumed on %s (%s), and the mesh has since told "+
"this node declaration %s, kept as %s. An older declaration is not applied over a newer one: "+
"`reconcile` applies what the mesh last said", known.Genesis.At.Format(time.RFC3339), short(digest),
short(last), store.DeclaredName)
}
return fmt.Errorf("this file (%s) is not the declaration the mesh last told this node (%s, kept as %s). "+
"A declaration carries no sequence and no issued-at, so this host cannot tell an older one from a "+
"newer, and it applies only what the mesh last said: `reconcile` applies that, and `push` from the "+
"controller changes it", short(digest), short(last), store.DeclaredName)
}
// applied is what an apply says in machine-readable form: what it planned, then what it did.
type applied struct {
Plan []apply.Step `json:"plan"`
Report apply.Report `json:"report"`
}
// runApply makes the machine match a declaration — after refusing one this node must not apply,
// and after saying what it would change.
//
// Refused first, and before anything is read from the machine: a declaration for the other
// mode than this node is in, or one older than what the mesh last said (novox/hq issue 104).
// Then the plan, printed whole before a single resource is touched; `--dry-run` is that and
// nothing more.
//
// The state is loaded before anything is touched and saved after, including when the apply
// fails part-way: what was applied before the failure is on the machine, and a host that did
// not record it would believe it owns less than it does and leave that behind forever.
func runApply(ctx context.Context, opts options, d *declaration.Declaration, source string) error {
func runApply(ctx context.Context, opts options, d *declaration.Declaration, raw []byte, from provenance) error {
out := opts.out
if out == nil {
out = os.Stdout
}
known, err := store.Load(opts.state)
if err != nil {
return err
}
source, origin, digest := from.name(opts), from.origin(), apply.DigestOf(raw)
// The mode this node is in. Recorded in the state since this check existed; a state written
// before then has it in what the mesh last said, which this node kept.
kept, keptErr := store.ReadDeclared(store.DeclaredPath(opts.state))
if known.Mode == "" && keptErr == nil {
if last, err := declaration.Parse(kept.Declaration); err == nil {
known.Mode = apply.ModeOf(last)
}
}
if err := apply.CheckMode(known, d); err != nil {
return err
}
if err := refuseStale(known, kept, keptErr, digest, from); err != nil {
return err
}
// Said before anything is done.
steps := apply.Plan(d, known, origin)
if opts.json && opts.dryRun {
return writeJSONTo(out, steps)
}
mode := known.Mode
if mode == "" {
mode = "in no mode yet — nothing has said one"
}
fmt.Fprintf(out, "%s (%s): %d resource(s), version %d\n", source, short(digest), len(d.Resources), d.Version)
fmt.Fprintf(out, "this node is %s; the declaration says %s\n", mode, apply.ModeOf(d))
fmt.Fprintf(out, "\nwould change, in this order:\n")
for _, step := range steps {
fmt.Fprintln(out, " "+step.String())
}
if opts.dryRun {
fmt.Printf("%s: %d resource(s), version %d — accepted, nothing applied\n",
source, len(d.Resources), d.Version)
fmt.Fprintf(out, "\n--dry-run: nothing applied\n")
return nil
}
@@ -338,12 +522,20 @@ func runApply(ctx context.Context, opts options, d *declaration.Declaration, sou
return err
}
report, updated, applyErr := apply.Apply(ctx, sys, d, known, store.OriginCarried,
fmt.Fprintf(out, "\napplying:\n")
report, updated, applyErr := apply.ApplyKeeping(ctx, sys, d, known, origin,
apply.ExecRunner, func(line string) {
if !opts.json {
fmt.Println(line)
fmt.Fprintln(out, line)
}
}, sealOpener(opts.state))
}, sealOpener(opts.state), apply.KeepIn(filepath.Dir(opts.state)))
// What this apply settles about the node, whichever way it went. The mode is what the
// declaration said and the check above agreed with; the bundle, once applied, is consumed.
updated.Mode = apply.ModeOf(d)
if from == fromBundle {
updated.Genesis = &store.Genesis{Digest: digest, At: time.Now().UTC()}
}
// Saved whichever way it went. Recording only on success would lose the footprint of a
// failed apply, and that footprint is on the machine either way.
@@ -380,13 +572,13 @@ func runApply(ctx context.Context, opts options, d *declaration.Declaration, sou
}
if opts.json {
return writeJSON(report)
return writeJSONTo(out, applied{Plan: steps, Report: report})
}
if !report.Changed() {
fmt.Printf("%s: already matches — %d resource(s) checked\n", source, len(report.Outcomes))
fmt.Fprintf(out, "%s: already matches — %d resource(s) checked\n", source, len(report.Outcomes))
return nil
}
fmt.Printf("%s: applied — %d resource(s)\n", source, len(report.Outcomes))
fmt.Fprintf(out, "%s: applied — %d resource(s)\n", source, len(report.Outcomes))
return nil
}
@@ -838,6 +1030,19 @@ func applyAndKeep(ctx context.Context, opts options, raw []byte, signed *store.D
return link.Report{Refused: err.Error()}
}
known, err := store.Load(opts.state)
if err != nil {
return link.Report{Refused: err.Error()}
}
// A declaration the link delivered is the controller's word on the node's mode — the flip
// arrives as exactly that, the first converged declaration after adopted ones — and becomes
// the record. What this node re-applies on its own is held to the record (novox/hq issue 104).
if signed == nil {
if err := apply.CheckMode(known, declared); err != nil {
return link.Report{Refused: err.Error()}
}
}
built, err := system.For(builtFor)
if err != nil {
return link.Report{Refused: err.Error()}
@@ -846,11 +1051,6 @@ func applyAndKeep(ctx context.Context, opts options, raw []byte, signed *store.D
return link.Report{Refused: err.Error()}
}
known, err := store.Load(opts.state)
if err != nil {
return link.Report{Refused: err.Error()}
}
if err := built.Confirm(ctx, apply.ExecRunner); err != nil {
return link.Report{Refused: err.Error()}
}
@@ -860,6 +1060,10 @@ func applyAndKeep(ctx context.Context, opts options, raw []byte, signed *store.D
outcome, updated, applyErr := apply.ApplyKeeping(ctx, built, declared, known, store.OriginDeclared,
apply.ExecRunner, nil, sealOpener(opts.state), apply.KeepIn(filepath.Dir(opts.state)))
// The mode the mesh said, recorded whichever way the apply went: the declaration is kept
// either way, and the node is held to it from the next reconcile (novox/hq ADR 0100).
updated.Mode = apply.ModeOf(declared)
// Saved whichever way it went. Recording only on success would lose the footprint of a
// failed apply, and that footprint is on the machine either way.
if saveErr := store.Save(opts.state, updated); saveErr != nil {
+207
View File
@@ -1,14 +1,20 @@
package main
import (
"bytes"
"context"
"crypto/ed25519"
"errors"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/novox/mesh-host/internal/apply"
"github.com/novox/mesh-host/internal/bundle"
"github.com/novox/mesh-host/internal/declaration"
"github.com/novox/mesh-host/internal/identity"
"github.com/novox/mesh-host/internal/link"
"github.com/novox/mesh-host/internal/store"
"github.com/novox/mesh-host/internal/system"
@@ -262,3 +268,204 @@ func TestAChangeThatNeverReachedTheMeshIsSaidAgain(t *testing.T) {
t.Error("a change the mesh was told was said again")
}
}
// Defends novox/hq issue 104: a declaration for the other mode than this node is in is refused at
// the point of application, whichever command delivered it, naming both — an adopted control-node
// once applied its converged genesis bundle and closed itself for forty-five minutes.
func stateWithMode(t *testing.T, mode string) options {
t.Helper()
dir := t.TempDir()
opts := options{state: filepath.Join(dir, "state.json"), out: &bytes.Buffer{}}
if err := store.Save(opts.state, store.State{Mode: mode}); err != nil {
t.Fatal(err)
}
return opts
}
func TestAConvergedDeclarationIsRefusedOnAnAdoptedNode(t *testing.T) {
opts := stateWithMode(t, store.ModeAdopted)
path := filepath.Join(filepath.Dir(opts.state), "filter.conf")
raw := []byte(`{"declaration":1,"resources":[{"id":"filter","type":"file","path":"` + path +
`","content":"table inet filter { chain input { policy drop; } }\n"}]}`)
d, err := declaration.ParseFileTrusted(raw)
if err != nil {
t.Fatal(err)
}
for _, from := range []provenance{fromFile, fromBundle} {
err := runApply(context.Background(), opts, d, raw, from)
if err == nil {
t.Fatalf("a converged declaration was applied to an adopted node (from %d)", from)
}
want := "this node is adopted; the declaration says converged"
if !strings.Contains(err.Error(), want) || !strings.Contains(err.Error(), "`converge`") {
t.Errorf("the refusal does not name both modes and the act that changes it: %v", err)
}
}
if _, err := os.Stat(path); !errors.Is(err, os.ErrNotExist) {
t.Error("the refused declaration touched the machine")
}
}
func TestAnAdoptedDeclarationIsRefusedOnAConvergedNode(t *testing.T) {
// Only the mesh can say a node is adopted, so this one arrives the way a disconnected node
// re-applies what it kept: through the reconcile loop, unsigned.
opts := stateWithMode(t, store.ModeConverged)
raw := []byte(`{"declaration":1,"adoption":{"taken":[]},"resources":[{"id":"a","type":"file","path":"` +
filepath.Join(filepath.Dir(opts.state), "a.conf") + `","content":"x\n"}]}`)
report := applyAndKeep(context.Background(), opts, raw, nil, nil)
want := "this node is converged; the declaration says adopted"
if !strings.Contains(report.Refused, want) || !strings.Contains(report.Refused, "`adopt`") {
t.Errorf("refused = %q, want it to name both modes and the act that changes it", report.Refused)
}
}
func TestTheMeshItselfMayChangeTheMode(t *testing.T) {
// The flip is a declaration: `converge` on the controller records the mode and sends the
// first converged declaration. Delivered by the link, signed, it is not held to the record —
// it becomes it. (With no system linked in, the apply is refused later for that; what this
// checks is that the refusal is not the mode's.)
opts := stateWithMode(t, store.ModeAdopted)
raw := []byte(`{"declaration":1,"resources":[{"id":"a","type":"file","path":"` +
filepath.Join(filepath.Dir(opts.state), "a.conf") + `","content":"x\n"}]}`)
report := applyAndKeep(context.Background(), opts, raw, &store.Declared{Declaration: raw}, nil)
if strings.Contains(report.Refused, "the declaration says") {
t.Errorf("the mesh's own flip was refused for its mode: %s", report.Refused)
}
}
// Defends novox/hq issue 104: a declaration older than what the mesh last said is refused, naming
// both — and a declaration carries no order, so one that is merely not the last is refused too,
// saying what is missing.
func TestAnOlderDeclarationIsRefused(t *testing.T) {
opts := stateWithMode(t, "")
genesis := []byte(`{"declaration":1,"resources":[{"id":"g","type":"file","path":"/tmp/g","content":"genesis\n"}]}`)
since := []byte(`{"declaration":1,"resources":[{"id":"g","type":"file","path":"/tmp/g","content":"since\n"}]}`)
other := []byte(`{"declaration":1,"resources":[{"id":"g","type":"file","path":"/tmp/g","content":"other\n"}]}`)
if err := store.Save(opts.state, store.State{Genesis: &store.Genesis{Digest: apply.DigestOf(genesis),
At: time.Now(), Rewritten: true}}); err != nil {
t.Fatal(err)
}
if err := store.SaveDeclared(store.DeclaredPath(opts.state), store.Declared{Declaration: since,
Signature: []byte("unverified here")}); err != nil {
t.Fatal(err)
}
parsed := func(raw []byte) *declaration.Declaration {
d, err := declaration.ParseFileTrusted(raw)
if err != nil {
t.Fatal(err)
}
return d
}
err := runApply(context.Background(), opts, parsed(genesis), genesis, fromFile)
if err == nil {
t.Fatal("the bundle genesis consumed was applied over what the mesh said since")
}
for _, want := range []string{"older", short(apply.DigestOf(genesis)), short(apply.DigestOf(since))} {
if !strings.Contains(err.Error(), want) {
t.Errorf("the refusal does not say %q: %v", want, err)
}
}
err = runApply(context.Background(), opts, parsed(other), other, fromFile)
if err == nil {
t.Fatal("a declaration that is not what the mesh last said was applied")
}
if !strings.Contains(err.Error(), "no sequence and no issued-at") ||
!strings.Contains(err.Error(), short(apply.DigestOf(since))) {
t.Errorf("the refusal does not say what is missing and what was last said: %v", err)
}
// A bundle other than the one genesis consumed: what genesis applied was rewritten for this
// machine, so the carried bytes never are.
err = runApply(context.Background(), opts, parsed(other), other, fromBundle)
if err == nil || !strings.Contains(err.Error(), "consumed") ||
!strings.Contains(err.Error(), short(apply.DigestOf(genesis))) {
t.Errorf("a bundle other than the consumed one was not refused naming it: %v", err)
}
}
// Defends novox/hq issue 104: what an apply would change is said before anything is, and
// `--dry-run` is that and nothing else — an action listed as the action it is.
func TestADryRunChangesNothingAndListsTheActions(t *testing.T) {
opts := stateWithMode(t, "")
opts.dryRun = true
out := &bytes.Buffer{}
opts.out = out
path := filepath.Join(filepath.Dir(opts.state), "a.conf")
raw := []byte(`{"declaration":1,"resources":[
{"id":"a","type":"file","path":"` + path + `","content":"x\n"},
{"id":"init","type":"action","command":["createdb","mesh"],"verify":["psql","-c","select 1"]}]}`)
d, err := declaration.ParseFileTrusted(raw)
if err != nil {
t.Fatal(err)
}
if err := runApply(context.Background(), opts, d, raw, fromFile); err != nil {
t.Fatalf("a dry run failed: %v", err)
}
if _, err := os.Stat(path); !errors.Is(err, os.ErrNotExist) {
t.Error("a dry run wrote the file")
}
for _, want := range []string{"would change", "create file a", "run action init",
"`createdb mesh`", "--dry-run: nothing applied"} {
if !strings.Contains(out.String(), want) {
t.Errorf("the preview does not say %q:\n%s", want, out.String())
}
}
if strings.Contains(out.String(), "applying:") {
t.Errorf("a dry run went on to apply:\n%s", out.String())
}
}
// Defends novox/hq issue 104: once the mesh has told this node anything, `reconcile` holds it to
// that — never to the bundle the host carries, which genesis consumed.
func TestReconcileAfterAControllerDeclarationDoesNotReapplyTheBundle(t *testing.T) {
was := builtFor
builtFor = "arch"
t.Cleanup(func() { builtFor = was })
opts := stateWithMode(t, store.ModeAdopted)
controllerPublic, controllerPrivate, err := ed25519.GenerateKey(nil)
if err != nil {
t.Fatal(err)
}
said := []byte(`{"declaration":1,"adoption":{"taken":[]},"resources":[{"id":"a","type":"file","path":"/tmp/a","content":"x\n"}]}`)
if err := store.SaveDeclared(store.DeclaredPath(opts.state), store.Declared{Declaration: said,
Signature: ed25519.Sign(controllerPrivate, said)}); err != nil {
t.Fatal(err)
}
// Told, and unable to prove by whom: refused, and the bundle is not applied in its place.
_, _, _, err = reconcileSource(opts)
if err == nil || !strings.Contains(err.Error(), "not applied in its place") {
t.Errorf("a node that cannot prove what it was told fell back to something: %v", err)
}
mine, err := identity.Generate("workstation")
if err != nil {
t.Fatal(err)
}
mine.Membership = identity.Membership{Broker: "198.51.100.10:5671", Fingerprint: "sha256:0",
Signer: controllerPublic, Password: "issued"}
if err := identity.Save(identity.Path(opts.state), mine); err != nil {
t.Fatal(err)
}
d, raw, from, err := reconcileSource(opts)
if err != nil {
t.Fatal(err)
}
if from != fromDeclared || !bytes.Equal(raw, said) || d.Adoption == nil {
t.Errorf("reconcile chose %d with %d bytes, not what the mesh last said", from, len(raw))
}
// And before the mesh has said anything: the bundle, as it always was. A test binary carries
// only the placeholder, and asking for it is what proves the path.
if err := os.Remove(store.DeclaredPath(opts.state)); err != nil {
t.Fatal(err)
}
_, _, _, err = reconcileSource(opts)
if !errors.Is(err, bundle.ErrEmpty) {
t.Errorf("with nothing said, reconcile did not reach for the carried bundle: %v", err)
}
}