Skip to content

fix: bring the dev instance back up, and make the checks that prove it - #48

Merged
arthware-dev merged 16 commits into
mainfrom
chore/agent-self-loop
Jul 31, 2026
Merged

fix: bring the dev instance back up, and make the checks that prove it#48
arthware-dev merged 16 commits into
mainfrom
chore/agent-self-loop

Conversation

@arthware-dev

@arthware-dev arthware-dev commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary

Brings the dev instance back up, and fixes what running it exposed.

The agent's vault tools were registered and non-functional. Both shipped that way with a green suite, because all four faults live in components the tools never see: memory person was missing from the core API allowlist, memory_tool.py passed a --backend mem0 flag that never existed, stack memory person read the source clone when profiles are generated into the brain projection, and the agent mounted that same source clone at vault/ (so it told Homer "there is no vault/homer/about.md" about a page that existed).

Two defects broke famstack on a clean install. stack memory person and correspondents died with No module named 'frontmatter' on any host that never ran pip install - the loaders used a third-party package that ships only in the test extra, so the tests carried a dependency users don't have. And a stacklet whose containers are all optional could never finish setup: docker compose exits 1 with no service selected, which is an empty selection, not a failure.

Also: stack doctor (diagnose an instance, print the fix), tools/branch-status (retired 46 stale branches), agent vault tools wired into sitecustomize.py, and a stacker-bot credential fix - two paths created the account with two different stored passwords, so the bot could never log in.

Notes for review

  • Tests here are anchored to something external rather than to our own reading: real Synapse for the password-PUT behaviour, a real container for doctor, real argv handed to the real allowlist and argparse parsers. Three bugs on this branch had passing tests that couldn't have caught them.
  • Verified on the live rig: Stacky cites the same about.md path the CLI returns.
  • The model prefers generic file tools over specialised ones, so whatever is mounted at vault/ is the agent's whole picture of the family. Tool descriptions don't steer it.
  • Parked, not fixed here: FAM-15, FAM-16, FAM-17.
  • c57af19 came from a Gemini session; its test needed two follow-up corrections.

Actionable items move to the board; docs/design-notes.md keeps the
reasoning - decisions, rejected approaches, unresolved tensions.
stacktests preflight checks versions, uv.lock, ruff and unit tests in
one command - the same checks a release gate needs, so local and CI
cannot drift. stacktests help now exists and states which commands are
safe to run without a human; before, --help crashed on Python 3.9.

Also guards against unpinned container images, which is how Paperless
rolled from 2.x to 3.0.2 unattended and broke document filing.
Tests assumed the realm was always test.local. When a container carried
a different one, every bot login failed with a bare 403 and no clue why
- which is how the dev instance lost all four of its bots.

The rig and the demo are now one Simpsons instance on a single realm,
read from stack.toml, so the two can no longer disagree.
`stacktests ai [local|mock|external]` flips the rig's model endpoint in
one command, so testing against a real model no longer means waiting for
the ai stacklet to install and load weights.

local is the mode to reach for: a self-hosted endpoint gives real answers
at no cost per call, and only costs a little speed. mock stays available
for tests that assert on exact model output, but a green mock run proves
the wiring, not the behaviour.

Endpoints, keys, and model names are read from the environment rather
than committed, so no one's infrastructure ends up in the repo.
`stack status` tells you a stacklet is failing. `stack doctor` tells you
why, and prints the command that fixes it.

It catches the failure that took this instance's bots down: a container
keeps the environment it was created with, so editing stack.toml changes
nothing until the container is recreated, and every login fails against
config that looks correct on disk. Doctor also names exited containers
with their exit code and age, and flags a configured AI endpoint that
isn't answering.

The checks are deliberately generic. Nothing here knows what Matrix is,
so the same rules catch the same class of problem for any stacklet.
Findings print setting names only, never values, since this environment
carries passwords and tokens.

Exits non-zero when it finds errors, so scripts can gate on it.

Also scope preflight's unit run to `make test-unit`. Re-spelling the
pytest arguments had already dropped its --ignore, pulling Docker tests
into what is meant to be the offline gate.
When COMPOSE_PROFILES excludes every service in a compose file,
docker compose exits 1 with "no service selected". That is an empty
selection, not a service that failed to start, but the CLI read any
non-zero as "Failed to start services" and refused to write the
setup marker.

The ai stacklet is the case that surfaced it: STACK_AI_NO_VOICE=1
clears the profile and its only container sits behind "voice", so
the documented local-dev opt-out could never complete setup, which
in turn blocked everything that requires the ai stacklet.
The command died with "No module named 'frontmatter'" on any host
that had not run pip install. It read the vault through loaders that
imported the third-party python-frontmatter package, which ships only
in the test extra, so the suite stayed green while the command was
broken for everyone. stack memory correspondents had the same defect.

Both loaders now use lib/stack/frontmatter.py, the stdlib-only parser
already built for exactly this, keeping the CLI's zero-pip-deps
promise. List fields go through _fm_list so a single value written
where a list belongs can no longer iterate into one entry per
character.

The shipped correspondents seed taught inline "[a, b]" list syntax,
which the vault format spec excludes and the parser does not read.
Seed and test fixture now use block lists.
The agent shipped with memory_search and memory_person registered and
both broken. Four faults, none visible to a unit test:

- memory person was missing from the core API allowlist, so every call
  returned "'memory person' is not allowed"
- memory_search passed --backend mem0, a flag that never existed, so
  every search returned a usage error and the agent looped retrying
- stack memory person read the memory source clone, but profiles are
  generated into the brain projection and purged out of source, so it
  answered "no profile" for every household member who had one
- the agent mounted the source clone at vault/, so its file tools could
  not see a profile either and it told Homer "there is no
  vault/homer/about.md" when the page existed

Asking Stacky about a family member now cites vault/homer/about.md,
the same path stack memory person returns.
@arthware-dev
arthware-dev merged commit 2d18798 into main Jul 31, 2026
1 check passed
@arthware-dev
arthware-dev deleted the chore/agent-self-loop branch August 2, 2026 05:42
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.

1 participant