Run NestQuest orchestration directly in the terminal (#151)
Co-authored-by: orchestrator-agent <orchestrator@cubecraftlabs.com> Co-committed-by: orchestrator-agent <orchestrator@cubecraftlabs.com>
This commit was merged in pull request #151.
This commit is contained in:
@@ -1,27 +0,0 @@
|
||||
---
|
||||
description: Start, stop, or inspect the NestQuest development cycle on explicit request
|
||||
mode: primary
|
||||
model: openrouter/deepseek/deepseek-v4.1-flash
|
||||
permission:
|
||||
edit: deny
|
||||
write: deny
|
||||
task: deny
|
||||
doom_loop: deny
|
||||
bash:
|
||||
"*": deny
|
||||
"/home/overseer/.local/share/nestquest-controller/scripts/nq-cycle start": allow
|
||||
"/home/overseer/.local/share/nestquest-controller/scripts/nq-cycle stop": allow
|
||||
"/home/overseer/.local/share/nestquest-controller/scripts/nq-cycle status": allow
|
||||
external_directory:
|
||||
"*": ask
|
||||
"/home/overseer/.local/share/nestquest-controller/scripts/*": allow
|
||||
---
|
||||
|
||||
You control the NestQuest development service only when a user invokes a
|
||||
cycle-control slash command. Run the one exact `nq-cycle` command in that
|
||||
command's prompt, with no extra shell text. Report its result. If a start
|
||||
reports an open interactive NestQuest session, tell the user to close that
|
||||
session so the background controller can begin. Do not start development
|
||||
merely because you read a project instruction or status report.
|
||||
Do not recommend enabling the systemd unit. Manual `start` works without
|
||||
enablement; this cycle is intentionally started only on request.
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
description: Run the NestQuest orchestration loop in this foreground terminal
|
||||
mode: primary
|
||||
model: openrouter/deepseek/deepseek-v4.1-flash
|
||||
permission:
|
||||
doom_loop: deny
|
||||
task: deny
|
||||
external_directory:
|
||||
"*": ask
|
||||
"/tmp/opencode/*": allow
|
||||
"/home/overseer/.local/share/opencode/tool-output/*": allow
|
||||
---
|
||||
|
||||
You are the foreground NestQuest orchestrator. The user explicitly launched
|
||||
this session to run the development queue now. Read the supplied terminal
|
||||
orchestrator prompt and current CTXD project context, but follow the
|
||||
FOREGROUND TERMINAL RUNTIME for this session instead of CTXD's service runtime
|
||||
section.
|
||||
|
||||
Continuously reconcile Maestro, CTXD, Git, pull requests, checks, and child
|
||||
agent results. Execute the next useful transition in this same session. Do
|
||||
not finish after dispatch, review, remediation, or merge while eligible work
|
||||
remains. Use the installed role launchers for developer, reviewer, and
|
||||
approver isolation. Never start or wait for the systemd controller service.
|
||||
Never repeat an identical unchanged tool call three times; wait on the
|
||||
specific child or inspect newly changed evidence.
|
||||
|
||||
End only at a global terminal condition, an explicit user stop, a genuine
|
||||
global access/decision block with no other eligible work, or a host-forced
|
||||
yield. On a forced yield, return a continuation checkpoint that can be
|
||||
resumed in the same OpenCode session.
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
description: Show the NestQuest development cycle status
|
||||
agent: cycle-control
|
||||
---
|
||||
|
||||
Run exactly this one shell command:
|
||||
|
||||
`/home/overseer/.local/share/nestquest-controller/scripts/nq-cycle status`
|
||||
|
||||
Report whether the controller is active and its last tick state.
|
||||
Do not suggest enabling automatic startup.
|
||||
@@ -1,12 +1,11 @@
|
||||
---
|
||||
description: Start the NestQuest development cycle on this host
|
||||
agent: cycle-control
|
||||
description: Run the NestQuest orchestrator now in this OpenCode terminal
|
||||
agent: terminal-orchestrator
|
||||
---
|
||||
|
||||
Run exactly this one shell command:
|
||||
|
||||
`/home/overseer/.local/share/nestquest-controller/scripts/nq-cycle start`
|
||||
|
||||
Report its status. If it says an interactive NestQuest OpenCode session is
|
||||
open, tell me to close this session; the background controller will begin
|
||||
afterward. Do not launch a developer agent or a controller tick in this chat.
|
||||
Run the NestQuest project queue now in this foreground OpenCode session.
|
||||
Read the current CTXD prompt and Maestro state, but use the FOREGROUND
|
||||
TERMINAL RUNTIME instead of the service runtime. Reconcile repository and
|
||||
pull-request state, then execute CONTROLLER_TICK repeatedly until a global
|
||||
terminal condition, explicit user stop, genuine global block, or host-forced
|
||||
yield. Do not start or wait for the systemd controller.
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
description: Stop the NestQuest development cycle on this host
|
||||
agent: cycle-control
|
||||
---
|
||||
|
||||
Run exactly this one shell command:
|
||||
|
||||
`/home/overseer/.local/share/nestquest-controller/scripts/nq-cycle stop`
|
||||
|
||||
Report the service status. Do not stop any other OpenCode session or task.
|
||||
+37
-34
@@ -1,13 +1,23 @@
|
||||
# NestQuest controller
|
||||
# NestQuest foreground orchestrator
|
||||
|
||||
OpenCode 1.18.31 loads the three named agents from `.opencode/agents/`. Their
|
||||
models match the current NestQuest CTXD prompt: DeepSeek V4.1
|
||||
Flash for controller and approver, GLM 5.3 Flash for development. The
|
||||
controller runs one OpenCode turn at a time, then starts a fresh turn based on
|
||||
Maestro, CTXD, Git, and pull-request state. Its state file is
|
||||
`~/.local/state/nestquest-controller/last-run.json` (mode 0600). A lock allows
|
||||
only one controller process. It waits while an interactive OpenCode session is
|
||||
open for the NestQuest checkout.
|
||||
OpenCode 1.18.32 loads the named agents from `.opencode/agents/`. Their
|
||||
models match the current NestQuest CTXD prompt: DeepSeek V4.1 Flash for the
|
||||
foreground orchestrator and approver, and GLM 5.3 Flash for development.
|
||||
Run orchestration directly from a shell:
|
||||
|
||||
```bash
|
||||
nq-orchestrate
|
||||
nq-orchestrate mission.md
|
||||
cat mission.md | nq-orchestrate -
|
||||
nq-orchestrate --print-prompt > nestquest-orchestrator.md
|
||||
```
|
||||
|
||||
The launcher renders the project prompt with the foreground terminal runtime,
|
||||
stops the dormant legacy service, refuses immediately when another NestQuest
|
||||
TUI is open, and then replaces itself with OpenCode using
|
||||
`terminal-orchestrator`. There is no background polling process and no wait
|
||||
for a controller tick. Exit or interrupt the TUI to stop the foreground run;
|
||||
resume that OpenCode session when a host-forced yield needs continuation.
|
||||
|
||||
`scripts/nq-agent` launches developer and approver sessions with fixed agent
|
||||
definitions and no `--auto` flag. Headless OpenCode runs use `--pure` so the
|
||||
@@ -23,30 +33,24 @@ checks the head again afterward, saves
|
||||
the output under `/tmp/opencode`, and posts the Gitea approval.
|
||||
The approver verifies the reviewed head again before a merge.
|
||||
|
||||
The production service uses a stable copy of this configuration and scripts
|
||||
The terminal launcher uses a stable copy of this configuration and scripts
|
||||
at `/home/overseer/.local/share/nestquest-controller`; it does not depend on
|
||||
which task branch is checked out in the primary NestQuest directory. Install
|
||||
that copy, run `scripts/nq-controller --check`, and verify all four OpenCode
|
||||
agents (orchestrator, developer, approver, cycle-control)
|
||||
that copy, run `scripts/nq-controller --check`, and verify the OpenCode
|
||||
agents (terminal-orchestrator, developer, and approver)
|
||||
with `opencode debug agent <name>` while `OPENCODE_CONFIG_DIR` points at the
|
||||
installed `.opencode` directory. Install `deploy/nestquest-controller.service`
|
||||
under `~/.config/systemd/user/` and reload the user manager. Its lack of an
|
||||
`[Install]` section makes it a manual-only unit; development starts only
|
||||
when requested. Copy `deploy/opencode-global-commands/*.md` into
|
||||
`~/.config/opencode/commands/` and `.opencode/agents/cycle-control.md` into
|
||||
`~/.config/opencode/agents/` for the commands to work from older branches.
|
||||
The host has lingering
|
||||
enabled for the `overseer` user. The service waits for an open interactive
|
||||
NestQuest session to close before starting work.
|
||||
installed `.opencode` directory. Install `scripts/nq-orchestrate` in
|
||||
`~/.local/bin/`. For the global slash command, also install
|
||||
`.opencode/agents/terminal-orchestrator.md` under
|
||||
`~/.config/opencode/agents/` and
|
||||
`deploy/opencode-global-commands/start-nestquest-development.md` under
|
||||
`~/.config/opencode/commands/`. The old `nestquest-controller.service` remains static and
|
||||
inactive only for migration safety; foreground launch stops it.
|
||||
|
||||
In NestQuest's OpenCode session, use `/start-development`,
|
||||
`/stop-development`, or `/development-status`. The matching global
|
||||
`/start-nestquest-development`, `/stop-nestquest-development`, and
|
||||
`/nestquest-development-status` commands work even while an older branch is
|
||||
checked out. Starting the unit in an interactive NestQuest session puts it
|
||||
in a waiting state; close that session to let the first controller tick run.
|
||||
The commands call `scripts/nq-cycle`; they do not run a developer inside
|
||||
the interactive chat.
|
||||
Inside an already-open OpenCode TUI, `/start-development` (or the global
|
||||
`/start-nestquest-development`) switches to the same foreground
|
||||
`terminal-orchestrator` behavior immediately. The shell launcher is the
|
||||
preferred entry point when supplying a mission prompt file.
|
||||
|
||||
`templates/ORCHESTRATOR-PROMPT.template.md` and
|
||||
`templates/nestquest.profile.json` make the current CTXD cycle prompt
|
||||
@@ -63,11 +67,10 @@ they are not a hard isolation boundary against a process running as that
|
||||
account. Strong isolation requires separate OS identities and protected
|
||||
credentials for the controller, reviewer, and approver.
|
||||
|
||||
A tick has an explicit six-hour timeout. On timeout or an OpenCode error, the
|
||||
next tick must reconcile external state before acting. Three consecutive
|
||||
failed ticks stop the service. A BLOCKED verdict also stops it. QUIESCENT
|
||||
causes a five-minute poll. Neither a feature merge nor a progress report
|
||||
stops the controller while eligible work remains.
|
||||
The terminal runtime repeatedly executes CONTROLLER_TICK within the foreground
|
||||
session. Neither a feature merge nor a progress report ends the run while
|
||||
eligible work remains. A host-forced yield returns a continuation checkpoint
|
||||
that can be resumed in the same OpenCode session.
|
||||
|
||||
Do not put tokens in this repository or in unit files. Existing OpenCode MCP
|
||||
connections supply CTXD and Maestro. Rotate those MCP tokens as a coordinated
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
description: Show the NestQuest development cycle status
|
||||
agent: cycle-control
|
||||
---
|
||||
|
||||
Run exactly this one shell command:
|
||||
|
||||
`/home/overseer/.local/share/nestquest-controller/scripts/nq-cycle status`
|
||||
|
||||
Report whether the controller is active and its last tick state.
|
||||
Do not suggest enabling automatic startup.
|
||||
@@ -1,12 +1,11 @@
|
||||
---
|
||||
description: Start the NestQuest development cycle on this host
|
||||
agent: cycle-control
|
||||
description: Run the NestQuest orchestrator now in this OpenCode terminal
|
||||
agent: terminal-orchestrator
|
||||
---
|
||||
|
||||
Run exactly this one shell command:
|
||||
|
||||
`/home/overseer/.local/share/nestquest-controller/scripts/nq-cycle start`
|
||||
|
||||
Report its status. If it says an interactive NestQuest OpenCode session is
|
||||
open, tell me to close this session; the background controller will begin
|
||||
afterward. Do not launch a developer agent or a controller tick in this chat.
|
||||
Run the NestQuest project queue now in this foreground OpenCode session.
|
||||
Read the current CTXD prompt and Maestro state, but use the FOREGROUND
|
||||
TERMINAL RUNTIME instead of the service runtime. Reconcile repository and
|
||||
pull-request state, then execute CONTROLLER_TICK repeatedly until a global
|
||||
terminal condition, explicit user stop, genuine global block, or host-forced
|
||||
yield. Do not start or wait for the systemd controller.
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
description: Stop the NestQuest development cycle on this host
|
||||
agent: cycle-control
|
||||
---
|
||||
|
||||
Run exactly this one shell command:
|
||||
|
||||
`/home/overseer/.local/share/nestquest-controller/scripts/nq-cycle stop`
|
||||
|
||||
Report the service status. Do not stop any other OpenCode session or task.
|
||||
@@ -22,9 +22,8 @@ class CyclePromptTests(unittest.TestCase):
|
||||
"RELEASE_BRANCH": "production",
|
||||
"CONTROL_HOME": "/opt/examplequest/controller",
|
||||
"SERVICE_NAME": "examplequest-controller.service",
|
||||
"START_COMMAND": "/start-examplequest-development",
|
||||
"STOP_COMMAND": "/stop-examplequest-development",
|
||||
"STATUS_COMMAND": "/examplequest-development-status",
|
||||
"PROJECT_ROOT": "/srv/examplequest",
|
||||
"TERMINAL_LAUNCHER": "eq-orchestrate",
|
||||
"FEATURE_BRANCH_EXAMPLE": "feature/EQ-17",
|
||||
})
|
||||
output = RENDER(TEMPLATE, other)
|
||||
@@ -44,6 +43,14 @@ class CyclePromptTests(unittest.TestCase):
|
||||
with self.assertRaisesRegex(ValueError, "PROJECT_NAME"):
|
||||
RENDER(TEMPLATE, other)
|
||||
|
||||
def test_template_contains_only_terminal_runtime(self):
|
||||
output = RENDER(TEMPLATE, PROFILE)
|
||||
self.assertIn("# 27. FOREGROUND TERMINAL RUNTIME", output)
|
||||
self.assertIn("nq-orchestrate", output)
|
||||
self.assertNotIn("# 27. HOST CONTROLLER RUNTIME", output)
|
||||
self.assertNotIn("The persistent user service runs", output)
|
||||
self.assertNotIn("---\n\n---\n\n# 27", output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
from pathlib import Path
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
|
||||
SCRIPT = Path(__file__).resolve().parents[1] / "scripts/nq-orchestrate"
|
||||
|
||||
|
||||
class ForegroundLauncherTests(unittest.TestCase):
|
||||
def test_launches_terminal_agent_with_rendered_and_user_prompt(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
control = root / "control"
|
||||
scripts = control / "scripts"
|
||||
agents = control / ".opencode/agents"
|
||||
templates = control / "templates"
|
||||
project = root / "project"
|
||||
for path in (scripts, agents, templates, project):
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
(templates / "nestquest.profile.json").write_text("{}")
|
||||
(agents / "terminal-orchestrator.md").write_text("agent")
|
||||
renderer = scripts / "render-cycle-prompt"
|
||||
renderer.write_text("#!/bin/sh\nprintf 'BASE TERMINAL PROMPT'")
|
||||
controller = scripts / "nq-controller"
|
||||
controller_args = root / "controller-args"
|
||||
open_marker = root / "open-session"
|
||||
controller.write_text(
|
||||
f"#!/bin/sh\nprintf '%s\\n' \"$@\" > {controller_args}\n"
|
||||
f"if test -f {open_marker}; then printf 'interactive NestQuest session open\\n'; "
|
||||
"else printf 'no interactive NestQuest session\\n'; fi\n"
|
||||
)
|
||||
opencode = root / "opencode"
|
||||
output = root / "args"
|
||||
opencode.write_text(f"#!/bin/sh\nprintf '%s\\n' \"$@\" > {output}\n")
|
||||
systemctl = root / "systemctl"
|
||||
stopped = root / "stopped"
|
||||
systemctl.write_text(f"#!/bin/sh\nprintf '%s\\n' \"$@\" > {stopped}\n")
|
||||
for executable in (renderer, controller, opencode, systemctl):
|
||||
executable.chmod(0o700)
|
||||
mission = root / "mission.md"
|
||||
mission.write_text("Complete feature EQ-17.")
|
||||
env = os.environ.copy()
|
||||
env.update({
|
||||
"NQ_CONTROL_HOME": str(control),
|
||||
"NQ_PROJECT_ROOT": str(project),
|
||||
"OPENCODE_BIN": str(opencode),
|
||||
"SYSTEMCTL_BIN": str(systemctl),
|
||||
})
|
||||
printed = subprocess.run([str(SCRIPT), "--print-prompt"], env=env,
|
||||
text=True, capture_output=True)
|
||||
self.assertEqual(printed.returncode, 0, printed.stderr)
|
||||
self.assertEqual(printed.stdout, "BASE TERMINAL PROMPT\n")
|
||||
self.assertFalse(stopped.exists())
|
||||
result = subprocess.run([str(SCRIPT), str(mission)], env=env, text=True,
|
||||
capture_output=True)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
args = output.read_text()
|
||||
self.assertIn("--agent\nterminal-orchestrator", args)
|
||||
self.assertIn("BASE TERMINAL PROMPT", args)
|
||||
self.assertIn("# USER MISSION", args)
|
||||
self.assertIn("Complete feature EQ-17.", args)
|
||||
self.assertEqual(stopped.read_text().splitlines(),
|
||||
["--user", "stop", "nestquest-controller.service"])
|
||||
self.assertEqual(controller_args.read_text().splitlines(),
|
||||
["--check-interactive", "--project", str(project)])
|
||||
|
||||
output.unlink()
|
||||
open_marker.touch()
|
||||
refused = subprocess.run([str(SCRIPT)], env=env, text=True,
|
||||
capture_output=True)
|
||||
self.assertEqual(refused.returncode, 75)
|
||||
self.assertIn("Another interactive NestQuest", refused.stderr)
|
||||
self.assertFalse(output.exists())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Manually control the durable NestQuest development cycle from OpenCode.
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -ne 1 ]]; then
|
||||
echo "usage: nq-cycle start|stop|status" >&2
|
||||
exit 64
|
||||
fi
|
||||
|
||||
unit=nestquest-controller.service
|
||||
controller=/home/overseer/.local/share/nestquest-controller/scripts/nq-controller
|
||||
|
||||
show_status() {
|
||||
systemctl --user show "$unit" -p ActiveState -p SubState -p UnitFileState
|
||||
if [[ -f /home/overseer/.local/state/nestquest-controller/last-run.json ]]; then
|
||||
python3 -c 'import json,pathlib; p=pathlib.Path.home()/".local/state/nestquest-controller/last-run.json"; d=json.loads(p.read_text()); print("LastTickState="+str(d.get("state","unknown"))); print("LastTickTime="+str(d.get("time","unknown")))'
|
||||
fi
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
systemctl --user start "$unit"
|
||||
show_status
|
||||
if session_status=$("$controller" --check-interactive); then
|
||||
echo "$session_status"
|
||||
if [[ "$session_status" == "interactive NestQuest session open" ]]; then
|
||||
echo "Close the interactive NestQuest OpenCode session to let the controller run its first tick."
|
||||
fi
|
||||
else
|
||||
echo "The controller will wait for OpenCode session detection to recover."
|
||||
fi
|
||||
;;
|
||||
stop)
|
||||
systemctl --user stop "$unit"
|
||||
show_status
|
||||
;;
|
||||
status)
|
||||
show_status
|
||||
;;
|
||||
*)
|
||||
echo "usage: nq-cycle start|stop|status" >&2
|
||||
exit 64
|
||||
;;
|
||||
esac
|
||||
Executable
+52
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
# Launch the NestQuest orchestrator immediately in the foreground terminal.
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -gt 1 ]]; then
|
||||
echo "usage: nq-orchestrate [MISSION_PROMPT_FILE|-|--print-prompt]" >&2
|
||||
exit 64
|
||||
fi
|
||||
|
||||
control_home=${NQ_CONTROL_HOME:-/home/overseer/.local/share/nestquest-controller}
|
||||
project=${NQ_PROJECT_ROOT:-/mnt/ai-storage/projects/NestQuest}
|
||||
opencode_bin=${OPENCODE_BIN:-/home/overseer/.opencode/bin/opencode}
|
||||
systemctl_bin=${SYSTEMCTL_BIN:-systemctl}
|
||||
renderer="$control_home/scripts/render-cycle-prompt"
|
||||
profile="$control_home/templates/nestquest.profile.json"
|
||||
|
||||
for required in "$renderer" "$profile"; do
|
||||
[[ -e "$required" ]] || { echo "missing required path: $required" >&2; exit 66; }
|
||||
done
|
||||
|
||||
prompt=$("$renderer" "$profile")
|
||||
if [[ ${1:-} == "--print-prompt" ]]; then
|
||||
printf '%s\n' "$prompt"
|
||||
exit 0
|
||||
fi
|
||||
for required in "$project" "$opencode_bin" "$control_home/.opencode/agents/terminal-orchestrator.md"; do
|
||||
[[ -e "$required" ]] || { echo "missing required path: $required" >&2; exit 66; }
|
||||
done
|
||||
if [[ $# -eq 1 ]]; then
|
||||
if [[ "$1" == "-" ]]; then
|
||||
mission=$(cat)
|
||||
else
|
||||
[[ -f "$1" ]] || { echo "mission prompt not found: $1" >&2; exit 66; }
|
||||
mission=$(cat "$1")
|
||||
fi
|
||||
[[ -n "${mission//[[:space:]]/}" ]] || { echo "mission prompt is empty" >&2; exit 65; }
|
||||
prompt+=$'\n\n---\n\n# USER MISSION\n\n'
|
||||
prompt+="$mission"
|
||||
fi
|
||||
|
||||
# Foreground mode owns orchestration. A dormant service must never race it.
|
||||
"$systemctl_bin" --user stop nestquest-controller.service
|
||||
|
||||
session_status=$("$control_home/scripts/nq-controller" --check-interactive --project "$project")
|
||||
if [[ "$session_status" == "interactive NestQuest session open" ]]; then
|
||||
echo "Another interactive NestQuest OpenCode session is open." >&2
|
||||
echo "Close it, then run nq-orchestrate again." >&2
|
||||
exit 75
|
||||
fi
|
||||
|
||||
export OPENCODE_CONFIG_DIR="$control_home/.opencode"
|
||||
exec "$opencode_bin" "$project" --pure --agent terminal-orchestrator --prompt "$prompt"
|
||||
@@ -922,44 +922,37 @@ You are the controller of a persistent development pipeline. Keep the pipeline m
|
||||
|
||||
---
|
||||
|
||||
# 27. HOST CONTROLLER RUNTIME
|
||||
# 27. FOREGROUND TERMINAL RUNTIME
|
||||
|
||||
This section is the host execution contract for OpenCode {{OPENCODE_VERSION}} and takes
|
||||
precedence over Section 21 when deciding whether a single model invocation may
|
||||
end. The persistent user service runs one fresh, headless orchestrator turn per
|
||||
controller tick. Section 21 governs mission completion, not the lifetime of
|
||||
one OpenCode turn.
|
||||
This section is the execution contract for a foreground OpenCode
|
||||
orchestrator. It replaces the service runtime section in the rendered prompt
|
||||
and takes precedence over any service-specific runtime instructions later
|
||||
read from {{CONTEXT_STORE}}.
|
||||
|
||||
The user starts this project's cycle explicitly from OpenCode with
|
||||
`{{START_COMMAND}}`. `{{STOP_COMMAND}}` stops it, and
|
||||
`{{STATUS_COMMAND}}` reports its state. The `{{SERVICE_NAME}}` user unit is
|
||||
not enabled at login. Reading this prompt in an ordinary interactive session
|
||||
does not authorize starting the background service. The service waits for the
|
||||
interactive project session to close before its first tick.
|
||||
The user explicitly started this run with `{{TERMINAL_LAUNCHER}}`. Begin
|
||||
work immediately in `{{PROJECT_ROOT}}`; do not start, wait for, or delegate
|
||||
control to `{{SERVICE_NAME}}`. This single OpenCode session owns the
|
||||
orchestration loop until a global terminal condition, an explicit user stop,
|
||||
or a host-forced yield.
|
||||
|
||||
At the end of EVERY orchestrator invocation, emit exactly one final line:
|
||||
`ORCHESTRATOR_STATE: CONTINUE`, `ORCHESTRATOR_STATE: QUIESCENT`, or
|
||||
`ORCHESTRATOR_STATE: BLOCKED`. Use CONTINUE whenever eligible or in-flight
|
||||
work remains and state the next concrete action immediately above that line.
|
||||
The service starts a fresh invocation after reconciling {{CONTEXT_STORE}}, {{WORK_TRACKER}}, Git,
|
||||
pull requests, and checks. Use QUIESCENT only after verifying that no eligible
|
||||
or in-flight work remains; the service polls again. Use BLOCKED only for a
|
||||
specific required human decision or access failure; name the missing action.
|
||||
A blocked task does not block other eligible work. If {{CONTEXT_STORE}} is unavailable,
|
||||
do not dispatch or merge, and return BLOCKED.
|
||||
Execute `CONTROLLER_TICK` repeatedly inside this foreground session. Do not
|
||||
end a turn merely to emit `ORCHESTRATOR_STATE: CONTINUE`, and do not treat
|
||||
one task, review, pull request, or feature merge as completion. After every
|
||||
child result or external state change, reconcile {{CONTEXT_STORE}},
|
||||
{{WORK_TRACKER}}, Git, pull requests, and checks, then perform the next
|
||||
eligible action.
|
||||
|
||||
The installed launchers live at
|
||||
`{{CONTROL_HOME}}/scripts/`. Use `{{ROLE_LAUNCHER}}`
|
||||
to start developer or approver OpenCode sessions. Use `{{REVIEW_LAUNCHER}}` with
|
||||
WORKTREE, BASE_REF, HEAD_REF, REVIEW_PROMPT_FILE, PR_NUMBER, OUTPUT_FILE to
|
||||
run a fresh read-only {{REVIEWER_DISPLAY_NAME}} review at high effort. Its output file
|
||||
must be under `{{SCRATCH_DIR}}`; it posts a {{CODE_HOST}} review as `{{REVIEW_ACCOUNT}}`
|
||||
only after approving the exact current head. The feature-to-{{INTEGRATION_BRANCH}} approver uses
|
||||
`{{MERGE_BROKER}}` and must verify that same exact-head {{CODE_HOST}} approval.
|
||||
No controller or developer session performs the feature-to-{{INTEGRATION_BRANCH}} merge.
|
||||
The installed role launchers live at `{{CONTROL_HOME}}/scripts/`. Use
|
||||
`{{ROLE_LAUNCHER}}` for developer and approver sessions. Use
|
||||
`{{REVIEW_LAUNCHER}}` with WORKTREE, BASE_REF, HEAD_REF,
|
||||
REVIEW_PROMPT_FILE, PR_NUMBER, OUTPUT_FILE for a fresh read-only
|
||||
{{REVIEWER_DISPLAY_NAME}} review. Review output belongs under
|
||||
`{{SCRATCH_DIR}}`, and exact-head approval must be posted by
|
||||
`{{REVIEW_ACCOUNT}}`. The feature-to-{{INTEGRATION_BRANCH}} approver must
|
||||
use `{{MERGE_BROKER}}`; the orchestrator and developer never perform that
|
||||
merge.
|
||||
|
||||
The service holds one controller lock and waits while an interactive
|
||||
{{PROJECT_NAME}} OpenCode session is open. Each model turn has a {{TICK_TIMEOUT_LABEL}} host
|
||||
timeout. After an error or timeout, reconcile external state before retrying;
|
||||
three consecutive failed turns stop the service for inspection. A successful
|
||||
feature merge is a transition back into scheduling, not a terminal condition.
|
||||
If the host forces a yield before a global terminal condition, return the
|
||||
machine-readable checkpoint from Section 22. The user can resume the same
|
||||
OpenCode session from the terminal. A yield is a checkpoint, not a claim that
|
||||
the project queue is complete.
|
||||
|
||||
+13
-5
@@ -16,9 +16,17 @@ section, especially task/feature semantics, release rules, permissions,
|
||||
review gates, and the host runtime section. The template assumes an OpenCode
|
||||
controller, a feature/task tracker, a versioned context store, a code host,
|
||||
and a separate reviewer and approver. Adapt those sections when the project's
|
||||
systems differ.
|
||||
systems differ. Section 27 is a single-session foreground loop suitable for
|
||||
`opencode --agent ... --prompt ...`; the reusable prompt has no service
|
||||
runtime.
|
||||
|
||||
Publish the reviewed **rendered** prompt into that project's context store
|
||||
with its version check. Give the project its own agent definitions, service
|
||||
unit, broker, and OpenCode start/stop commands. The template itself does not
|
||||
start a controller or register another project.
|
||||
For NestQuest, `nq-orchestrate --print-prompt` emits the fully rendered
|
||||
foreground prompt without starting OpenCode. `nq-orchestrate [mission.md]`
|
||||
renders that same prompt, appends the optional mission, and supplies it to the
|
||||
foreground orchestrator agent.
|
||||
|
||||
Publish the reviewed rendered prompt into that project's context store with
|
||||
its version check.
|
||||
Give the project its own agent definitions, broker, profile, and terminal
|
||||
launcher. The template itself does not start an orchestrator or register
|
||||
another project.
|
||||
|
||||
@@ -21,12 +21,11 @@
|
||||
"CODE_HOST": "Gitea",
|
||||
"TASK_ID_EXAMPLE": "TASK-123",
|
||||
"FEATURE_BRANCH_EXAMPLE": "feature/NQ-17-family-calendar",
|
||||
"OPENCODE_VERSION": "1.18.31",
|
||||
"OPENCODE_VERSION": "1.18.32",
|
||||
"PROJECT_ROOT": "/mnt/ai-storage/projects/NestQuest",
|
||||
"CONTROL_HOME": "/home/overseer/.local/share/nestquest-controller",
|
||||
"SERVICE_NAME": "nestquest-controller.service",
|
||||
"START_COMMAND": "/start-nestquest-development",
|
||||
"STOP_COMMAND": "/stop-nestquest-development",
|
||||
"STATUS_COMMAND": "/nestquest-development-status",
|
||||
"TERMINAL_LAUNCHER": "nq-orchestrate",
|
||||
"ROLE_LAUNCHER": "nq-agent",
|
||||
"REVIEW_LAUNCHER": "nq-review",
|
||||
"MERGE_BROKER": "nq-approve-merge",
|
||||
|
||||
Reference in New Issue
Block a user