From 6c3a7dbb928c802d06f45db36acfeb52cf99f236 Mon Sep 17 00:00:00 2001 From: Lan Nguyen Si Date: Thu, 30 Jul 2026 19:58:19 +0200 Subject: [PATCH 1/2] feat(memory-sync): committed linux profile for the third machine Linux desktop (WSL2, user lan) joins the pandora memory pool as the third spoke, live and verified on the machine since 2026-07-30. Filled in from linux.example.json; carries the machine-state syncPaths entry the template does not yet include (deviation documented in machine-setup.md section f), so the machine is visible to the toolchain-parity check. Refs: agent-tasks ff76c32e Co-Authored-By: Claude Fable 5 --- .../agent-memory-sync/profiles/linux.json | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 packages/agent-memory-sync/profiles/linux.json diff --git a/packages/agent-memory-sync/profiles/linux.json b/packages/agent-memory-sync/profiles/linux.json new file mode 100644 index 0000000..91a54bb --- /dev/null +++ b/packages/agent-memory-sync/profiles/linux.json @@ -0,0 +1,45 @@ +{ + "//": [ + "Machine profile: Linux desktop (WSL2, desktop-9tdlgar) — fallback machine.", + "The Mac mini is the single source of truth (see mac-mini.json); this", + "machine pulls via the periodic sync timer and pushes debounced snapshots", + "via `watch`. Filled in from linux.example.json — read that template's", + "'//' field for the full rationale on every field below.", + "", + "repositorySubdir MUST stay the fixed literal 'pandora', identical on", + "every machine sharing the remote — a per-machine value silently", + "partitions the bare repo into disjoint trees (happened before, caught by", + "a live cross-machine E2E test). See the template / machine-setup.md.", + "", + "rootDir is this machine's own Claude Code slug, read off", + "`ls ~/.claude/projects/` HERE (encodes OS user 'lan' + checkout path", + "/home/lan/git/pandora) — never copied from another machine's profile.", + "", + "Deviation from linux.example.json, deliberate: a third syncPaths entry", + "for machine-state (absolute source ~/.harness/machine-state), which the", + "template does not yet carry (noted in machine-setup.md section f) —", + "without it this machine is invisible to the toolchain-parity check.", + "Producer: `harness session-start toolchain-parity`; convention is", + "owner-writes-only, one file per machine (machine-state/linux.json).", + "", + "frictions producer: friction-log sync_export in", + "~/.config/friction-log/config.yml writes ~/.harness/frictions/linux.json.", + "", + "Activation: pass BOTH --config (this file) and the profile name", + "positionally:", + " agent-memory-sync run linux --config profiles/linux.json" + ], + "profile": "linux", + "rootDir": "/home/lan/.claude/projects/-home-lan-git-pandora/memory", + "remoteUrl": "mini:~/memory-sync/pandora-memory.git", + "branch": "main", + "repositorySubdir": "pandora", + "stateDir": "/home/lan/.agent-memory-sync/linux", + "conflictStrategy": "inline-markers", + "reachabilityTimeoutMs": 10000, + "syncPaths": [ + { "source": ".", "destination": "memory", "kind": "directory" }, + { "source": "/home/lan/.harness/machine-state", "destination": "machine-state", "kind": "directory" }, + { "source": "/home/lan/.harness/frictions", "destination": "frictions", "kind": "directory" } + ] +} From 166e1d57f9ecf8a8923cd9671b75e3e7d3cee7f1 Mon Sep 17 00:00:00 2001 From: Lan Nguyen Si Date: Thu, 30 Jul 2026 20:06:13 +0200 Subject: [PATCH 2/2] test+docs: cover linux.json in the profile guards and setup doc Review findings from the linux-profile commit: the cross-machine profile guard test pinned an explicit file list, so the new committed profile was invisible to the exact invariant test that guards against the disjoint-tree incident; the three profile enumerations in machine-setup.md went stale the same way. Also restates the load-bearing invariants inline in the profile's comment field and documents the reachabilityTimeoutMs deviation from the template. Refs: agent-tasks ff76c32e Co-Authored-By: Claude Fable 5 --- packages/agent-memory-sync/docs/machine-setup.md | 16 +++++++++------- packages/agent-memory-sync/profiles/linux.json | 16 +++++++++++----- .../integration/cross-machine-profiles.test.ts | 13 +++++++------ 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/packages/agent-memory-sync/docs/machine-setup.md b/packages/agent-memory-sync/docs/machine-setup.md index a3970b5..3947814 100644 --- a/packages/agent-memory-sync/docs/machine-setup.md +++ b/packages/agent-memory-sync/docs/machine-setup.md @@ -77,7 +77,8 @@ This document wires together the pieces already documented individually must be running on every fallback machine. Machine-specific values (paths, SSH alias) are committed as profile files -under `profiles/`: `profiles/macbook.json`, `profiles/mac-mini.json`, and a +under `profiles/`: `profiles/macbook.json`, `profiles/mac-mini.json`, +`profiles/linux.json` (the Linux desktop set up via (c) below), and a template for further machines, `profiles/linux.example.json`. Each profile file documents its own placeholders and the namespace-divergence gotcha in its `"//"` field — read the profile you're activating before copying @@ -331,7 +332,8 @@ bullet at the top of this document. ## e) machine-state payload (toolchain snapshots) -Every committed profile (`profiles/mac-mini.json`, `profiles/macbook.json`) +Every committed machine profile (`profiles/mac-mini.json`, +`profiles/macbook.json`, `profiles/linux.json`) carries a **second, independent** `syncPaths` entry alongside the `memory` one described at the top of this document: @@ -399,11 +401,11 @@ exist ahead of any sync. ## f) frictions payload (friction-log exports) Every committed profile (`profiles/mac-mini.json`, `profiles/macbook.json`, -`profiles/linux.example.json`) also carries an **independent** `syncPaths` -entry pointing at `~/.harness/frictions` — the third entry in -`mac-mini.json`/`macbook.json` (after `memory` and `machine-state`), and -the second in `linux.example.json` (that template does not yet include a -`machine-state` entry): +`profiles/linux.json`, `profiles/linux.example.json`) also carries an +**independent** `syncPaths` entry pointing at `~/.harness/frictions` — the +third entry in `mac-mini.json`/`macbook.json`/`linux.json` (after `memory` +and `machine-state`), and the second in `linux.example.json` (that template +does not yet include a `machine-state` entry): ```json { "source": "/Users//.harness/frictions", "destination": "frictions", "kind": "directory" } diff --git a/packages/agent-memory-sync/profiles/linux.json b/packages/agent-memory-sync/profiles/linux.json index 91a54bb..274f6a8 100644 --- a/packages/agent-memory-sync/profiles/linux.json +++ b/packages/agent-memory-sync/profiles/linux.json @@ -1,10 +1,16 @@ { "//": [ - "Machine profile: Linux desktop (WSL2, desktop-9tdlgar) — fallback machine.", - "The Mac mini is the single source of truth (see mac-mini.json); this", - "machine pulls via the periodic sync timer and pushes debounced snapshots", - "via `watch`. Filled in from linux.example.json — read that template's", - "'//' field for the full rationale on every field below.", + "Machine profile: Linux desktop (WSL2, DESKTOP-9TDLGAR) — fallback", + "machine. The Mac mini is the single source of truth (see mac-mini.json);", + "this machine pulls via the periodic sync timer and pushes debounced", + "snapshots via `watch`. Filled in from linux.example.json — read that", + "template's '//' field for the full rationale; the load-bearing invariants", + "restated inline: the memory entry's destination must be the literal", + "'memory', never '.' (a '.' destination is silently unmappable on", + "pull/restore); stateDir must stay OUTSIDE rootDir (the '.' walk would", + "otherwise sync the tool's own state); rootDir/stateDir are absolute,", + "the loader performs no tilde expansion. reachabilityTimeoutMs is 10000", + "like macbook.json (ssh-reached spoke), not the template's 5000.", "", "repositorySubdir MUST stay the fixed literal 'pandora', identical on", "every machine sharing the remote — a per-machine value silently", diff --git a/packages/agent-memory-sync/tests/integration/cross-machine-profiles.test.ts b/packages/agent-memory-sync/tests/integration/cross-machine-profiles.test.ts index c06bfc9..a725393 100644 --- a/packages/agent-memory-sync/tests/integration/cross-machine-profiles.test.ts +++ b/packages/agent-memory-sync/tests/integration/cross-machine-profiles.test.ts @@ -126,16 +126,16 @@ test("macbook and mac-mini profiles share one remote tree and see each other's p ); }); -test("profiles/macbook.json, profiles/mac-mini.json, and profiles/linux.example.json all declare the same repositorySubdir", () => { +test("all committed profiles (macbook, mac-mini, linux, linux.example) declare the same repositorySubdir", () => { // A narrower, faster companion to the end-to-end test above: pins the // specific config field that caused the divergence directly against the // committed files, independent of any CLI/git plumbing. Includes // linux.example.json — the copy-paste source for any new machine — so a // future template edit that reintroduces a per-machine placeholder (as // this template originally had, mirroring the pre-fix macbook/mac-mini - // profiles) is caught here too, not just on the two profiles already in + // profiles) is caught here too, not just on the profiles already in // active use. - const profileFiles = ["macbook.json", "mac-mini.json", "linux.example.json"]; + const profileFiles = ["macbook.json", "mac-mini.json", "linux.json", "linux.example.json"]; const settingsByFile = Object.fromEntries( profileFiles.map((file) => [file, JSON.parse(readText(path.join(PROFILES_DIR, file)))]) ); @@ -196,9 +196,10 @@ test("profiles/macbook.json, profiles/mac-mini.json, and profiles/linux.example. } // Same pin for the pre-existing machine-state entry, closing the identical - // #64 coverage gap — macbook.json and mac-mini.json only, since - // linux.example.json's template has never carried a machine-state entry. - for (const file of ["macbook.json", "mac-mini.json"]) { + // #64 coverage gap — every committed machine profile, but not + // linux.example.json, since the template has never carried a machine-state + // entry (linux.json adds it deliberately; see machine-setup.md section f). + for (const file of ["macbook.json", "mac-mini.json", "linux.json"]) { const machineStateEntries = findEntriesByDestination(settingsByFile[file].syncPaths, "machine-state"); assert.equal( machineStateEntries.length,