Deferred by decision — filed so it is not lost. This is debt against the spec, not new scope.
What the spec already requires
A2: "Must persist: active project/task, per-project state-machine position, ... (dev) git clone locations." Plural in the spec.
B1: IDLE → [use <project>]"clone repo, begin architecture planning".
What we built instead
agent_state is a single row (id = 1). It holds the position, not one per project, so switching projects would overwrite rather than park.
use <project> always starts fresh; there is no resuming a project left at, say, PR_OPEN.
The repo is global config (NISSE_GITEA_TEST_REPO), so a project name does not resolve to anything and nothing is cloned.
That is sufficient while exactly one project is ever known, which is why it has not bitten.
Work
Schema migration: a projects table keyed by project name (state, data, updated-at) plus an active-project pointer; the existing single row migrates in as the first project.
Per-project descriptor for what is global config today: repo, workspace/clone path, plan-board issue, open PR, and the last-seen comment id.
use <project> resolves the name against the forge (on a single instance the forge is external truth for what exists), clones if needed, and resumes an existing project rather than wiping it.
status reports the active project and what is parked elsewhere.
One bug this fixes
CommentPoller.prime() marks existing comments seen so the agent does not re-address old feedback — correct on first attach. But a poller recreated when switching back to a project primes again and silently swallows every comment made while away. The last-seen cursor has to live in per-project durable state rather than poller memory.
Ordering note
Cheaper before the step-10 nisse-core extraction than after, when the same change lands in a library with two consumers.
Deferred by decision — filed so it is not lost. This is **debt against the spec**, not new scope.
### What the spec already requires
- **A2**: "Must persist: active project/task, **per-project state-machine position**, ... (dev) **git clone locations**." Plural in the spec.
- **B1**: `IDLE → [use <project>]` **"clone repo, begin architecture planning"**.
### What we built instead
- `agent_state` is a single row (`id = 1`). It holds *the* position, not one per project, so switching projects would overwrite rather than park.
- `use <project>` always starts fresh; there is no resuming a project left at, say, `PR_OPEN`.
- The repo is global config (`NISSE_GITEA_TEST_REPO`), so a project name does not resolve to anything and nothing is cloned.
That is sufficient while exactly one project is ever known, which is why it has not bitten.
### Work
- Schema migration: a `projects` table keyed by project name (state, data, updated-at) plus an active-project pointer; the existing single row migrates in as the first project.
- Per-project descriptor for what is global config today: repo, workspace/clone path, plan-board issue, open PR, and the **last-seen comment id**.
- `use <project>` resolves the name against the forge (on a single instance the forge is external truth for what exists), clones if needed, and **resumes** an existing project rather than wiping it.
- `status` reports the active project and what is parked elsewhere.
### One bug this fixes
`CommentPoller.prime()` marks existing comments seen so the agent does not re-address old feedback — correct on first attach. But a poller recreated when switching back to a project primes again and **silently swallows every comment made while away**. The last-seen cursor has to live in per-project durable state rather than poller memory.
### Ordering note
Cheaper before the step-10 `nisse-core` extraction than after, when the same change lands in a library with two consumers.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Deferred by decision — filed so it is not lost. This is debt against the spec, not new scope.
What the spec already requires
IDLE → [use <project>]"clone repo, begin architecture planning".What we built instead
agent_stateis a single row (id = 1). It holds the position, not one per project, so switching projects would overwrite rather than park.use <project>always starts fresh; there is no resuming a project left at, say,PR_OPEN.NISSE_GITEA_TEST_REPO), so a project name does not resolve to anything and nothing is cloned.That is sufficient while exactly one project is ever known, which is why it has not bitten.
Work
projectstable keyed by project name (state, data, updated-at) plus an active-project pointer; the existing single row migrates in as the first project.use <project>resolves the name against the forge (on a single instance the forge is external truth for what exists), clones if needed, and resumes an existing project rather than wiping it.statusreports the active project and what is parked elsewhere.One bug this fixes
CommentPoller.prime()marks existing comments seen so the agent does not re-address old feedback — correct on first attach. But a poller recreated when switching back to a project primes again and silently swallows every comment made while away. The last-seen cursor has to live in per-project durable state rather than poller memory.Ordering note
Cheaper before the step-10
nisse-coreextraction than after, when the same change lands in a library with two consumers.