CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m14s
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>
47 lines
1.3 KiB
Python
47 lines
1.3 KiB
Python
"""Entry point for the dev agent.
|
|
|
|
Loads configuration, builds the agent, and stays up until told to stop.
|
|
Shutdown is deliberate rather than abrupt: A2's durability guarantees assume
|
|
the process may die at any moment, but a clean stop still beats relying on
|
|
them.
|
|
"""
|
|
|
|
import os
|
|
import signal
|
|
import sys
|
|
from pathlib import Path
|
|
from types import FrameType
|
|
|
|
from nisse_dev.agent import build_agent
|
|
from nisse_dev.config import AgentConfig, ConfigError
|
|
from nisse_dev.envfile import load_env_file
|
|
|
|
|
|
def main() -> int:
|
|
load_env_file(Path(os.environ.get("NISSE_ENV", ".env")).expanduser())
|
|
try:
|
|
config = AgentConfig.from_env()
|
|
except ConfigError as exc:
|
|
# Fail here rather than twenty minutes into a milestone on the one
|
|
# call that needed the missing value.
|
|
print(f"nisse-dev: {exc}", file=sys.stderr)
|
|
return 2
|
|
|
|
agent = build_agent(config)
|
|
|
|
def shutdown(_signum: int, _frame: FrameType | None) -> None:
|
|
print("nisse-dev: stopping", file=sys.stderr)
|
|
agent.stop()
|
|
|
|
signal.signal(signal.SIGINT, shutdown)
|
|
signal.signal(signal.SIGTERM, shutdown)
|
|
|
|
agent.start()
|
|
print(f"nisse-dev: ready as {agent.account} on {config.project}", file=sys.stderr)
|
|
agent.wait()
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|