ai-agents builds a dangling memory symlink when a memory-flagged checkout is absent from disk #127

Closed
opened 2026-10-01 14:53:30 +01:00 by allod-agent · 2 comments
Member

ai-agents builds the Claude projects-memory symlink even when the memory-flagged checkout does not exist on the target machine, producing a dangling link that fails every harness's memory reads silently at session start.

Evidence (allod-dev, 2026-10-01, generation system-9): the machine's config flags the private fork agent-memory as a memory repo, the checkout was never cloned (the guest bootstrap only runs at provisioning, see the nexus issue this refs), and the rendered activation ran ln -sfnT "$HOME/work/agent-memory" "$HOME/.claude/projects/-home-allod-work/memory" without any error — a link to nowhere. Both Claude and pi then fail their configured memory reads on every session start. The eval-time asserts in modules/ai-agents.nix verify registry and repos consistency and checkout string safety, but nothing can see the filesystem at eval time, and the activation line has no existence guard.

Primary goals:

  • Fail loud at activation — when a memory-flagged checkout directory is absent (or exists but carries no memory.md) at activation time, the activation refuses that step loudly and names the missing path, rather than building a dangling symlink.
  • The failure does not strand the machine — per the existing activation conventions in this module, a loud refusal must not abort the whole activation script; the other entries the DAG sorts after it still run.

Current state and specifics

home.activation.llmMemoryLinks runs mkdir -p on the link's parent and ln -sfnT the target with no existence check; ln -sfnT succeeds silently against a missing target. The pointer-file instructions the same module renders name the missing checkout too, so the machine advertises a memory file it cannot serve.

Scope

In scope: an existence check in the llmMemoryLinks activation (warn or fail loudly by this module's existing conventions — the choice should follow what the module already does for target collisions) and a check-level witness that a missing checkout directory is caught. Out of scope: cloning the checkout (that is the provisioning-side fix, filed in allod/nexus), changing the symlink target selection, and any eval-time behavior.

Issue: allod/nexus#71.

`ai-agents` builds the Claude projects-memory symlink even when the memory-flagged checkout does not exist on the target machine, producing a dangling link that fails every harness's memory reads silently at session start. Evidence (allod-dev, 2026-10-01, generation system-9): the machine's config flags the private fork `agent-memory` as a memory repo, the checkout was never cloned (the guest bootstrap only runs at provisioning, see the nexus issue this refs), and the rendered activation ran `ln -sfnT "$HOME/work/agent-memory" "$HOME/.claude/projects/-home-allod-work/memory"` without any error — a link to nowhere. Both Claude and pi then fail their configured memory reads on every session start. The eval-time asserts in `modules/ai-agents.nix` verify registry and `repos` consistency and checkout string safety, but nothing can see the filesystem at eval time, and the activation line has no existence guard. Primary goals: - **Fail loud at activation** — when a memory-flagged checkout directory is absent (or exists but carries no `memory.md`) at activation time, the activation refuses that step loudly and names the missing path, rather than building a dangling symlink. - **The failure does not strand the machine** — per the existing activation conventions in this module, a loud refusal must not abort the whole activation script; the other entries the DAG sorts after it still run. ### Current state and specifics `home.activation.llmMemoryLinks` runs `mkdir -p` on the link's parent and `ln -sfnT` the target with no existence check; `ln -sfnT` succeeds silently against a missing target. The pointer-file instructions the same module renders name the missing checkout too, so the machine advertises a memory file it cannot serve. ### Scope In scope: an existence check in the `llmMemoryLinks` activation (warn or fail loudly by this module's existing conventions — the choice should follow what the module already does for target collisions) and a check-level witness that a missing checkout directory is caught. Out of scope: cloning the checkout (that is the provisioning-side fix, filed in allod/nexus), changing the symlink target selection, and any eval-time behavior. Issue: allod/nexus#71.

One ordering constraint changes the first goal.

Every memory checkout is absent at a fresh machine's first activation, by design. provision-vm installs the system, restarts the guest, and only then runs bootstrap-vm, which clones the repos; home-manager activation has already run at that first boot. An activation that refuses to create the link while its target is missing therefore leaves a newly provisioned machine with no memory link at all until its next rebuild, which provisioning never does. The refusal should be a loud warning that still creates the link: ln -sfnT as today, plus warnEcho naming the missing path when the directory or its memory.md is absent, following this module's non-aborting convention for the status-line collisions and the provisioning rule that activation must tolerate a half-provisioned machine. Once the clone lands the link resolves without another rebuild.

Check every memory checkout, not only the symlink target. The pointer files this module renders name every memory file (memoryFiles), so a missing non-target checkout fails session-start reads just as hard while the symlink looks healthy.

Witness. checks/pi-integration.nix already captures home.activation.llmMemoryLinks.data into a script and runs it; the same fixture with the checkout directory absent can assert that the warning text appears and that the link still exists. modules/memory-traces.nix has the message shape to mirror (checkout is missing: <path>).

The on-disk cause on allod-dev also had a deployment-side part: the memory fork's registry remote resolves against the wrong forge owner during bootstrap, so even a repair run would not have cloned it. That is being fixed in the private registry; details on allod/nexus#71.

One ordering constraint changes the first goal. **Every memory checkout is absent at a fresh machine's first activation, by design.** `provision-vm` installs the system, restarts the guest, and only then runs `bootstrap-vm`, which clones the repos; home-manager activation has already run at that first boot. An activation that refuses to create the link while its target is missing therefore leaves a newly provisioned machine with no memory link at all until its next rebuild, which provisioning never does. The refusal should be a loud warning that still creates the link: `ln -sfnT` as today, plus `warnEcho` naming the missing path when the directory or its `memory.md` is absent, following this module's non-aborting convention for the status-line collisions and the provisioning rule that activation must tolerate a half-provisioned machine. Once the clone lands the link resolves without another rebuild. **Check every memory checkout, not only the symlink target.** The pointer files this module renders name every memory file (`memoryFiles`), so a missing non-target checkout fails session-start reads just as hard while the symlink looks healthy. **Witness.** `checks/pi-integration.nix` already captures `home.activation.llmMemoryLinks.data` into a script and runs it; the same fixture with the checkout directory absent can assert that the warning text appears and that the link still exists. `modules/memory-traces.nix` has the message shape to mirror (`checkout is missing: <path>`). The on-disk cause on allod-dev also had a deployment-side part: the memory fork's registry remote resolves against the wrong forge owner during bootstrap, so even a repair run would not have cloned it. That is being fixed in the private registry; details on allod/nexus#71.

Correction to the last paragraph of my comment: the registry-remote fix does not resolve the on-disk cause on allod-dev. The machine's forge identity is denied read access to the private memory fork by design, so a guest-side clone can never succeed; the checkout has to be injected and refreshed from the hypervisor, tracked at allod/strategy#62. The activation-side point stands, and gains weight: on such a machine the checkout is absent at every activation until the host step runs, so the link must still be created and the absence warned about, never refused.

Correction to the last paragraph of my comment: the registry-remote fix does not resolve the on-disk cause on allod-dev. The machine's forge identity is denied read access to the private memory fork by design, so a guest-side clone can never succeed; the checkout has to be injected and refreshed from the hypervisor, tracked at allod/strategy#62. The activation-side point stands, and gains weight: on such a machine the checkout is absent at every activation until the host step runs, so the link must still be created and the absence warned about, never refused.
vnprc closed this issue 2026-10-01 21:00:42 +01:00
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
allod/archetypes#127
No description provided.