Compare commits

...

9 Commits

Author SHA1 Message Date
pi 3cdc2069db release: v1.2.4 — pi 0.80.2 → 0.80.3; global gitignore, env_file-only secrets, promote-base-latest CI fix
Publish Docker Image / resolve-versions (push) Successful in 1m2s
Publish Docker Image / base-decide (push) Successful in 43s
Publish Docker Image / build-base (push) Successful in 41m46s
Publish Docker Image / smoke (push) Successful in 4m10s
Publish Docker Image / smoke-studio (push) Successful in 6m42s
Publish Docker Image / build-variant (push) Successful in 16m16s
Publish Docker Image / promote-base-latest (push) Failing after 4s
Publish Docker Image / update-description (push) Successful in 12s
Publish Docker Image / build-variant-studio (push) Successful in 17m52s
2026-07-01 16:49:25 +02:00
pi cc53877328 feat: bake global gitignore (core.excludesFile) into image
Seed ~/.gitignore_global from /etc/skel-devbox (seed-if-absent, like
.bash_aliases/.inputrc, so user edits survive recreate) and wire it via
git config --global core.excludesFile, guarded so a user-set excludesFile
is never overridden. Ignores *.bak, *.bak.*, *~, *.orig, *.swp, *.tmp
across all repos without per-repo .gitignore entries.
2026-06-28 11:52:02 +02:00
pi c42b237d30 compose: deliver secrets via env_file only (drop environment: passthrough)
Removes GITEA_ACCESS_TOKEN / GITEA_HOST / GITHUB_PERSONAL_ACCESS_TOKEN from
the compose environment: block. An environment: entry both overrides
env_file AND is interpolated from the host shell, so a stale shell export
(e.g. one auto-loaded by an opencode/dotenv hook) silently shadowed the
users .env — an updated token never reached the container. Secrets now flow
solely via env_file: .env; .env.example already documents every variable.

- docker-compose.yml: drop the 3 passthrough lines + explanatory comment
- README.md: sync the "basic shape" snippet
- CHANGELOG.md: note under Unreleased (no tag bump / unpublished)
2026-06-27 23:48:02 +02:00
pi b7197e88b0 ci(promote-base-latest): re-point base-latest by digest, not need_build
The gate keyed off need_build=='true', assuming need_build==false meant
base-latest was already current. A dry-run dispatch (promote_latest=false)
that pre-builds base-<hash> falsifies that: the later tag run sees
need_build==false and skipped promotion, leaving base-latest one base
behind (observed 2026-06-27, v1.2.3 dry-run-first release).

