Skip to content

fix: self-update corrupted the running installer and CLI - #33

Merged
TJCurnutte merged 26 commits into
mainfrom
claude/busy-cannon-806v4m
Jun 12, 2026
Merged

fix: self-update corrupted the running installer and CLI#33
TJCurnutte merged 26 commits into
mainfrom
claude/busy-cannon-806v4m

Conversation

@TJCurnutte

Copy link
Copy Markdown
Owner

Hotfix — production break in memory-hive update

Symptom (reported from a real machine):

/Users/.../.memory-hive/install.sh: line 215: syntax error near unexpected token `do'
/Users/.../.local/bin/memory-hive: line 6895: syntax error near unexpected token `('

Cause: the updater executes $INSTALL_DIR/install.sh, which cp's install.sh and memory-hive over themselves in place (the PATH shim is a symlink to the same inode). Shells read scripts in buffered chunks, so the running interpreter picked up the new, longer file at old byte offsets → garbage parse → phantom syntax errors → half-applied update. Latent for ages; v1.3.0–v1.6.0 grew both files by hundreds of lines, making it near-deterministic. CI never caught it because only fresh installs were tested, never the self-overwriting update path.

Fix:

  • Helper installs and hook renders stage to a temp file and mv into place — rename swaps the directory entry while the old inode stays readable for running processes.
  • update.sh runs the installed installer from a temp copy, never from the path being refreshed.

Regression test: new CI step installs, then self-updates from an upstream copy padded by thousands of bytes (forcing the offset shift), asserting a clean run, applied update, and a working CLI — for both the direct installer path and the update.sh wrapper (reverse shift).

Proof: locally reproduced with the pristine v1.6.0 tree (Syntax error: "}" unexpected (expecting ";;"), exit 2) and verified the fixed tree passes the identical scenario in both directions, with doctor green after both updates.

User recovery on already-affected machines: curl -fsSL https://raw.githubusercontent.com/TJCurnutte/memory-hive/main/install.sh | sh (piped script — can't self-overwrite), or a second memory-hive update (byte-identical copies are harmless). Agent silos were never touched; the crash happened after the preserve/restore step.

https://claude.ai/code/session_01TbAgEqEHJ4xTcx9otN3DrN


Generated by Claude Code

claude added 26 commits June 9, 2026 17:31
Ship Memory Hive as a native Claude Code Agent Skill. The template
carries the on-demand depth layer (retrieval verb guide, raw-learning
frontmatter spec, lane rules, curator loop) so the always-on CLAUDE.md
boot block can stay small; Claude Code loads the full body only when
memory work is happening (progressive disclosure), and the skill is
invocable as /memory-hive. Installer wiring, doctor receipts, CI smoke,
and docs land in the follow-up commit.

https://claude.ai/code/session_01TbAgEqEHJ4xTcx9otN3DrN
Extend the platform smoke steps: auto-inject asserts the rendered skill
lands at ~/.claude/skills/memory-hive/SKILL.md with intact frontmatter,
substituted paths, and a placeholder-bearing shipped template; opt-out
asserts MEMORY_HIVE_SKIP_CLAUDE_SKILL=1 skips the skill while the
CLAUDE.md block still lands; idempotent asserts stable checksums across
re-installs.

https://claude.ai/code/session_01TbAgEqEHJ4xTcx9otN3DrN
Wire the skill template into the full install/verify lifecycle:

- install.sh: render templates/skills/memory-hive/SKILL.md (same
  ${HIVE_DIR}/${INSTALL_DIR} substitution as the boot block) and write it
  atomically to ~/.claude/skills/memory-hive/SKILL.md when ~/.claude
  exists. New platform record id `claude-skill` with wired/skip/gone
  banner lines. Opt out with MEMORY_HIVE_SKIP_CLAUDE_SKILL=1 (skill
  only) or MEMORY_HIVE_SKIP_CLAUDE_CODE=1 (both surfaces). Unrendered
  template also shipped to $INSTALL_DIR/templates/ for inspection.
- memory-hive doctor: new check 4b warns (exit 1) when the skill file is
  missing or references a different install, with a re-install hint;
  silent when ~/.claude is absent.
- SKILL.md: align body with repo truth — lint errors vs warnings split,
  50KB size cap, `kind:` documented as optional promote/conflicts
  metadata (not lint-enforced), tasks/queue.md marked curator-written /
  read-for-coordination, architecture deep-link points at the GitHub
  source instead of a file the installer never ships.
- docs: INTEGRATION.md, templates/platforms/claude-code.md, README, and
  CHANGELOG describe the two-surface model (always-on CLAUDE.md block +
  on-demand skill) and the new env vars/uninstall steps.

The skill gives Claude Code progressive disclosure: one description line
always in context, full operational depth (retrieval verbs, lint-passing
frontmatter, lane rules, curator loop) loaded only when memory work is
happening — also invocable directly as /memory-hive.

https://claude.ai/code/session_01TbAgEqEHJ4xTcx9otN3DrN
Make the on-demand depth layer work on every platform, not just Claude
Code. The single source of truth moves to templates/guide.md; surfaces
are assembled from it:

- New `memory-hive guide [topic]` verb renders the guide with the
  install's real paths at read time. Topics narrow to one section
  (paths|id|hydrate|retrieve|write|lanes|curate|health). Any agent that
  can run a shell — Cursor, Codex, Gemini CLI, Goose, Warp, Amp,
  OpenCode, ... — now has the same progressive disclosure Claude Code
  gets.
- The Claude Code Agent Skill is now assembled at install time from the
  SKILL.md frontmatter head + guide.md body, so the skill and the
  cross-platform guide cannot drift apart. Older checkouts without
  guide.md fall back to the self-contained SKILL.md render.
- install.sh ships the unrendered guide to $INSTALL_DIR/templates/
  (load-bearing for the verb); doctor warns when it goes missing.
- The managed boot block — spliced into all 17 auto-inject platforms —
  gains a 'Going deeper' pointer to `memory-hive guide`.
- Fix two pre-existing boot-block bugs that taught lint-failing writes
  on every platform: the raw-learning example now uses the canonical
  agent-subdir layout (learnings/raw/<id>/YYYY-MM-DD-<slug>.md) and
  includes the required `confidence` field; lane-keeping now scopes
  shared-pool writes to the agent's own subdir.
- CI: assert the skill body is assembled from guide.md, the shipped
  guide keeps placeholders, `guide` renders cleanly, topic extraction
  works, unknown topics fail, and wired boot blocks carry the pointer.

https://claude.ai/code/session_01TbAgEqEHJ4xTcx9otN3DrN
… Code

The boot block and Agent Skill are prompt-level: they ask the model to
follow the memory contract. These hooks make the harness enforce it:

- SessionStart hook (templates/hooks/session-start.sh): injects a
  token-budgeted hive bundle plus a `memory-hive guide` pointer into
  every new session as additionalContext, tagged with the agent id,
  hive path, and model when the payload names one. Sessions boot
  hydrated even if the model never reads CLAUDE.md.
- Stop hook (templates/hooks/stop-ritual.sh): when a substantive
  session ends without a fresh dated line in the agent's log.md, block
  exactly once with instructions to run the ritual. stop_hook_active
  prevents loops, short transcripts are exempt, and every failure path
  (missing hive, missing python3, unparseable payload) exits 0 — hooks
  fail open, never trapping a session.
- install.sh renders both scripts to $INSTALL_DIR/hooks/ and merges the
  entries into ~/.claude/settings.json via python3 (already required
  for HyperRecall). Entries carry a `# memory-hive` command marker so
  re-runs replace exactly ours; user-authored hooks are never touched
  and malformed settings.json is left alone with a warning. New
  platform record id `claude-hooks`; opt out at install with
  MEMORY_HIVE_SKIP_CLAUDE_HOOKS=1, mute at runtime with
  MEMORY_HIVE_HOOKS_DISABLE=1, retarget with MEMORY_HIVE_AGENT_ID.
- Both scripts drain the hook payload from stdin before the python
  heredoc runs — `python3 - <<EOF` takes its program from stdin, so
  reading the payload inside python would see EOF and silently no-op.
- doctor check 4d warns when the scripts are missing or settings.json
  no longer references them; CI smoke-tests the merge (idempotency,
  user-hook preservation, matcher), the SessionStart JSON contract,
  all three Stop-hook paths, and the skip env var.

https://claude.ai/code/session_01TbAgEqEHJ4xTcx9otN3DrN
Promote the Unreleased section to 1.3.0 (guide verb + Agent Skill +
harness hooks + boot-block lint fixes) and list `guide` in the README
inspection command family.

https://claude.ai/code/session_01TbAgEqEHJ4xTcx9otN3DrN
Tag pushes keep working unchanged. workflow_dispatch (version + optional
sha) now creates the tag via the API first and then publishes the same
changelog-backed release in one run — needed because tags created with
GITHUB_TOKEN do not re-trigger the tag-push path, and some environments
can push branches but not tag refs.

https://claude.ai/code/session_01TbAgEqEHJ4xTcx9otN3DrN
This environment can push this branch but not tag refs, and the
workflow-dispatch API is unavailable to it, so release.yml's tag-push
trigger can never fire from here. This push-triggered one-shot creates
the v1.3.0 tag at the merged main commit and publishes the same
changelog-backed release. Idempotent; remove after the release is live.

https://claude.ai/code/session_01TbAgEqEHJ4xTcx9otN3DrN
v1.3.0 was tagged and published by its single run; the file documents
its own removal once done. The reusable path for future releases from
restricted environments is release.yml's new workflow_dispatch input.

https://claude.ai/code/session_01TbAgEqEHJ4xTcx9otN3DrN
…atform

The write-back contract was prose: every model on every harness had to
hand-format paths, dates, and frontmatter, and weaker models got it
wrong in ways lint catches only after the fact. Make it executable:

- `memory-hive log "<what you did>" [--agent <id>]` appends the dated
  line to the agent's silo log.
- `memory-hive learn "<imperative rule>" --context "<one line>"
  [--kind pattern|win|mistake|insight] [--confidence low|medium|high]
  [--agent <id>] [--body <file>|-]` writes the raw learning to the
  canonical learnings/raw/<id>/YYYY-MM-DD-<slug>.md path with
  frontmatter that is lint-valid by construction, scaffolds the body
  from context + title when none is given, dodges slug collisions, and
  runs lint on the new file as a receipt.
- The managed boot block (all 17 auto-inject platforms: Codex, Hermes,
  Cursor, Gemini CLI, Goose, Warp, Amp, OpenCode, ...) now instructs
  agents to RUN the verbs instead of hand-writing files; the guide's
  Workflow 3 leads with them; the Claude Stop hook's nudge cites the
  exact command.
- CI smokes both verbs end to end and asserts the generated learning
  passes lint with zero warnings.

https://claude.ai/code/session_01TbAgEqEHJ4xTcx9otN3DrN
Promote the Unreleased section (log/learn verbs, verb-first boot block
and guide, stop-hook nudge, release workflow dispatch path) to 1.4.0.

https://claude.ai/code/session_01TbAgEqEHJ4xTcx9otN3DrN
Bring the two aspects of ambient-memory products (pieces.app) that
memory-hive lacked, built local-first in the existing dependency
envelope:

- MCP server (`memory-hive mcp`): stdio JSON-RPC, stdlib-python only.
  Any MCP client — Claude Desktop/Code, Cursor, Goose, Copilot — gets
  native tools with no boot block: ask_hive (HyperRecall retrieval with
  query fallback), hive_log, hive_learn, hive_capture, hive_guide.
  Mirrors the ask/create tool split (ask_pieces_ltm /
  create_pieces_memory) while keeping writes lane-correct by routing
  every call through the CLI verbs. `mcp --config` prints the client
  snippet with real paths. Ships as memory_hive_mcp.py; doctor 4e.
- Ambient workstream capture: `memory-hive capture "<event>"
  [--source <name>] [--agent <id>]` appends timestamped events to
  hive/raw/<source>/YYYY-MM-DD.md per the existing Tier-1 convention —
  searchable by query/recall immediately, curator-distillable later.
- New Claude Code SessionEnd hook feeds hive/raw/sessions/
  automatically (reason + cwd), so a trace of every session exists even
  when the model never ran the ritual. Same fail-open, marker-tagged
  settings.json merge; doctor and CI cover all three hooks.
- CI: capture + session-end smoke, MCP initialize/list/call over real
  stdio with a lint-clean learning written through the server.

https://claude.ai/code/session_01TbAgEqEHJ4xTcx9otN3DrN
Claude Code was the only platform with mechanical enforcement; Cursor
has a beta hooks system (~/.cursor/hooks.json, version 1) that maps to
it almost one-to-one, so wire the same two-hook layer:

- cursor-stop.sh (`stop` event): when a completed conversation has no
  fresh dated line in the agent's log.md, reply once with a
  followup_message citing the exact `memory-hive log` / `memory-hive
  learn` commands. Cursor's own loop_count is the loop guard (nudge
  only at 0); aborted/error runs and short transcripts are exempt;
  every failure path exits 0 silently.
