New projects from conversation (M2.5, PR B) #108

Merged
dev-agent merged 2 commits from new-projects into main 2026-10-07 16:25:41 +00:00
Collaborator

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.ai/code/session_01PPQ14L3LikyefCMFBqEbKE

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
dev-agent added 2 commits 2026-10-07 15:39:11 +00:00
- 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
A design milestone holds documents only, enforced
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 5m4s
CI / e2e (pull_request) Successful in 24m22s
88c7d2e8c9
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
dev-agent force-pushed new-projects from dd6196c742 to 88c7d2e8c9 2026-10-07 15:39:11 +00:00 Compare
dongar approved these changes 2026-10-07 16:24:58 +00:00
dev-agent merged commit 04c352b30c into main 2026-10-07 16:25:41 +00:00
dev-agent deleted branch new-projects 2026-10-07 16:25:41 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dev/nisse#108