Gate now runs on every tag release / promote dispatch; the no-op
optimization moved into the step as a crane digest compare so it re-tags
only when base-latest actually differs from the released base-<hash>.
Workflow-only change; base hash unaffected (no base rebuild).
2026-06-27 20:57:03 +02:00
pi 2985d9ade8 release: v1.2.3 — mempalace-mcp self-heal (toolkit e12b624)
Publish Docker Image / resolve-versions (push) Successful in 7s
Publish Docker Image / base-decide (push) Successful in 14s
Publish Docker Image / build-base (push) Has been skipped
Publish Docker Image / smoke (push) Successful in 3m30s
Publish Docker Image / smoke-studio (push) Successful in 11m47s
Publish Docker Image / build-variant (push) Successful in 15m48s
Publish Docker Image / promote-base-latest (push) Has been skipped
Publish Docker Image / update-description (push) Successful in 7s
Publish Docker Image / build-variant-studio (push) Successful in 17m28s
Patch release. Headline: mempalace-mcp self-heals instead of latching
available=false permanently after a slow virtiofs cold-open. Base image
rebuilds via the mempalace-toolkit ref advancing to e12b624 (folded into
the base-decide hash). No pi/mempalace version change — pi npm latest is
still 0.80.2 (= v1.2.2). Also releases the queued yq (mikefarah Go yq) and
mempalace-skill temporal-grounding changes.
2026-06-27 18:45:24 +02:00
pi bff810c1eb docs(dockerfile): sync mempalace stall-protection comment with self-heal
mempalace.ts now self-heals (respawn with capped backoff) instead of
latching unavailable, and the init-timeout default is 300000. Update the
explanatory comment + tunable list (MEMPALACE_MCP_MAX_RESPAWNS,
MEMPALACE_MCP_RESPAWN_BACKOFF_MS). Comment-only; no build/ENV change.
2026-06-26 00:22:43 +02:00
pi 904fe85249 skill(mempalace): teach temporal grounding (recreate != new day)
Baked mempalace SKILL.md now instructs agents to establish current date/time
and compute the delta against the actual diary/drawer timestamp before using
relative terms (yesterday/last week), and explicitly that a container recreate
or fresh session is NOT a day boundary (pi-devbox restarts several times a day).
Phase 1 wake-up section + anti-pattern bullet. CHANGELOG Unreleased.
2026-06-25 22:53:31 +02:00
pi cda488c565 base: yq follows latest (was pinned v4.53.3), gate on major v4
Match the repo's latest-following convention (tealdeer/uv/etc.) and keep the
container in sync with the Mac's brew yq. smoke-test now asserts mikefarah AND
major v4, so a surprise yq v5 fails CI instead of silently breaking
provision.sh. Pin still available via --build-arg YQ_VERSION=vX.Y.Z.
2026-06-25 16:33:27 +02:00
pi 9ab9a28458 base: install mikefarah yq (pinned v4.53.3), drop Debian python yq
Debian/Ubuntu `apt install yq` is kislyuk/yq (Python, v3.x), incompatible
with the mikefarah v4 syntax the cloud-init repo's provision.sh/deploy.sh
require. Replace the apt package with a pinned mikefarah Go binary, mirroring
the existing tealdeer ARG (latest-or-pin) pattern, multi-arch amd64/arm64.
smoke-test.sh now asserts `yq --version` reports mikefarah so CI catches a
regression. CHANGELOG: Unreleased entry.
2026-06-25 16:29:51 +02:00
9 changed files with 234 additions and 31 deletions
+34 -11
View File
@@ -565,16 +565,19 @@ jobs:
needs:
- base-decide
- build-variant
# Skip on cache-hit base builds: when need_build=false, base-latest
# already points at the same digest as base-<hash>, so the retag is
# a tautology and any transient failure of it is purely cosmetic.
# Manual workflow_dispatch with promote_latest=true overrides this
# gate as an escape hatch (e.g., if base-latest got hand-deleted).
# Run on every tag release (and on promote_latest=true dispatches).
# The job-level gate deliberately does NOT key off need_build anymore:
# the actual no-op optimization moved INTO the step as a digest compare
# (see below). Keying the gate on need_build was wrong because a prior
# dry-run dispatch (promote_latest=false) can pre-build+push base-<hash>,
# making need_build=false on the subsequent tag run even though
# base-latest is still stale — the old gate then skipped promotion and
# left base-latest pointing at the PREVIOUS base. (Observed 2026-06-27,
# v1.2.3: dry-run-first release left base-latest one base behind.)
if: |
always() &&
needs.build-variant.result == 'success' &&
(inputs.promote_latest == 'true' ||
(github.ref_type == 'tag' && needs.base-decide.outputs.need_build == 'true'))
(inputs.promote_latest == 'true' || github.ref_type == 'tag')
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
@@ -596,11 +599,31 @@ jobs:
crane auth login docker.io \
-u ${{ vars.DOCKERHUB_USERNAME }} \
-p "${{ secrets.DOCKERHUB_TOKEN }}"
- name: Re-tag base-<hash> as base-latest
- name: Re-tag base-<hash> as base-latest (only if stale)
env:
BASE_HASH_REF: ${{ env.IMAGE }}:${{ needs.base-decide.outputs.base_tag }}
BASE_LATEST_REF: ${{ env.IMAGE }}:base-latest
run: |
crane copy \
${{ env.IMAGE }}:${{ needs.base-decide.outputs.base_tag }} \
${{ env.IMAGE }}:base-latest
set -euo pipefail
# Correctness invariant: after a release, base-latest must resolve to
# the SAME digest as the base-<hash> the just-built variants were
# FROM. Compare digests rather than trusting need_build — a prior
# dry-run dispatch can pre-build base-<hash>, so need_build=false on
# the tag run does NOT imply base-latest is already current. When the
# digests already match (genuine cache-hit release) this is a no-op,
# so we skip the crane copy entirely — preserving the original
# "don't do a tautological retag" intent and avoiding any cosmetic
# transient-failure exposure on releases that change nothing.
want=$(crane digest "${BASE_HASH_REF}")
have=$(crane digest "${BASE_LATEST_REF}" 2>/dev/null || echo "")
echo "base-<hash> digest: ${want}"
echo "base-latest digest: ${have:-<absent>}"
if [ "${want}" = "${have}" ]; then
echo "base-latest already current; nothing to promote."
else
echo "Promoting base-latest -> ${BASE_HASH_REF}"
crane copy "${BASE_HASH_REF}" "${BASE_LATEST_REF}"
fi
# ── Phase 6: update Hub description (only on real release runs) ────
update-description:
+107
View File
@@ -11,6 +11,113 @@ Pre-v1.0.0 tags followed the pi npm version (`v{pi_version}[letter]`).
---
## v1.2.4 — 2026-06-29
Patch release. Headline: **pi `0.80.2` → `0.80.3`** (npm `latest`). Also ships a
global gitignore baked into the image, secrets-via-`env_file`-only compose
hardening, and a CI fix so `promote-base-latest` re-points `base-latest`
reliably after a dry-run-first release. The mempalace pin stays `3.5.0`. The
base image rebuilds because `Dockerfile.base` changed (the gitignore seed +
`entrypoint-user.sh` wiring).
### Added
- **Global gitignore baked into the image.** A `~/.gitignore_global`
(`*.bak`, `*.bak.*`, `*~`, `*.orig`, `*.swp`, `*.tmp`) is seeded into the home
dir from `/etc/skel-devbox/` on first boot (seed-if-absent, like
`.bash_aliases`/`.inputrc`, so user edits survive recreate) and wired via
`git config --global core.excludesFile`. Personal/tooling backup artifacts are
now ignored across all repos in the container without per-repo `.gitignore`
entries. The `core.excludesFile` wiring is skipped if the user already set one.
### Changed
- **Secrets are now delivered to the container via `env_file: .env` only; the
`environment:` block no longer re-declares `GITEA_ACCESS_TOKEN`,
`GITEA_HOST`, or `GITHUB_PERSONAL_ACCESS_TOKEN`.** An `environment:` entry
both overrides `env_file:` and is interpolated from the host shell, so a
stale shell export (e.g. one auto-loaded by an opencode/dotenv hook) would
silently shadow the value in your `.env` — an updated token in `.env` never
reached the container. Delivering secrets via `env_file` only decouples the
container from whatever the host shell happens to export. No action needed:
`.env.example` already documents every supported variable. Affects
`docker-compose.yml` and the README “basic shape” snippet.
### Fixed (CI)
- **`promote-base-latest` now re-points `base-latest` reliably after a
dry-run-first release.** The job's gate previously required
`need_build == 'true'`, on the assumption that `need_build == false`
implied `base-latest` was already current. That assumption breaks when a
`workflow_dispatch` dry-run (`promote_latest=false`) pre-builds and pushes
`base-<hash>` first: the subsequent tag run then sees `need_build == false`
(probe hit) and **skipped** promotion, leaving `base-latest` pointing at the
*previous* base. (Observed 2026-06-27 releasing v1.2.3 via dry-run-then-tag
`base-latest` ended up one base behind, lacking the mempalace self-heal.)
Now the gate runs on every tag release (or `promote_latest=true` dispatch),
and the no-op optimization moved **into** the step as a `crane digest`
compare: it re-tags only when `base-latest` actually differs from the
released `base-<hash>`, so genuine cache-hit releases stay a no-op while
stale aliases get corrected. No image-content change; base hash unaffected.
---
## v1.2.3 — 2026-06-27
Patch release. Headline: **mempalace-mcp now self-heals** instead of latching
`available=false` permanently after a slow cold-open. Also folds in the `yq`
and mempalace-skill changes that were sitting unreleased. **No pi/mempalace
version change** — pi npm `latest` is still `0.80.2` (= v1.2.2) and the
mempalace pin stays `3.5.0`; the base image rebuilds purely because the
`mempalace-toolkit` ref advances to pick up the self-heal extension.
### Fixed
- **mempalace-mcp self-heal — no more permanent `available=false` latch.**
The `mempalace.ts` pi extension (from `mempalace-toolkit`, bumped to
[`e12b624`](https://gitea.jordbo.se/joakimp/mempalace-toolkit/commit/e12b624))
previously tripped its per-request timeout on a slow virtiofs cold-open of
the palace, killed the child, and set `available=false` **forever** (no
respawn) — a pi restart was the only recovery.
- **Bounded respawn with capped exponential backoff** via `ensureAlive()`
(`MEMPALACE_MCP_MAX_RESPAWNS=2`, `MEMPALACE_MCP_RESPAWN_BACKOFF_MS=1000`;
set max to `0` to disable). Both `execute()` and initial startup route
through it. The respawn budget **resets on any successful JSON-RPC
response** (`onStdout`), so a healthy session can't slowly exhaust it.
- **Scoped init timeout** raised `120000 → 300000` ms (`MEMPALACE_MCP_INIT_TIMEOUT_MS`),
affecting **init only** — the per-call timeout stays `60000`
(`MEMPALACE_MCP_TIMEOUT_MS`) — so a genuine cold HNSW deserialize isn't
killed mid-open.
- **Concurrency hardening:** a generation counter prevents a late-exiting
killed process from clobbering a fresh respawn, and an explicit `healthy`
flag replaces the racy `proc != null` check.
- Note: the build-time `smoke-test.sh` verifies the extension is present and
deployed but does **not** exercise respawn behaviour — first live
validation is on a running container.
- **`yq` is now mikefarah's Go yq, not Debian's Python `yq`.** The base image
previously apt-installed `yq`, which on Debian/Ubuntu is the unrelated
kislyuk/`yq` (a jq wrapper, v3.x) — incompatible with the mikefarah v4 syntax
the `cloud-init` repo's `provision.sh`/`deploy.sh` expect. Dropped the apt
package and install the mikefarah binary instead (multi-arch amd64/arm64,
following the repo's `latest` convention like `tealdeer`/`uv`; pin a tag
with `--build-arg YQ_VERSION=vX.Y.Z`). The build-time `smoke-test.sh` gate
asserts `yq --version` reports `mikefarah` **and** major **v4**, so both a
regression to the Python package and a surprise future yq v5 fail CI.
### Changed
- **Baked `mempalace` skill now teaches temporal grounding.** Added a
*Temporal grounding* rule to the image-baked
`skills/mempalace/SKILL.md` (Phase 1 wake-up + a matching anti-pattern):
before using relative time terms ("yesterday", "last week"), establish the
current date/time and compute the delta against the actual diary/drawer
timestamp. Explicitly calls out that a **container recreate or fresh session
is not a day boundary** — pi-devbox restarts several times a day, so two
entries minutes apart can straddle a recreate. Fixes agents mislabelling
same-day sessions as "yesterday".
---
## v1.2.2 — 2026-06-24
Patch release: pick up **pi `0.80.2`** (npm `latest`) and **mempalace `3.5.0`**,
+39 -12
View File
@@ -51,7 +51,8 @@ ENV DEBIAN_FRONTEND=noninteractive
# See the bundled `dot-watch` helper for live .dot -> PNG
# re-render (handy with pi-studio's image preview).
# imagemagick — image conversion / resizing for thumbnails, etc. ~50 MB.
# yq — YAML-aware companion to jq.
# (yq is NOT apt-installed: Debian's `yq` is the unrelated Python tool;
# mikefarah's Go yq is installed as a pinned binary further down.)
# socat — TCP relay. Powers `studio-expose`, which bridges
# pi-studio's container-loopback server to the container's
# external interface so a published port can reach it.
@@ -66,7 +67,6 @@ RUN apt-get update && \
openssh-client \
gnupg \
jq \
yq \
ripgrep \
fd-find \
tree \
@@ -289,15 +289,21 @@ RUN ARCH=$(case "${TARGETARCH}" in amd64) echo "x86_64" ;; arm64) echo "aarch64"
# Always installed in the base. Set INSTALL_MEMPALACE=false at base-build
# time to shave ~300 MB.
#
# Stall protection (fixed 2026-06-13): mempalace-mcp is launched by the
# `mempalace.ts` pi extension from mempalace-toolkit (cloned below). That
# extension now applies a per-REQUEST timeout in its JSON-RPC client and
# kills the child on stall, so a virtiofs cold-open of chroma.sqlite3 /
# HNSW load can no longer hang the pi TUI uninterruptibly. Tunables:
# Stall protection (fixed 2026-06-13; self-heal added 2026-06-25):
# mempalace-mcp is launched by the `mempalace.ts` pi extension from
# mempalace-toolkit (cloned below). That extension applies a per-REQUEST
# timeout in its JSON-RPC client and kills the child on stall, so a virtiofs
# cold-open of chroma.sqlite3 / HNSW load can no longer hang the pi TUI
# uninterruptibly. A stall-kill is no longer a permanent latch either: the
# next tool call respawns the server with capped exponential backoff (the
# budget resets on any successful response). Tunables:
# MEMPALACE_MCP_TIMEOUT_MS (default 60000), MEMPALACE_MCP_INIT_TIMEOUT_MS
# (default 120000); 0 disables. A standalone stdio-watchdog shim is NOT
# needed — the extension already owns request/response correlation. See
# CHANGELOG.md "Unreleased > Fixed".
# (default 300000 — generous so a genuine first cold-open isn't killed),
# MEMPALACE_MCP_MAX_RESPAWNS (default 2; 0 disables self-heal),
# MEMPALACE_MCP_RESPAWN_BACKOFF_MS (default 1000); timeouts of 0 disable.
# Defaults live in the extension, so no ENV is needed here. A standalone
# stdio-watchdog shim is NOT needed — the extension already owns
# request/response correlation. See CHANGELOG.md "Unreleased > Fixed".
ARG INSTALL_MEMPALACE=true
# Pin to a known-good version. Bump deliberately, not implicitly: an
# unpinned install silently swept in mempalace 3.3.x/3.4.0 with a broken
@@ -404,6 +410,26 @@ RUN ARCH=$(case "${TARGETARCH}" in amd64) echo "x86_64" ;; arm64) echo "aarch64"
chmod +x /usr/local/bin/tldr && \
tldr --version
# ── yq (mikefarah) — YAML processor, jq's companion for YAML ─────────
# Installed as the mikefarah Go binary — NOT Debian's `yq` apt package, which
# is the unrelated Python kislyuk/yq (a jq wrapper with different syntax and
# version line, e.g. 3.x). The cloud-init repo's deploy.sh/provision.sh
# require mikefarah yq v4 (the unrelated Debian python yq is v3.x). Follows
# the repo's `latest` convention (like tealdeer/uv/etc.); the smoke test pins
# the contract to major v4, so a future yq v5 fails CI instead of silently
# breaking provision.sh. Pin a specific tag with --build-arg YQ_VERSION=vX.Y.Z.
ARG YQ_VERSION=latest
RUN ARCH=$(case "${TARGETARCH}" in amd64) echo "amd64" ;; arm64) echo "arm64" ;; *) echo "amd64" ;; esac) && \
V="${YQ_VERSION}" && \
if [ "$V" = "latest" ]; then \
V=$(curl -sI --retry 5 --retry-delay 5 --retry-all-errors "https://github.com/mikefarah/yq/releases/latest" | awk 'tolower($1)=="location:" { sub(/\r$/,"",$2); n=split($2,a,"/"); print a[n] }'); \
fi && \
[ -n "$V" ] && \
echo "Installing mikefarah yq ${V}" && \
curl -fsSL --retry 5 --retry-delay 5 --retry-all-errors "https://github.com/mikefarah/yq/releases/download/${V}/yq_linux_${ARCH}" -o /usr/local/bin/yq && \
chmod +x /usr/local/bin/yq && \
yq --version
# ── AWS CLI v2 (for SSO/Bedrock authentication) ─────────────────────
RUN ARCH=$(case "${TARGETARCH}" in \
amd64) echo "x86_64" ;; \
@@ -451,8 +477,9 @@ ENV PATH="/home/${USER_NAME}/.pi/npm-global/bin:${PATH}"
# ── Shell defaults (bash history, aliases, readline) ─────────────────
RUN mkdir -p /etc/skel-devbox
COPY rootfs/home/developer/.bash_aliases /etc/skel-devbox/.bash_aliases
COPY rootfs/home/developer/.inputrc /etc/skel-devbox/.inputrc
COPY rootfs/home/developer/.bash_aliases /etc/skel-devbox/.bash_aliases
COPY rootfs/home/developer/.inputrc /etc/skel-devbox/.inputrc
COPY rootfs/home/developer/.gitignore_global /etc/skel-devbox/.gitignore_global
# ── Entrypoint ────────────────────────────────────────────────────────
COPY rootfs/usr/local/lib/pi-devbox/ /usr/local/lib/pi-devbox/
+4 -3
View File
@@ -319,9 +319,10 @@ services:
environment:
- TERM=xterm-256color
# - STUDIO_EXPOSE=1 # -studio only: auto-start the socat bridge on boot
- GITEA_ACCESS_TOKEN=${GITEA_ACCESS_TOKEN:-}
- GITEA_HOST=${GITEA_HOST:-}
- GITHUB_PERSONAL_ACCESS_TOKEN=${GITHUB_PERSONAL_ACCESS_TOKEN:-}
# Secrets (GITEA_*, GITHUB_*, …) come from env_file: .env above — not
# duplicated here. An environment: entry overrides env_file and is
# interpolated from the host shell, so a stale shell export would
# silently shadow your .env. See .env.example for the full list.
volumes:
# Workspace: your host source tree
- ${WORKSPACE_PATH:-.}:/workspace
+7 -3
View File
@@ -31,9 +31,13 @@ services:
- .env
environment:
- TERM=xterm-256color
- GITEA_ACCESS_TOKEN=${GITEA_ACCESS_TOKEN:-}
- GITEA_HOST=${GITEA_HOST:-}
- GITHUB_PERSONAL_ACCESS_TOKEN=${GITHUB_PERSONAL_ACCESS_TOKEN:-}
# Secrets (GITEA_*, GITHUB_*, and any others) are delivered to the
# container via `env_file: .env` above — do NOT duplicate them here.
# An `environment:` entry overrides env_file AND is interpolated from
# the host shell, so a stale shell export (e.g. one auto-loaded by a
# dotenv hook) would silently shadow the value in your .env. Keeping
# secrets env_file-only decouples the container from the host shell.
# See .env.example for the full list of supported variables.
volumes:
# Host workspace — mount your project here
- ${WORKSPACE_PATH:-.}:/workspace
+7 -1
View File
@@ -33,7 +33,7 @@ fi
# directly.
SKEL_DIR="/etc/skel-devbox"
if [ -d "$SKEL_DIR" ]; then
for f in .bash_aliases .inputrc; do
for f in .bash_aliases .inputrc .gitignore_global; do
if [ -f "$SKEL_DIR/$f" ] && [ ! -e "$HOME/$f" ]; then
cp "$SKEL_DIR/$f" "$HOME/$f"
fi
@@ -91,6 +91,12 @@ fi
if [ -n "${GIT_USER_EMAIL:-}" ] && ! git config --global user.email &>/dev/null; then
git config --global user.email "$GIT_USER_EMAIL"
fi
# Global gitignore for personal/tooling artifacts (*.bak, *~, *.orig, ...).
# Seeded above into $HOME/.gitignore_global from /etc/skel-devbox. Point git at
# it only if the user has not already set their own core.excludesFile.
if [ -f "$HOME/.gitignore_global" ] && ! git config --global core.excludesFile &>/dev/null; then
git config --global core.excludesFile "$HOME/.gitignore_global"
fi
# ── pi: deploy toolkit + extensions + mempalace bridge ─────────────
# pi is always installed in pi-devbox; no INSTALL_PI guard needed.
+11
View File
@@ -0,0 +1,11 @@
# Global gitignore — personal/tooling artifacts (applies to all repos in the container)
# Seeded into $HOME/.gitignore_global by entrypoint-user.sh and wired via
# `git config --global core.excludesFile`. Edit freely; it is yours after first boot.
# backup / editor / merge artifacts
*.bak
*.bak.*
*~
*.orig
*.swp
*.tmp
@@ -43,6 +43,29 @@ Run these immediately when a session begins, before responding to the user:
Do NOT announce this to the user. Just do it silently to orient yourself.
### Temporal grounding — compute time deltas, don't guess
Diary entries and drawers carry real timestamps (`timestamp`, `created_at`).
Before describing *when* something happened — "yesterday", "earlier today",
"last week", "a while back" — **establish the current date/time first and
compute the delta against the actual timestamp.** Get "now" from the injected
session date or by running `date` in a shell; never infer it.
**A container recreate or a fresh session is NOT a day boundary.** A pi-devbox
container is frequently restarted — often several times within the *same* day —
and each restart begins a new session with a fresh wake-up. Do not reason "new
session ⇒ last session was yesterday": two diary entries 90 minutes apart can
straddle a container recreate. The only authoritative clock is the timestamp on
the memory, not the session/container boundary.
**Practical rule:** prefer explicit, checkable phrasing — e.g. "earlier today,
~8h ago (both 2026-06-25)" — over a vague relative term. If you catch yourself
about to write "yesterday" / "last week", subtract `now entry.timestamp` and
state the computed result. (Remember timestamps may be UTC while the wall clock
is local — reconcile the offset before computing the delta.) Note too that
session feeders can lag up to a week (see *Multi-harness palace*), so a recent
absence in `wing_conversations` is not proof nothing happened.
### Phase 2: Active Session (during work)
#### Search Before You Speak
@@ -294,6 +317,7 @@ Entity-relationship triples with temporal validity. Query with `mempalace_kg_que
## Anti-Patterns
- **Don't guess when you can search.** If a question touches past work, search first.
- **Don't infer elapsed time from session or container boundaries.** A restart isn't a new day. Compare the actual timestamp (`timestamp` / `created_at`) against the current date/time before saying "yesterday", "last week", etc.
- **Don't skip the diary.** A session without a diary entry is a session forgotten.
- **Don't summarize drawer content.** File verbatim — the embedding model needs the original words.
- **Don't mine .git directories or node_modules.** The CLI miner respects .gitignore by default.
+1 -1
View File
@@ -76,7 +76,7 @@ run "mempalace-mcp" "mempalace-mcp --help"
run "pandoc" "pandoc --version"
run "graphviz (dot)" "dot -V"
run "imagemagick" "magick --version"
run "yq" "yq --version"
run "yq (mikefarah v4)" "yq --version | grep -qE 'mikefarah.*version v4'"
run "tldr (tealdeer)" "tldr --version"
run "socat" "socat -V"
run "studio-expose helper" "test -x /usr/local/bin/studio-expose"