Projects as configuration entries (M1.4, D8)
CI / checks (push) Skipped
CI / checks (pull_request) Failing after 5m7s
CI / checks (push) Skipped
CI / checks (pull_request) Failing after 5m7s
- Config: `projects: {name: entry}` with repo, base_branch, workspace,
build_host, push credential, labels (defaults added to every issue),
secrets by ${VAR} reference with visibility (commands | harness), and
egress (recorded, not enforced). Every problem in every entry is
reported at once; an inline secret, a bad visibility, a repeated repo or
a non-slug name is an error.
- The old loop runs exactly one project (only_project) and refuses more.
- The new loop is per project: each its own clone, workspace (with its
commands-visible secrets, redacted on return), push credential and
milestones; intake polls every project's repository; items are scoped
to their project for every run, publish and PR watch. Issues are named
project#14 when there is more than one project, and a bare #14 that
matches two is asked about. Switching projects parks the one worked on
last (all others after a boot).
- A restatement that ends in a question is sent back to the driver, not
posted: the e2e driver kept asking "Is this the right interpretation?",
which a plain yes can't answer.
Tests: config entries and errors, two-project assembly, two-project loop
tests (intake, own workspaces and repos, #14 in both, do-next, label
defaults, parking across projects), the property walk over two projects
with a new own_repository_only property (an item using the first
project's repo fails 15 of 16 seeds), and the restatement check.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
This commit is contained in:
1 parent
4ecff19933
commit
064d9bf554
17 files changed
+1139
-315
No files matched your search
@@ -14,7 +14,7 @@ from dataclasses import dataclass
|
||||
from nisse_dev.approvals import PlanBoard
|
||||
from nisse_dev.cancel import Cancel
|
||||
from nisse_dev.catalog import ModelCatalog, select_client
|
||||
from nisse_dev.config import AgentConfig
|
||||
from nisse_dev.config import AgentConfig, Project
|
||||
from nisse_dev.context import BudgetedClient
|
||||
from nisse_dev.control import Orchestrator
|
||||
from nisse_dev.delegation import Caps, Delegation
|
||||
@@ -165,30 +165,34 @@ def make_llm_clients(
|
||||
return normalized, passthrough
|
||||
|
||||
|
||||
def build_host(config: AgentConfig) -> SSHHost:
|
||||
"""The tool VM the workspace lives on (L13). Its known-hosts file and
|
||||
connection socket live beside the state store."""
|
||||
def build_host(config: AgentConfig, project: Project | None = None) -> SSHHost:
|
||||
"""A project's build VM (L13; the only project's, if none is named). Its
|
||||
known-hosts file and connection socket live beside the state store."""
|
||||
host = (project or config.only_project()).build_host
|
||||
return SSHHost(
|
||||
config.build_host.address,
|
||||
key=config.build_host.key,
|
||||
user=config.build_host.user,
|
||||
port=config.build_host.port,
|
||||
host.address,
|
||||
key=host.key,
|
||||
user=host.user,
|
||||
port=host.port,
|
||||
state_dir=config.state_db.parent / "ssh",
|
||||
)
|
||||
|
||||
|
||||
def push_credential(config: AgentConfig) -> PushCredential | None:
|
||||
"""What pushes go by: a push-only account's token over HTTPS, or a
|
||||
deploy key over SSH (D4)."""
|
||||
if config.push_user and config.push_token:
|
||||
return HTTPSToken(config.push_user, config.push_token)
|
||||
if config.push_key is None:
|
||||
def push_credential(
|
||||
config: AgentConfig, project: Project | None = None
|
||||
) -> PushCredential | None:
|
||||
"""What a project's pushes go by: a push-only account's token over
|
||||
HTTPS, or a deploy key over SSH (D4)."""
|
||||
project = project or config.only_project()
|
||||
if project.push_user and project.push_token:
|
||||
return HTTPSToken(project.push_user, project.push_token)
|
||||
if project.push_key is None:
|
||||
return None
|
||||
try:
|
||||
return SSHKey(config.push_key.read_text())
|
||||
return SSHKey(project.push_key.read_text())
|
||||
except OSError as exc:
|
||||
raise RuntimeError(
|
||||
f"cannot read the push key {config.push_key}: {exc}"
|
||||
f"cannot read the push key {project.push_key}: {exc}"
|
||||
) from exc
|
||||
|
||||
|
||||
@@ -205,6 +209,7 @@ def prepare_workspace(
|
||||
account: str,
|
||||
remote: str,
|
||||
credential: PushCredential | None,
|
||||
project: Project | None = None,
|
||||
) -> GitRepo:
|
||||
"""Ensure the workspace on the build VM is a clone of the project repo.
|
||||
|
||||
@@ -213,7 +218,7 @@ def prepare_workspace(
|
||||
Cloning here rather than when a work item starts is the single-project
|
||||
shortcut that M1.4 and D3's fresh workspace replace.
|
||||
"""
|
||||
repo = GitRepo(host, config.workspace)
|
||||
repo = GitRepo(host, (project or config.only_project()).workspace)
|
||||
if not repo.exists():
|
||||
repo.clone(remote, credential=credential)
|
||||
repo.configure_identity(account, f"{account}@users.noreply.local")
|
||||
|
||||
Reference in new issue
Block a user