Answering "is the workspace where the agent runs, or what it works
on?" — it is what it works on, and conflating the two is dangerous
rather than merely untidy.
The agent commits its workspace with git add -A and pushes it, so
anything inside the clone reaches the project repository. A .env
placed there would publish the LiteLLM, Matrix and Gitea tokens as a
side effect of the agent working correctly: silently, once, and
probably unnoticed. Startup now refuses both that and a state store
inside the clone, and .env.example documents the intended layout.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nothing had ever assembled the agent. main.py was still the hello-world
placeholder from the scaffold, and no code outside the test suite
constructed an Orchestrator — which is how two fully built, fully
tested components came to be reachable from nothing: the delegation
harness, and the Gitea comment poller. Unit tests that construct a
component directly cannot catch that; only assembly can.
- config.py validates every variable before anything is built and
names all of them at once, rather than failing at first use twenty
minutes into a milestone on the call that mattered. Whitespace-only
counts as missing, since a token of spaces passes a truthiness check
and fails at the first request instead
- agent.py builds the whole graph and owns startup and shutdown order:
reconcile before accepting commands, and on stop, stop watching,
then stop talking, then close the store last
- The Gitea account is read via whoami rather than configured: it names
the identity every permission is evaluated against, and proves the
token works before anything depends on it
- Poller lifecycle is now a state-machine concern: watch the plan issue
while a gate stands, the PR while it is open, and stop on leaving.
The orchestrator owns when to watch; an injected factory owns how to
build one
- The executor reports each turn, because it runs synchronously inside
the transport callback: without it the agent is silent for a whole
milestone and a working run looks exactly like a hung one
- main.py loads .env, builds, and shuts down cleanly on SIGINT/SIGTERM
The entry-point tests changed shape: nisse-dev no longer greets, it
refuses to start unconfigured and says everything that is missing. Both
tests now run with a deliberately empty environment and a .env path
that does not exist, because the working directory's .env holds live
credentials and a test must never be one cwd away from starting a real
agent.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>