M2.5, PR B: the new-project sequence agreed in #104 (D1, D4, D8). With #106, M2.5 is done.
The flow
Proposal. In a conversation, propose_project(name, purpose, decisions) goes up as rev-N for a go-ahead or a plain yes. It's checked: the name is a slug and not an existing project, and there's no trailing question.
Checklist. On go, the agent posts it:
a repository with a README, so the default branch exists;
protection on that branch;
the agent as a collaborator with write access;
a build VM;
a deploy key;
the project entry as YAML, with <...> placeholders to fill in.
"done".
Reload: the agent reads its configuration again and adds the new project live. Only additions are picked up live. The new build_loop_agent(config, load=…) reads the file by default.
Checks (verified, not assumed, per D4):
the repository's permissions and the default branch's protection as the agent sees them;
then in practice, on a throwaway branch with an empty commit: the branch push must succeed, and both a push to the default branch and merging its own PR must be refused. The PR is closed and the branch deleted.
The practical check is needed because the agent can't read the protection rule itself (403), and Gitea's user_can_merge is true even when an approval is required.
Failures: any check that fails is reported, and nothing starts.
Record. Once all checks pass, the agent opens the tracking issue ("Implement the build plan for …", with the distilled decisions and the new item's ID). The project's item starts agreed and labelled design, so its first milestone is ARCHITECTURE.md and the build plan.
A conversation waiting on provisioning doesn't close as idle.
Also
The walk replays exactly from its seed. Item IDs come from a seeded uuid4. Simulated restarts keep event IDs unique, as a server's are. A reused $event3 had sent one conversation's reply to another, which showed up as a false go_binds failure.
nisse-test.yaml holds output to 8,192 tokens. The test key's per-request cost cap ($0.10, worst case including reserved output) refused one long e2e call at the default 16,384.
Tests
test_loop_new_projects.py:
the checklist, whose YAML parses as configuration once filled in;
"done" before the entry is added;
an unprotected repository named as not ready, with the check's PR closed;
a checked project's record issue, its design item and first PR (ARCHITECTURE.md plus a plan, "Part of #N");
an existing name sent back.
The fake forge enforces protection with a pre-receive hook on its bare repository.
Walk: it now proposes projects and says "done" without an entry.
E2E: a conversation proposes a project. The scripted operator provisions it from the agent's checklist, using a fresh_repo without the seed, and the agent's YAML filled in. The agent checks it all and opens a first PR with ARCHITECTURE.md and a plan.
Results: the full suite's 8 tests all passed. One was re-run after the token change, after this afternoon's budget refusals. Unit (708) and integration (21) tiers pass, and no test repos were left behind.
Added after the first local e2e run (rebased onto #110)
On qwen3.8-27b, the new project's first PR held its code as well as ARCHITECTURE.md, with no build plan: D1's "design before any code" rested on wording only. Now it's enforced:
Until a design item's PR has merged,finish_milestone is sent back if the milestone changes anything but ARCHITECTURE.md and docs/. The message names the paths to take out. The merge is recorded (design_merged), and code may follow.
The wording of the label's instruction and the new project's brief now says plainly: documents first, in a PR of their own.
Tests: a design milestone with code is sent back and only the documents are published, then code is allowed after the merge. The walk's scripted model follows the rule.
CI now runs the e2e suite on this PR, on the local model.
M2.5, PR B: the new-project sequence agreed in #104 (D1, D4, D8). With #106, M2.5 is done.
## The flow
1. **Proposal.** In a conversation, `propose_project(name, purpose, decisions)` goes up as `rev-N` for a go-ahead or a plain yes. It's checked: the name is a slug and not an existing project, and there's no trailing question.
2. **Checklist.** On go, the agent posts it:
- a repository with a README, so the default branch exists;
- protection on that branch;
- the agent as a collaborator with write access;
- a build VM;
- a deploy key;
- the project entry as YAML, with `<...>` placeholders to fill in.
3. **"done".**
- **Reload:** the agent reads its configuration again and adds the new project live. Only additions are picked up live. The new `build_loop_agent(config, load=…)` reads the file by default.
- **Checks** (verified, not assumed, per D4):
- the repository's permissions and the default branch's protection as the agent sees them;
- then in practice, on a throwaway branch with an empty commit: the branch push must succeed, and both a push to the default branch and merging its own PR must be refused. The PR is closed and the branch deleted.
- The practical check is needed because the agent can't read the protection rule itself (403), and Gitea's `user_can_merge` is true even when an approval is required.
- **Failures:** any check that fails is reported, and nothing starts.
4. **Record.** Once all checks pass, the agent opens the tracking issue ("Implement the build plan for …", with the distilled decisions and the new item's ID). The project's item starts agreed and labelled `design`, so its first milestone is `ARCHITECTURE.md` and the build plan.
A conversation waiting on provisioning doesn't close as idle.
## Also
- **The walk replays exactly from its seed.** Item IDs come from a seeded `uuid4`. Simulated restarts keep event IDs unique, as a server's are. A reused `$event3` had sent one conversation's reply to another, which showed up as a false `go_binds` failure.
- **`nisse-test.yaml`** holds output to 8,192 tokens. The test key's per-request cost cap ($0.10, worst case including reserved output) refused one long e2e call at the default 16,384.
## Tests
- **`test_loop_new_projects.py`:**
- the checklist, whose YAML parses as configuration once filled in;
- "done" before the entry is added;
- an unprotected repository named as not ready, with the check's PR closed;
- a checked project's record issue, its `design` item and first PR (`ARCHITECTURE.md` plus a plan, "Part of #N");
- an existing name sent back.
- **The fake forge enforces protection** with a `pre-receive` hook on its bare repository.
- **Walk:** it now proposes projects and says "done" without an entry.
- **E2E:** a conversation proposes a project. The scripted operator provisions it from the agent's checklist, using a `fresh_repo` without the seed, and the agent's YAML filled in. The agent checks it all and opens a first PR with `ARCHITECTURE.md` and a plan.
- **Results:** the full suite's 8 tests all passed. One was re-run after the token change, after this afternoon's budget refusals. Unit (708) and integration (21) tiers pass, and no test repos were left behind.
## Added after the first local e2e run (rebased onto #110)
On qwen3.8-27b, the new project's first PR held its code as well as `ARCHITECTURE.md`, with no build plan: D1's "design before any code" rested on wording only. Now it's enforced:
- **Until a `design` item's PR has merged,** `finish_milestone` is sent back if the milestone changes anything but `ARCHITECTURE.md` and `docs/`. The message names the paths to take out. The merge is recorded (`design_merged`), and code may follow.
- **The wording** of the label's instruction and the new project's brief now says plainly: documents first, in a PR of their own.
- **Tests:** a design milestone with code is sent back and only the documents are published, then code is allowed after the merge. The walk's scripted model follows the rule.
- **CI** now runs the e2e suite on this PR, on the local model.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
- propose_project(name, purpose, decisions) in a conversation goes up for
a go-ahead (checked: a slug, not an existing project, no trailing
question).
- On go, the provisioning checklist (D1): a repository with a README,
protection on its default branch, the agent as a collaborator, a build
VM, a deploy key, and the project entry as YAML with <...> to fill in.
- On "done", the agent reads its configuration again and adds the new
project live (build_loop_agent(load=...); only additions), then checks
what was provided (D4, verified, not assumed): permissions and
protection as it sees them, then on a throwaway branch with an empty
commit, a branch push must succeed and a push to the default branch and
merging its own PR must be refused. The check's PR is closed and its
branch deleted. Failures are said per item; nothing starts.
- Checked: the tracking issue (the distilled decisions, carrying the new
item's ID), and the project's item, agreed and labelled `design`, whose
first milestone is ARCHITECTURE.md and the build plan. A conversation
waiting on provisioning doesn't close as idle.
- The walk replays exactly from its seed (item IDs too), and event IDs
stay unique across simulated restarts, as a server's are.
- The test config holds output to 8192 tokens, under the test key's
per-request cost cap.
Tests: new-project unit tests (checklist and its YAML parse as config, done
before the entry, what isn't set up, a checked project's record and first
PR, an existing name), protection enforced by a hook on the fake forge,
the walk proposing projects and saying done, and an e2e new project in a
repository the test operator provisions from the agent's checklist.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
Found by the first local e2e run: qwen3.8-27b put a new project's code in
the same PR as its ARCHITECTURE.md, with no build plan. D1 says a `design`
item agrees its design before any code, and that rested on wording only.
- Until a PR of a `design` item has merged, finish_milestone is sent back
if the milestone changes anything but ARCHITECTURE.md and docs/, naming
the paths to take out. The merge is recorded (design_merged) and code
may follow.
- The label's instruction and the new project's brief say so plainly:
documents first, in a PR of their own; code after it has merged.
Tests: a design milestone with code is sent back and only the documents
are published, then code after the merge; the walk's model follows the
rule.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
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.
M2.5, PR B: the new-project sequence agreed in #104 (D1, D4, D8). With #106, M2.5 is done.
The flow
propose_project(name, purpose, decisions)goes up asrev-Nfor a go-ahead or a plain yes. It's checked: the name is a slug and not an existing project, and there's no trailing question.<...>placeholders to fill in.build_loop_agent(config, load=…)reads the file by default.user_can_mergeis true even when an approval is required.design, so its first milestone isARCHITECTURE.mdand the build plan.A conversation waiting on provisioning doesn't close as idle.
Also
uuid4. Simulated restarts keep event IDs unique, as a server's are. A reused$event3had sent one conversation's reply to another, which showed up as a falsego_bindsfailure.nisse-test.yamlholds output to 8,192 tokens. The test key's per-request cost cap ($0.10, worst case including reserved output) refused one long e2e call at the default 16,384.Tests
test_loop_new_projects.py:designitem and first PR (ARCHITECTURE.mdplus a plan, "Part of #N");pre-receivehook on its bare repository.fresh_repowithout the seed, and the agent's YAML filled in. The agent checks it all and opens a first PR withARCHITECTURE.mdand a plan.Added after the first local e2e run (rebased onto #110)
On qwen3.8-27b, the new project's first PR held its code as well as
ARCHITECTURE.md, with no build plan: D1's "design before any code" rested on wording only. Now it's enforced:designitem's PR has merged,finish_milestoneis sent back if the milestone changes anything butARCHITECTURE.mdanddocs/. The message names the paths to take out. The merge is recorded (design_merged), and code may follow.🤖 Generated with Claude Code
https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
dd6196c742to88c7d2e8c9