- cursor-session-end.sh (`sessionEnd` event): ambient capture — append
  a timestamped session-ended event (model, first workspace root) to
  hive/raw/sessions/ via `memory-hive capture`.
- install.sh merges both into ~/.cursor/hooks.json via python3:
  ownership is matched by the distinctive hooks/cursor-*.sh script
  paths (Cursor may argv-split commands, so the settings.json-style
  trailing-comment marker is not safe here); user entries are never
  touched, malformed files are refused with a warning. New platform
  record id `cursor-hooks`; MEMORY_HIVE_SKIP_CURSOR_HOOKS=1 skips the
  hooks alone, MEMORY_HIVE_SKIP_CURSOR=1 skips all Cursor wiring,
  MEMORY_HIVE_HOOKS_DISABLE=1 mutes at runtime.
- doctor 4f checks scripts + hooks.json wiring; CI smokes the merge
  (idempotency, user-entry preservation, version field) and all four
  stop-hook paths plus sessionEnd capture; opt-out step covers the
  skip var.

https://claude.ai/code/session_01TbAgEqEHJ4xTcx9otN3DrN
`memory-hive update` executes $INSTALL_DIR/install.sh, which copied
install.sh and memory-hive over themselves in place (the PATH shim is a
symlink to the same inode). The running interpreter then read the new,
longer file at old byte offsets and died with phantom syntax errors,
leaving a half-applied update. The v1.3.0-v1.6.0 releases grew both
files by hundreds of lines, making the corruption near-deterministic.

- helper installs and hook renders now stage to a temp file and mv into
  place: rename() swaps the directory entry, the old inode stays
  readable for running processes
- update.sh runs the installed installer from a temp copy, never from
  the path being refreshed
- new CI step reproduces the scenario (self-update from an upstream
  copy padded by thousands of bytes) and asserts a clean run, applied
  update, working CLI — for both the direct and wrapper paths

Verified locally: the released v1.6.0 tree crashes in this scenario
(Syntax error: "}" unexpected); the fixed tree passes all paths.

https://claude.ai/code/session_01TbAgEqEHJ4xTcx9otN3DrN
@TJCurnutte
TJCurnutte merged commit 1edebf8 into main Jun 12, 2026
2 checks passed
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